diff options
Diffstat (limited to 'indra/llinventory')
-rw-r--r-- | indra/llinventory/llcategory.cpp | 161 | ||||
-rw-r--r-- | indra/llinventory/llcategory.h | 80 | ||||
-rw-r--r-- | indra/llinventory/lleconomy.cpp | 227 | ||||
-rw-r--r-- | indra/llinventory/lleconomy.h | 116 | ||||
-rw-r--r-- | indra/llinventory/llinventory.cpp | 1773 | ||||
-rw-r--r-- | indra/llinventory/llinventory.h | 411 | ||||
-rw-r--r-- | indra/llinventory/llnotecard.cpp | 286 | ||||
-rw-r--r-- | indra/llinventory/llnotecard.h | 41 | ||||
-rw-r--r-- | indra/llinventory/llparcel.cpp | 1813 | ||||
-rw-r--r-- | indra/llinventory/llparcel.h | 576 | ||||
-rw-r--r-- | indra/llinventory/llparcelflags.h | 105 | ||||
-rw-r--r-- | indra/llinventory/llpermissions.cpp | 1171 | ||||
-rw-r--r-- | indra/llinventory/llpermissions.h | 426 | ||||
-rw-r--r-- | indra/llinventory/llpermissionsflags.h | 78 | ||||
-rw-r--r-- | indra/llinventory/llsaleinfo.cpp | 356 | ||||
-rw-r--r-- | indra/llinventory/llsaleinfo.h | 110 | ||||
-rw-r--r-- | indra/llinventory/lltransactionflags.cpp | 43 | ||||
-rw-r--r-- | indra/llinventory/lltransactionflags.h | 27 | ||||
-rw-r--r-- | indra/llinventory/lltransactiontypes.h | 93 | ||||
-rw-r--r-- | indra/llinventory/lluserrelations.cpp | 89 | ||||
-rw-r--r-- | indra/llinventory/lluserrelations.h | 154 |
21 files changed, 8136 insertions, 0 deletions
diff --git a/indra/llinventory/llcategory.cpp b/indra/llinventory/llcategory.cpp new file mode 100644 index 0000000000..7a7bbf530a --- /dev/null +++ b/indra/llinventory/llcategory.cpp @@ -0,0 +1,161 @@ +/** + * @file llcategory.cpp + * + * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "linden_common.h" + +#include "llcategory.h" + +#include "message.h" + +const LLCategory LLCategory::none; + +///---------------------------------------------------------------------------- +/// Local function declarations, constants, enums, and typedefs +///---------------------------------------------------------------------------- + +// This is the storage of the category names. It's loosely based on a +// heap-like structure with indices into it for faster searching and +// so that we don't have to maintain a balanced heap. It's *VITALLY* +// important that the CATEGORY_INDEX and CATEGORY_NAME tables are kept +// in synch. + +// CATEGORY_INDEX indexes into CATEGORY_NAME at the first occurance of +// a child. Thus, the first child of root is "Object" which is located +// in CATEGORY_NAME[1]. +const S32 CATEGORY_INDEX[] = +{ + 1, // ROOT + 6, // object + 7, // clothing + 7, // texture + 7, // sound + 7, // landmark + 7, // object|component + 7, // off the end (required for child count calculations) +}; + +// The heap of names +const char* CATEGORY_NAME[] = +{ + "(none)", + "Object", // (none) + "Clothing", + "Texture", + "Sound", + "Landmark", + "Component", // object + NULL +}; + +///---------------------------------------------------------------------------- +/// Class llcategory +///---------------------------------------------------------------------------- + +LLCategory::LLCategory() +{ + // this is used as a simple compile time assertion. If this code + // fails to compile, the depth has been changed, and we need to + // clean up some of the code that relies on the depth, such as the + // default constructor. If CATEGORY_DEPTH != 4, this code will + // attempt to construct a zero length array - which the compiler + // should balk at. +// static const char CATEGORY_DEPTH_CHECK[(CATEGORY_DEPTH == 4)?1:0] = {' '}; // unused + + // actually initialize the object. + mData[0] = 0; + mData[1] = 0; + mData[2] = 0; + mData[3] = 0; +} + +void LLCategory::init(U32 value) +{ + U8 v; + for(S32 i = 0; i < CATEGORY_DEPTH; i++) + { + v = (U8)((0x000000ff) & value); + mData[CATEGORY_DEPTH - 1 - i] = v; + value >>= 8; + } +} + +U32 LLCategory::getU32() const +{ + U32 rv = 0; + rv |= mData[0]; + rv <<= 8; + rv |= mData[1]; + rv <<= 8; + rv |= mData[2]; + rv <<= 8; + rv |= mData[3]; + return rv; +} + +S32 LLCategory::getSubCategoryCount() const +{ + S32 rv = CATEGORY_INDEX[mData[0] + 1] - CATEGORY_INDEX[mData[0]]; + return rv; +} + +// This method will return a category that is the nth subcategory. If +// you're already at the bottom of the hierarchy, then the method will +// return a copy of this. +LLCategory LLCategory::getSubCategory(U8 n) const +{ + LLCategory rv(*this); + for(S32 i = 0; i < (CATEGORY_DEPTH - 1); i++) + { + if(rv.mData[i] == 0) + { + rv.mData[i] = n + 1; + break; + } + } + return rv; +} + +// This method will return the name of the leaf category type +const char* LLCategory::lookupName() const +{ + S32 i = 0; + S32 index = mData[i++]; + while((i < CATEGORY_DEPTH) && (mData[i] != 0)) + { + index = CATEGORY_INDEX[index]; + ++i; + } + return CATEGORY_NAME[index]; +} + +// message serialization +void LLCategory::packMessage(LLMessageSystem* msg) const +{ + U32 data = getU32(); + msg->addU32Fast(_PREHASH_Category, data); +} + +// message serialization +void LLCategory::unpackMessage(LLMessageSystem* msg, const char* block) +{ + U32 data; + msg->getU32Fast(block, _PREHASH_Category, data); + init(data); +} + +// message serialization +void LLCategory::unpackMultiMessage(LLMessageSystem* msg, const char* block, + S32 block_num) +{ + U32 data; + msg->getU32Fast(block, _PREHASH_Category, data, block_num); + init(data); +} + +///---------------------------------------------------------------------------- +/// Local function definitions +///---------------------------------------------------------------------------- diff --git a/indra/llinventory/llcategory.h b/indra/llinventory/llcategory.h new file mode 100644 index 0000000000..a28b707d93 --- /dev/null +++ b/indra/llinventory/llcategory.h @@ -0,0 +1,80 @@ +/** + * @file llcategory.h + * @brief LLCategory class header file. + * + * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LL_LLCATEGORY_H +#define LL_LLCATEGORY_H + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLCategory +// +// An instance of the LLCategory class represents a particular +// category in a hierarchical classification system. For now, it is 4 +// levels deep with 255 (minus 1) possible values at each level. If a +// non zero value is found at level 4, that is the leaf category, +// otherwise, it is the first level that has a 0 in the next depth +// level. +// +// To output the names of all top level categories, you could do the +// following: +// +// S32 count = LLCategory::none.getSubCategoryCount(); +// for(S32 i = 0; i < count; i++) +// { +// llinfos << none.getSubCategory(i).lookupNmae() << llendl; +// } +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLMessageSystem; + +class LLCategory +{ +public: + // Nice default static const. + static const LLCategory none; + + // construction. Since this is really a POD type, destruction, + // copy, and assignment are handled by the compiler. + LLCategory(); + explicit LLCategory(U32 value) { init(value); } + + // methods + void init(U32 value); + U32 getU32() const; + S32 getSubCategoryCount() const; + + // This method will return a category that is the nth + // subcategory. If you're already at the bottom of the hierarchy, + // then the method will return a copy of this. + LLCategory getSubCategory(U8 n) const; + + // This method will return the name of the leaf category type + const char* lookupName() const; + + // This method will return the full hierarchy name in an easily + // interpreted (TOP)|(SUB1)|(SUB2) format. *NOTE: not implemented + // because we don't have anything but top level categories at the + // moment. + //const char* lookupFullName() const; + + // message serialization + void packMessage(LLMessageSystem* msg) const; + void unpackMessage(LLMessageSystem* msg, const char* block); + void unpackMultiMessage(LLMessageSystem* msg, const char* block, + S32 block_num); +protected: + enum + { + CATEGORY_TOP = 0, + CATEGORY_DEPTH = 4, + }; + + U8 mData[CATEGORY_DEPTH]; +}; + + +#endif // LL_LLCATEGORY_H diff --git a/indra/llinventory/lleconomy.cpp b/indra/llinventory/lleconomy.cpp new file mode 100644 index 0000000000..378ab8ced1 --- /dev/null +++ b/indra/llinventory/lleconomy.cpp @@ -0,0 +1,227 @@ +/** + * @file lleconomy.cpp + * + * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "linden_common.h" + +#include "lleconomy.h" +#include "llerror.h" +#include "message.h" +#include "v3math.h" + +LLGlobalEconomy::LLGlobalEconomy() +: mObjectCount( -1 ), + mObjectCapacity( -1 ), + mPriceObjectClaim( -1 ), + mPricePublicObjectDecay( -1 ), + mPricePublicObjectDelete( -1 ), + mPriceEnergyUnit( -1 ), + mPriceUpload( -1 ), + mPriceRentLight( -1 ), + mTeleportMinPrice( -1 ), + mTeleportPriceExponent( -1 ), + mPriceGroupCreate( -1 ) +{ } + +LLGlobalEconomy::~LLGlobalEconomy() +{ } + +// static +void LLGlobalEconomy::processEconomyData(LLMessageSystem *msg, void** user_data) +{ + S32 i; + F32 f; + + LLGlobalEconomy *this_ptr = (LLGlobalEconomy*)user_data; + + msg->getS32Fast(_PREHASH_Info, _PREHASH_ObjectCapacity, i); + this_ptr->setObjectCapacity(i); + msg->getS32Fast(_PREHASH_Info, _PREHASH_ObjectCount, i); + this_ptr->setObjectCount(i); + msg->getS32Fast(_PREHASH_Info, _PREHASH_PriceEnergyUnit, i); + this_ptr->setPriceEnergyUnit(i); + msg->getS32Fast(_PREHASH_Info, _PREHASH_PriceObjectClaim, i); + this_ptr->setPriceObjectClaim(i); + msg->getS32Fast(_PREHASH_Info, _PREHASH_PricePublicObjectDecay, i); + this_ptr->setPricePublicObjectDecay(i); + msg->getS32Fast(_PREHASH_Info, _PREHASH_PricePublicObjectDelete, i); + this_ptr->setPricePublicObjectDelete(i); + msg->getS32Fast(_PREHASH_Info, _PREHASH_PriceUpload, i); + this_ptr->setPriceUpload(i); + msg->getS32Fast(_PREHASH_Info, _PREHASH_PriceRentLight, i); + this_ptr->setPriceRentLight(i); + msg->getS32Fast(_PREHASH_Info, _PREHASH_TeleportMinPrice, i); + this_ptr->setTeleportMinPrice(i); + msg->getF32Fast(_PREHASH_Info, _PREHASH_TeleportPriceExponent, f); + this_ptr->setTeleportPriceExponent(f); + msg->getS32Fast(_PREHASH_Info, _PREHASH_PriceGroupCreate, i); + this_ptr->setPriceGroupCreate(i); +} + +S32 LLGlobalEconomy::calculateTeleportCost(F32 distance) const +{ + S32 min_cost = getTeleportMinPrice(); + F32 exponent = getTeleportPriceExponent(); + F32 divisor = 100.f * pow(3.f, exponent); + S32 cost = (U32)(distance * pow(log10(distance), exponent) / divisor); + if (cost < 0) + { + cost = 0; + } + else if (cost < min_cost) + { + cost = min_cost; + } + + return cost; +} + +S32 LLGlobalEconomy::calculateLightRent(const LLVector3& object_size) const +{ + F32 intensity_mod = llmax(object_size.magVec(), 1.f); + return (S32)(intensity_mod * getPriceRentLight()); +} + +void LLGlobalEconomy::print() +{ + llinfos << "Global Economy Settings: " << llendl; + llinfos << "Object Capacity: " << mObjectCapacity << llendl; + llinfos << "Object Count: " << mObjectCount << llendl; + llinfos << "Claim Price Per Object: " << mPriceObjectClaim << llendl; + llinfos << "Claim Price Per Public Object: " << mPricePublicObjectDecay << llendl; + llinfos << "Delete Price Per Public Object: " << mPricePublicObjectDelete << llendl; + llinfos << "Release Price Per Public Object: " << getPricePublicObjectRelease() << llendl; + llinfos << "Price Per Energy Unit: " << mPriceEnergyUnit << llendl; + llinfos << "Price Per Upload: " << mPriceUpload << llendl; + llinfos << "Light Base Price: " << mPriceRentLight << llendl; + llinfos << "Teleport Min Price: " << mTeleportMinPrice << llendl; + llinfos << "Teleport Price Exponent: " << mTeleportPriceExponent << llendl; + llinfos << "Price for group creation: " << mPriceGroupCreate << llendl; +} + +LLRegionEconomy::LLRegionEconomy() +: LLGlobalEconomy(), + mPriceObjectRent( -1.f ), + mPriceObjectScaleFactor( -1.f ), + mEnergyEfficiency( -1.f ), + mBasePriceParcelClaimDefault(-1), + mBasePriceParcelClaimActual(-1), + mPriceParcelClaimFactor(-1.f), + mBasePriceParcelRent(-1), + mAreaOwned(-1.f), + mAreaTotal(-1.f) +{ } + +LLRegionEconomy::~LLRegionEconomy() +{ } + +BOOL LLRegionEconomy::hasData() const +{ + return (mBasePriceParcelRent != -1); +} + +// static +void LLRegionEconomy::processEconomyData(LLMessageSystem *msg, void** user_data) +{ + S32 i; + F32 f; + + LLGlobalEconomy::processEconomyData(msg, user_data); + + LLRegionEconomy *this_ptr = (LLRegionEconomy*)user_data; + + msg->getS32Fast(_PREHASH_Info, _PREHASH_PriceParcelClaim, i); + this_ptr->setBasePriceParcelClaimDefault(i); + msg->getF32(_PREHASH_Info, _PREHASH_PriceParcelClaimFactor, f); + this_ptr->setPriceParcelClaimFactor(f); + msg->getF32Fast(_PREHASH_Info, _PREHASH_EnergyEfficiency, f); + this_ptr->setEnergyEfficiency(f); + msg->getF32Fast(_PREHASH_Info, _PREHASH_PriceObjectRent, f); + this_ptr->setPriceObjectRent(f); + msg->getF32Fast(_PREHASH_Info, _PREHASH_PriceObjectScaleFactor, f); + this_ptr->setPriceObjectScaleFactor(f); + msg->getS32Fast(_PREHASH_Info, _PREHASH_PriceParcelRent, i); + this_ptr->setBasePriceParcelRent(i); +} + +// static +void LLRegionEconomy::processEconomyDataRequest(LLMessageSystem *msg, void **user_data) +{ + LLRegionEconomy *this_ptr = (LLRegionEconomy*)user_data; + + msg->newMessageFast(_PREHASH_EconomyData); + msg->nextBlockFast(_PREHASH_Info); + msg->addS32Fast(_PREHASH_ObjectCapacity, this_ptr->getObjectCapacity()); + msg->addS32Fast(_PREHASH_ObjectCount, this_ptr->getObjectCount()); + msg->addS32Fast(_PREHASH_PriceEnergyUnit, this_ptr->getPriceEnergyUnit()); + msg->addS32Fast(_PREHASH_PriceObjectClaim, this_ptr->getPriceObjectClaim()); + msg->addS32Fast(_PREHASH_PricePublicObjectDecay, this_ptr->getPricePublicObjectDecay()); + msg->addS32Fast(_PREHASH_PricePublicObjectDelete, this_ptr->getPricePublicObjectDelete()); + msg->addS32Fast(_PREHASH_PriceParcelClaim, this_ptr->mBasePriceParcelClaimActual); + msg->addF32Fast(_PREHASH_PriceParcelClaimFactor, this_ptr->mPriceParcelClaimFactor); + msg->addS32Fast(_PREHASH_PriceUpload, this_ptr->getPriceUpload()); + msg->addS32Fast(_PREHASH_PriceRentLight, this_ptr->getPriceRentLight()); + msg->addS32Fast(_PREHASH_TeleportMinPrice, this_ptr->getTeleportMinPrice()); + msg->addF32Fast(_PREHASH_TeleportPriceExponent, this_ptr->getTeleportPriceExponent()); + + msg->addF32Fast(_PREHASH_EnergyEfficiency, this_ptr->getEnergyEfficiency()); + msg->addF32Fast(_PREHASH_PriceObjectRent, this_ptr->getPriceObjectRent()); + msg->addF32Fast(_PREHASH_PriceObjectScaleFactor, this_ptr->getPriceObjectScaleFactor()); + msg->addS32Fast(_PREHASH_PriceParcelRent, this_ptr->getPriceParcelRent()); + msg->addS32Fast(_PREHASH_PriceGroupCreate, this_ptr->getPriceGroupCreate()); + + msg->sendReliable(msg->getSender()); +} + + +S32 LLRegionEconomy::getPriceParcelClaim() const +{ + //return (S32)((F32)mBasePriceParcelClaim * (mAreaTotal / (mAreaTotal - mAreaOwned))); + return (S32)((F32)mBasePriceParcelClaimActual * mPriceParcelClaimFactor); +} + +S32 LLRegionEconomy::getPriceParcelRent() const +{ + return mBasePriceParcelRent; +} + + +void LLRegionEconomy::print() +{ + this->LLGlobalEconomy::print(); + + llinfos << "Region Economy Settings: " << llendl; + llinfos << "Land (square meters): " << mAreaTotal << llendl; + llinfos << "Owned Land (square meters): " << mAreaOwned << llendl; + llinfos << "Daily Object Rent: " << mPriceObjectRent << llendl; + llinfos << "Daily Land Rent (per meter): " << getPriceParcelRent() << llendl; + llinfos << "Energey Efficiency: " << mEnergyEfficiency << llendl; +} + + +void LLRegionEconomy::setBasePriceParcelClaimDefault(S32 val) +{ + mBasePriceParcelClaimDefault = val; + if(mBasePriceParcelClaimActual == -1) + { + mBasePriceParcelClaimActual = val; + } +} + +void LLRegionEconomy::setBasePriceParcelClaimActual(S32 val) +{ + mBasePriceParcelClaimActual = val; +} + +void LLRegionEconomy::setPriceParcelClaimFactor(F32 val) +{ + mPriceParcelClaimFactor = val; +} + +void LLRegionEconomy::setBasePriceParcelRent(S32 val) +{ + mBasePriceParcelRent = val; +} diff --git a/indra/llinventory/lleconomy.h b/indra/llinventory/lleconomy.h new file mode 100644 index 0000000000..c71fc9e6d0 --- /dev/null +++ b/indra/llinventory/lleconomy.h @@ -0,0 +1,116 @@ +/** + * @file lleconomy.h + * + * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LL_LLECONOMY_H +#define LL_LLECONOMY_H + +class LLMessageSystem; +class LLVector3; + +class LLGlobalEconomy +{ +public: + LLGlobalEconomy(); + virtual ~LLGlobalEconomy(); + + virtual void print(); + + static void processEconomyData(LLMessageSystem *msg, void **user_data); + + S32 calculateTeleportCost(F32 distance) const; + S32 calculateLightRent(const LLVector3& object_size) const; + + S32 getObjectCount() const { return mObjectCount; } + S32 getObjectCapacity() const { return mObjectCapacity; } + S32 getPriceObjectClaim() const { return mPriceObjectClaim; } + S32 getPricePublicObjectDecay() const { return mPricePublicObjectDecay; } + S32 getPricePublicObjectDelete() const { return mPricePublicObjectDelete; } + S32 getPricePublicObjectRelease() const { return mPriceObjectClaim - mPricePublicObjectDelete; } + S32 getPriceEnergyUnit() const { return mPriceEnergyUnit; } + S32 getPriceUpload() const { return mPriceUpload; } + S32 getPriceRentLight() const { return mPriceRentLight; } + S32 getTeleportMinPrice() const { return mTeleportMinPrice; } + F32 getTeleportPriceExponent() const { return mTeleportPriceExponent; } + S32 getPriceGroupCreate() const { return mPriceGroupCreate; } + + + void setObjectCount(S32 val) { mObjectCount = val; } + void setObjectCapacity(S32 val) { mObjectCapacity = val; } + void setPriceObjectClaim(S32 val) { mPriceObjectClaim = val; } + void setPricePublicObjectDecay(S32 val) { mPricePublicObjectDecay = val; } + void setPricePublicObjectDelete(S32 val) { mPricePublicObjectDelete = val; } + void setPriceEnergyUnit(S32 val) { mPriceEnergyUnit = val; } + void setPriceUpload(S32 val) { mPriceUpload = val; } + void setPriceRentLight(S32 val) { mPriceRentLight = val; } + void setTeleportMinPrice(S32 val) { mTeleportMinPrice = val; } + void setTeleportPriceExponent(F32 val) { mTeleportPriceExponent = val; } + void setPriceGroupCreate(S32 val) { mPriceGroupCreate = val; } + +private: + S32 mObjectCount; + S32 mObjectCapacity; + S32 mPriceObjectClaim; // per primitive + S32 mPricePublicObjectDecay; // per primitive + S32 mPricePublicObjectDelete; // per primitive + S32 mPriceEnergyUnit; + S32 mPriceUpload; + S32 mPriceRentLight; + S32 mTeleportMinPrice; + F32 mTeleportPriceExponent; + S32 mPriceGroupCreate; +}; + + +class LLRegionEconomy : public LLGlobalEconomy +{ +public: + LLRegionEconomy(); + ~LLRegionEconomy(); + + static void processEconomyData(LLMessageSystem *msg, void **user_data); + static void processEconomyDataRequest(LLMessageSystem *msg, void **user_data); + + void print(); + + BOOL hasData() const; + F32 getPriceObjectRent() const { return mPriceObjectRent; } + F32 getPriceObjectScaleFactor() const {return mPriceObjectScaleFactor;} + F32 getEnergyEfficiency() const { return mEnergyEfficiency; } + S32 getPriceParcelClaim() const; + S32 getPriceParcelRent() const; + F32 getAreaOwned() const { return mAreaOwned; } + F32 getAreaTotal() const { return mAreaTotal; } + S32 getBasePriceParcelClaimActual() const { return mBasePriceParcelClaimActual; } + + void setPriceObjectRent(F32 val) { mPriceObjectRent = val; } + void setPriceObjectScaleFactor(F32 val) { mPriceObjectScaleFactor = val; } + void setEnergyEfficiency(F32 val) { mEnergyEfficiency = val; } + + void setBasePriceParcelClaimDefault(S32 val); + void setBasePriceParcelClaimActual(S32 val); + void setPriceParcelClaimFactor(F32 val); + void setBasePriceParcelRent(S32 val); + + void setAreaOwned(F32 val) { mAreaOwned = val; } + void setAreaTotal(F32 val) { mAreaTotal = val; } + +private: + F32 mPriceObjectRent; + F32 mPriceObjectScaleFactor; + F32 mEnergyEfficiency; + + S32 mBasePriceParcelClaimDefault; + S32 mBasePriceParcelClaimActual; + F32 mPriceParcelClaimFactor; + S32 mBasePriceParcelRent; + + F32 mAreaOwned; + F32 mAreaTotal; + +}; + +#endif diff --git a/indra/llinventory/llinventory.cpp b/indra/llinventory/llinventory.cpp new file mode 100644 index 0000000000..bffc4df281 --- /dev/null +++ b/indra/llinventory/llinventory.cpp @@ -0,0 +1,1773 @@ +/** + * @file llinventory.cpp + * @brief Implementation of the inventory system. + * + * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "linden_common.h" + +#include <time.h> + +#include "llinventory.h" + +#include "lldbstrings.h" +#include "llcrypto.h" +#include "llsd.h" +#include "message.h" +#include <boost/tokenizer.hpp> + +#include "llsdutil.h" + +#include "llsdutil.h" + +///---------------------------------------------------------------------------- +/// Local function declarations, constants, enums, and typedefs +///---------------------------------------------------------------------------- + +const U8 TASK_INVENTORY_ITEM_KEY = 0; +const U8 TASK_INVENTORY_ASSET_KEY = 1; + +const LLUUID MAGIC_ID("3c115e51-04f4-523c-9fa6-98aff1034730"); + +// helper function which returns true if inventory type and asset type +// are potentially compatible. For example, an attachment must be an +// object, but a wearable can be a bodypart or clothing asset. +bool inventory_and_asset_types_match( + LLInventoryType::EType inventory_type, + LLAssetType::EType asset_type); + + +///---------------------------------------------------------------------------- +/// Class LLInventoryType +///---------------------------------------------------------------------------- + +// Unlike asset type names, not limited to 8 characters. +// Need not match asset type names. +static const char* INVENTORY_TYPE_NAMES[LLInventoryType::IT_COUNT] = +{ + "texture", // 0 + "sound", + "callcard", + "landmark", + NULL, + NULL, // 5 + "object", + "notecard", + "category", + "root", + "script", // 10 + NULL, + NULL, + NULL, + NULL, + "snapshot", // 15 + NULL, + "attach", + "wearable", + "animation", + "gesture", // 20 +}; + +// This table is meant for decoding to human readable form. Put any +// and as many printable characters you want in each one. +// See also LLAssetType::mAssetTypeHumanNames +static const char* INVENTORY_TYPE_HUMAN_NAMES[LLInventoryType::IT_COUNT] = +{ + "texture", // 0 + "sound", + "calling card", + "landmark", + NULL, + NULL, // 5 + "object", + "note card", + "folder", + "root", + "script", // 10 + NULL, + NULL, + NULL, + NULL, + "snapshot", // 15 + NULL, + "attachment", + "wearable", + "animation", + "gesture", // 20 +}; + +// Maps asset types to the default inventory type for that kind of asset. +// Thus, "Lost and Found" is a "Category" +static const LLInventoryType::EType +DEFAULT_ASSET_FOR_INV_TYPE[LLAssetType::AT_COUNT] = +{ + LLInventoryType::IT_TEXTURE, // AT_TEXTURE + LLInventoryType::IT_SOUND, // AT_SOUND + LLInventoryType::IT_CALLINGCARD, // AT_CALLINGCARD + LLInventoryType::IT_LANDMARK, // AT_LANDMARK + LLInventoryType::IT_LSL, // AT_SCRIPT + LLInventoryType::IT_WEARABLE, // AT_CLOTHING + LLInventoryType::IT_OBJECT, // AT_OBJECT + LLInventoryType::IT_NOTECARD, // AT_NOTECARD + LLInventoryType::IT_CATEGORY, // AT_CATEGORY + LLInventoryType::IT_ROOT_CATEGORY, // AT_ROOT_CATEGORY + LLInventoryType::IT_LSL, // AT_LSL_TEXT + LLInventoryType::IT_LSL, // AT_LSL_BYTECODE + LLInventoryType::IT_TEXTURE, // AT_TEXTURE_TGA + LLInventoryType::IT_WEARABLE, // AT_BODYPART + LLInventoryType::IT_CATEGORY, // AT_TRASH + LLInventoryType::IT_CATEGORY, // AT_SNAPSHOT_CATEGORY + LLInventoryType::IT_CATEGORY, // AT_LOST_AND_FOUND + LLInventoryType::IT_SOUND, // AT_SOUND_WAV + LLInventoryType::IT_NONE, // AT_IMAGE_TGA + LLInventoryType::IT_NONE, // AT_IMAGE_JPEG + LLInventoryType::IT_ANIMATION, // AT_ANIMATION + LLInventoryType::IT_GESTURE, // AT_GESTURE +}; + +static const int MAX_POSSIBLE_ASSET_TYPES = 2; +static const LLAssetType::EType +INVENTORY_TO_ASSET_TYPE[LLInventoryType::IT_COUNT][MAX_POSSIBLE_ASSET_TYPES] = +{ + { LLAssetType::AT_TEXTURE, LLAssetType::AT_NONE }, // IT_TEXTURE + { LLAssetType::AT_SOUND, LLAssetType::AT_NONE }, // IT_SOUND + { LLAssetType::AT_CALLINGCARD, LLAssetType::AT_NONE }, // IT_CALLINGCARD + { LLAssetType::AT_LANDMARK, LLAssetType::AT_NONE }, // IT_LANDMARK + { LLAssetType::AT_NONE, LLAssetType::AT_NONE }, + { LLAssetType::AT_NONE, LLAssetType::AT_NONE }, + { LLAssetType::AT_OBJECT, LLAssetType::AT_NONE }, // IT_OBJECT + { LLAssetType::AT_NOTECARD, LLAssetType::AT_NONE }, // IT_NOTECARD + { LLAssetType::AT_NONE, LLAssetType::AT_NONE }, // IT_CATEGORY + { LLAssetType::AT_NONE, LLAssetType::AT_NONE }, // IT_ROOT_CATEGORY + { LLAssetType::AT_LSL_TEXT, LLAssetType::AT_LSL_BYTECODE }, // IT_LSL + { LLAssetType::AT_NONE, LLAssetType::AT_NONE }, + { LLAssetType::AT_NONE, LLAssetType::AT_NONE }, + { LLAssetType::AT_NONE, LLAssetType::AT_NONE }, + { LLAssetType::AT_NONE, LLAssetType::AT_NONE }, + { LLAssetType::AT_TEXTURE, LLAssetType::AT_NONE }, // IT_SNAPSHOT + { LLAssetType::AT_NONE, LLAssetType::AT_NONE }, + { LLAssetType::AT_OBJECT, LLAssetType::AT_NONE }, // IT_ATTACHMENT + { LLAssetType::AT_CLOTHING, LLAssetType::AT_BODYPART }, // IT_WEARABLE + { LLAssetType::AT_ANIMATION, LLAssetType::AT_NONE }, // IT_ANIMATION + { LLAssetType::AT_GESTURE, LLAssetType::AT_NONE }, // IT_GESTURE +}; + +// static +const char* LLInventoryType::lookup(EType type) +{ + if((type >= 0) && (type < IT_COUNT)) + { + return INVENTORY_TYPE_NAMES[S32(type)]; + } + else + { + return NULL; + } +} + +// static +LLInventoryType::EType LLInventoryType::lookup(const char* name) +{ + for(S32 i = 0; i < IT_COUNT; ++i) + { + if((INVENTORY_TYPE_NAMES[i]) + && (0 == strcmp(name, INVENTORY_TYPE_NAMES[i]))) + { + // match + return (EType)i; + } + } + return IT_NONE; +} + +// XUI:translate +// translation from a type to a human readable form. +// static +const char* LLInventoryType::lookupHumanReadable(EType type) +{ + if((type >= 0) && (type < IT_COUNT)) + { + return INVENTORY_TYPE_HUMAN_NAMES[S32(type)]; + } + else + { + return NULL; + } +} + +// return the default inventory for the given asset type. +// static +LLInventoryType::EType LLInventoryType::defaultForAssetType(LLAssetType::EType asset_type) +{ + if((asset_type >= 0) && (asset_type < LLAssetType::AT_COUNT)) + { + return DEFAULT_ASSET_FOR_INV_TYPE[S32(asset_type)]; + } + else + { + return IT_NONE; + } +} + +///---------------------------------------------------------------------------- +/// Class LLInventoryObject +///---------------------------------------------------------------------------- + +LLInventoryObject::LLInventoryObject( + const LLUUID& uuid, + const LLUUID& parent_uuid, + LLAssetType::EType type, + const LLString& name) : + mUUID(uuid), + mParentUUID(parent_uuid), + mType(type), + mName(name) +{ + LLString::replaceNonstandardASCII(mName, ' '); + LLString::replaceChar(mName, '|', ' '); + LLString::trim(mName); + LLString::truncate(mName, DB_INV_ITEM_NAME_STR_LEN); +} + +LLInventoryObject::LLInventoryObject() : + mType(LLAssetType::AT_NONE) +{ +} + +LLInventoryObject::~LLInventoryObject( void ) +{ +} + +void LLInventoryObject::copy(const LLInventoryObject* other) +{ + mUUID = other->mUUID; + mParentUUID = other->mParentUUID; + mType = other->mType; + mName = other->mName; +} + +const LLUUID& LLInventoryObject::getUUID() const +{ + return mUUID; +} + +const LLUUID& LLInventoryObject::getParentUUID() const +{ + return mParentUUID; +} + +const LLString& LLInventoryObject::getName() const +{ + return mName; +} + +LLAssetType::EType LLInventoryObject::getType() const +{ + return mType; +} + +void LLInventoryObject::setUUID(const LLUUID& new_uuid) +{ + mUUID = new_uuid; +} + +void LLInventoryObject::rename(const LLString& n) +{ + LLString new_name(n); + LLString::replaceNonstandardASCII(new_name, ' '); + LLString::replaceChar(new_name, '|', ' '); + LLString::trim(new_name); + LLString::truncate(new_name, DB_INV_ITEM_NAME_STR_LEN); + + if( new_name != mName ) + { + mName = new_name; + } +} + +void LLInventoryObject::setParent(const LLUUID& new_parent) +{ + mParentUUID = new_parent; +} + +void LLInventoryObject::setType(LLAssetType::EType type) +{ + mType = type; +} + + +// virtual +BOOL LLInventoryObject::importLegacyStream(std::istream& input_stream) +{ + char buffer[MAX_STRING]; + char keyword[MAX_STRING]; + char valuestr[MAX_STRING]; + + keyword[0] = '\0'; + valuestr[0] = '\0'; + while(input_stream.good()) + { + input_stream.getline(buffer, MAX_STRING); + sscanf(buffer, " %254s %254s", keyword, valuestr); + if(!keyword) + { + continue; + } + if(0 == strcmp("{",keyword)) + { + continue; + } + if(0 == strcmp("}", keyword)) + { + break; + } + else if(0 == strcmp("obj_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("name", keyword)) + { + //strcpy(valuestr, buffer + strlen(keyword) + 3); + // *NOTE: Not ANSI C, but widely supported. + sscanf(buffer, " %254s %[^|]", keyword, valuestr); + mName.assign(valuestr); + LLString::replaceNonstandardASCII(mName, ' '); + LLString::replaceChar(mName, '|', ' '); + LLString::trim(mName); + LLString::truncate(mName, DB_INV_ITEM_NAME_STR_LEN); + } + else + { + llwarns << "unknown keyword '" << keyword + << "' in LLInventoryObject::importLegacyStream() for object " << mUUID << llendl; + } + } + return TRUE; +} + +// exportFile should be replaced with exportLegacyStream +// not sure whether exportLegacyStream(llofstream(fp)) would work, fp may need to get icramented... +BOOL LLInventoryObject::exportFile(FILE* fp, BOOL) const +{ + char uuid_str[UUID_STR_LENGTH]; + fprintf(fp, "\tinv_object\t0\n\t{\n"); + mUUID.toString(uuid_str); + fprintf(fp, "\t\tobj_id\t%s\n", uuid_str); + mParentUUID.toString(uuid_str); + fprintf(fp, "\t\tparent_id\t%s\n", uuid_str); + fprintf(fp, "\t\ttype\t%s\n", LLAssetType::lookup(mType)); + fprintf(fp, "\t\tname\t%s|\n", mName.c_str()); + fprintf(fp,"\t}\n"); + return TRUE; +} + +BOOL LLInventoryObject::exportLegacyStream(std::ostream& output_stream, BOOL) const +{ + char uuid_str[UUID_STR_LENGTH]; + output_stream << "\tinv_object\t0\n\t{\n"; + mUUID.toString(uuid_str); + output_stream << "\t\tobj_id\t" << uuid_str << "\n"; + mParentUUID.toString(uuid_str); + output_stream << "\t\tparent_id\t" << uuid_str << "\n"; + output_stream << "\t\ttype\t" << LLAssetType::lookup(mType) << "\n"; + output_stream << "\t\tname\t" << mName.c_str() << "|\n"; + output_stream << "\t}\n"; + return TRUE; +} + + +void LLInventoryObject::removeFromServer() +{ + // don't do nothin' + llwarns << "LLInventoryObject::removeFromServer() called. Doesn't do anything." << llendl; +} + +void LLInventoryObject::updateParentOnServer(BOOL) const +{ + // don't do nothin' + llwarns << "LLInventoryObject::updateParentOnServer() called. Doesn't do anything." << llendl; +} + +void LLInventoryObject::updateServer(BOOL) const +{ + // don't do nothin' + llwarns << "LLInventoryObject::updateServer() called. Doesn't do anything." << llendl; +} + + +///---------------------------------------------------------------------------- +/// Class LLInventoryItem +///---------------------------------------------------------------------------- + +LLInventoryItem::LLInventoryItem( + const LLUUID& uuid, + const LLUUID& parent_uuid, + const LLPermissions& permissions, + const LLUUID& asset_uuid, + LLAssetType::EType type, + LLInventoryType::EType inv_type, + const LLString& name, + const LLString& desc, + const LLSaleInfo& sale_info, + U32 flags, + S32 creation_date_utc) : + LLInventoryObject(uuid, parent_uuid, type, name), + mPermissions(permissions), + mAssetUUID(asset_uuid), + mDescription(desc), + mSaleInfo(sale_info), + mInventoryType(inv_type), + mFlags(flags), + mCreationDate(creation_date_utc) +{ + LLString::replaceNonstandardASCII(mDescription, ' '); + LLString::replaceChar(mDescription, '|', ' '); +} + +LLInventoryItem::LLInventoryItem() : + LLInventoryObject(), + mPermissions(), + mAssetUUID(), + mDescription(), + mSaleInfo(), + mInventoryType(LLInventoryType::IT_NONE), + mFlags(0), + mCreationDate(0) +{ +} + +LLInventoryItem::LLInventoryItem(const LLInventoryItem* other) : + LLInventoryObject() +{ + copy(other); +} + +LLInventoryItem::~LLInventoryItem() +{ +} + +// virtual +void LLInventoryItem::copy(const LLInventoryItem* other) +{ + LLInventoryObject::copy(other); + mPermissions = other->mPermissions; + mAssetUUID = other->mAssetUUID; + mDescription = other->mDescription; + mSaleInfo = other->mSaleInfo; + mInventoryType = other->mInventoryType; + mFlags = other->mFlags; + mCreationDate = other->mCreationDate; +} + +// As a constructor alternative, the clone() method works like a +// copy constructor, but gens a new UUID. +void LLInventoryItem::clone(LLPointer<LLInventoryItem>& newitem) const +{ + newitem = new LLInventoryItem; + newitem->copy(this); + newitem->mUUID.generate(); +} + +const LLPermissions& LLInventoryItem::getPermissions() const +{ + return mPermissions; +} + +const LLUUID& LLInventoryItem::getCreatorUUID() const +{ + return mPermissions.getCreator(); +} + +const LLUUID& LLInventoryItem::getAssetUUID() const +{ + return mAssetUUID; +} + +void LLInventoryItem::setAssetUUID(const LLUUID& asset_id) +{ + mAssetUUID = asset_id; +} + + +const LLString& LLInventoryItem::getDescription() const +{ + return mDescription; +} + +S32 LLInventoryItem::getCreationDate() const +{ + return mCreationDate; +} + +U32 LLInventoryItem::getCRC32() const +{ + // *FIX: Not a real crc - more of a checksum. + // *NOTE: We currently do not validate the name or description, + // but if they change in transit, it's no big deal. + U32 crc = mUUID.getCRC32(); + //lldebugs << "1 crc: " << std::hex << crc << std::dec << llendl; + crc += mParentUUID.getCRC32(); + //lldebugs << "2 crc: " << std::hex << crc << std::dec << llendl; + crc += mPermissions.getCRC32(); + //lldebugs << "3 crc: " << std::hex << crc << std::dec << llendl; + crc += mAssetUUID.getCRC32(); + //lldebugs << "4 crc: " << std::hex << crc << std::dec << llendl; + crc += mType; + //lldebugs << "5 crc: " << std::hex << crc << std::dec << llendl; + crc += mInventoryType; + //lldebugs << "6 crc: " << std::hex << crc << std::dec << llendl; + crc += mFlags; + //lldebugs << "7 crc: " << std::hex << crc << std::dec << llendl; + crc += mSaleInfo.getCRC32(); + //lldebugs << "8 crc: " << std::hex << crc << std::dec << llendl; + crc += mCreationDate; + //lldebugs << "9 crc: " << std::hex << crc << std::dec << llendl; + return crc; +} + + +void LLInventoryItem::setDescription(const LLString& d) +{ + LLString new_desc(d); + LLString::replaceNonstandardASCII(new_desc, ' '); + LLString::replaceChar(new_desc, '|', ' '); + if( new_desc != mDescription ) + { + mDescription = new_desc; + } +} + +void LLInventoryItem::setPermissions(const LLPermissions& perm) +{ + mPermissions = perm; +} + +void LLInventoryItem::setInventoryType(LLInventoryType::EType inv_type) +{ + mInventoryType = inv_type; +} + +void LLInventoryItem::setFlags(U32 flags) +{ + mFlags = flags; +} + +void LLInventoryItem::setCreationDate(S32 creation_date_utc) +{ + mCreationDate = creation_date_utc; +} + + +const LLSaleInfo& LLInventoryItem::getSaleInfo() const +{ + return mSaleInfo; +} + +void LLInventoryItem::setSaleInfo(const LLSaleInfo& sale_info) +{ + mSaleInfo = sale_info; +} + +LLInventoryType::EType LLInventoryItem::getInventoryType() const +{ + return mInventoryType; +} + +U32 LLInventoryItem::getFlags() const +{ + return mFlags; +} + +// virtual +void LLInventoryItem::packMessage(LLMessageSystem* msg) const +{ + msg->addUUIDFast(_PREHASH_ItemID, mUUID); + msg->addUUIDFast(_PREHASH_FolderID, mParentUUID); + mPermissions.packMessage(msg); + msg->addUUIDFast(_PREHASH_AssetID, mAssetUUID); + 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 LLInventoryItem::unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num) +{ + msg->getUUIDFast(block, _PREHASH_ItemID, mUUID, block_num); + msg->getUUIDFast(block, _PREHASH_FolderID, mParentUUID, block_num); + mPermissions.unpackMessage(msg, block, block_num); + msg->getUUIDFast(block, _PREHASH_AssetID, mAssetUUID, block_num); + + S8 type; + msg->getS8Fast(block, _PREHASH_Type, type, block_num); + mType = static_cast<LLAssetType::EType>(type); + msg->getS8(block, "InvType", type, block_num); + mInventoryType = static_cast<LLInventoryType::EType>(type); + + msg->getU32Fast(block, _PREHASH_Flags, mFlags, block_num); + + mSaleInfo.unpackMultiMessage(msg, block, block_num); + + char name[DB_INV_ITEM_NAME_BUF_SIZE]; + msg->getStringFast(block, _PREHASH_Name, DB_INV_ITEM_NAME_BUF_SIZE, name, block_num); + mName.assign(name); + LLString::replaceNonstandardASCII(mName, ' '); + + char desc[DB_INV_ITEM_DESC_BUF_SIZE]; + msg->getStringFast(block, _PREHASH_Description, DB_INV_ITEM_DESC_BUF_SIZE, desc, block_num); + mDescription.assign(desc); + LLString::replaceNonstandardASCII(mDescription, ' '); + + msg->getS32(block, "CreationDate", mCreationDate, block_num); + + U32 local_crc = getCRC32(); + U32 remote_crc = 0; + msg->getU32(block, "CRC", remote_crc, block_num); +//#define CRC_CHECK +#ifdef CRC_CHECK + if(local_crc == remote_crc) + { + lldebugs << "crc matches" << llendl; + return TRUE; + } + else + { + llwarns << "inventory crc mismatch: local=" << std::hex << local_crc + << " remote=" << remote_crc << std::dec << llendl; + return FALSE; + } +#else + return (local_crc == remote_crc); +#endif +} + +// virtual +BOOL LLInventoryItem::importFile(FILE* fp) +{ + char buffer[MAX_STRING]; + char keyword[MAX_STRING]; + char valuestr[MAX_STRING]; + char junk[MAX_STRING]; + BOOL success = TRUE; + + keyword[0] = '\0'; + valuestr[0] = '\0'; + + mInventoryType = LLInventoryType::IT_NONE; + mAssetUUID.setNull(); + while(success && (!feof(fp))) + { + fgets(buffer, MAX_STRING, fp); + sscanf(buffer, " %254s %254s", keyword, valuestr); + if(!keyword) + { + continue; + } + if(0 == strcmp("{",keyword)) + { + continue; + } + if(0 == strcmp("}", keyword)) + { + break; + } + else if(0 == strcmp("item_id", keyword)) + { + mUUID.set(valuestr); + } + else if(0 == strcmp("parent_id", keyword)) + { + mParentUUID.set(valuestr); + } + else if(0 == strcmp("permissions", keyword)) + { + success = mPermissions.importFile(fp); + } + else if(0 == strcmp("sale_info", keyword)) + { + // Sale info used to contain next owner perm. It is now in + // the permissions. Thus, we read that out, and fix legacy + // objects. It's possible this op would fail, but it + // should pick up the vast majority of the tasks. + BOOL has_perm_mask = FALSE; + U32 perm_mask = 0; + success = mSaleInfo.importFile(fp, has_perm_mask, perm_mask); + if(has_perm_mask) + { + if(perm_mask == PERM_NONE) + { + perm_mask = mPermissions.getMaskOwner(); + } + // fair use fix. + if(!(perm_mask & PERM_COPY)) + { + perm_mask |= PERM_TRANSFER; + } + mPermissions.setMaskNext(perm_mask); + } + } + else if(0 == strcmp("shadow_id", keyword)) + { + mAssetUUID.set(valuestr); + LLXORCipher cipher(MAGIC_ID.mData, UUID_BYTES); + cipher.decrypt(mAssetUUID.mData, UUID_BYTES); + } + else if(0 == strcmp("asset_id", keyword)) + { + mAssetUUID.set(valuestr); + } + else if(0 == strcmp("type", keyword)) + { + mType = LLAssetType::lookup(valuestr); + } + else if(0 == strcmp("inv_type", keyword)) + { + mInventoryType = LLInventoryType::lookup(valuestr); + } + else if(0 == strcmp("flags", keyword)) + { + sscanf(valuestr, "%x", &mFlags); + } + else if(0 == strcmp("name", keyword)) + { + //strcpy(valuestr, buffer + strlen(keyword) + 3); + // *NOTE: Not ANSI C, but widely supported. + sscanf(buffer, " %254s%[\t]%[^|]", keyword, junk, valuestr); + + // IW: sscanf chokes and puts | in valuestr if there's no name + if (valuestr[0] == '|') + { + valuestr[0] = '\000'; + } + + mName.assign(valuestr); + LLString::replaceNonstandardASCII(mName, ' '); + LLString::replaceChar(mName, '|', ' '); + } + else if(0 == strcmp("desc", keyword)) + { + //strcpy(valuestr, buffer + strlen(keyword) + 3); + // *NOTE: Not ANSI C, but widely supported. + sscanf(buffer, " %s%[\t]%[^|]", keyword, junk, valuestr); + + if (valuestr[0] == '|') + { + valuestr[0] = '\000'; + } + + mDescription.assign(valuestr); + LLString::replaceNonstandardASCII(mDescription, ' '); + /* TODO -- ask Ian about this code + const char *donkey = mDescription.c_str(); + if (donkey[0] == '|') + { + llerrs << "Donkey" << llendl; + } + */ + } + else if(0 == strcmp("creation_date", keyword)) + { + sscanf(valuestr, "%d", &mCreationDate); + } + else + { + llwarns << "unknown keyword '" << keyword + << "' in inventory import of item " << mUUID << llendl; + } + } + + // Need to convert 1.0 simstate files to a useful inventory type + // and potentially deal with bad inventory tyes eg, a landmark + // marked as a texture. + if((LLInventoryType::IT_NONE == mInventoryType) + || !inventory_and_asset_types_match(mInventoryType, mType)) + { + lldebugs << "Resetting inventory type for " << mUUID << llendl; + mInventoryType = LLInventoryType::defaultForAssetType(mType); + } + return success; +} + +BOOL LLInventoryItem::exportFile(FILE* fp, BOOL include_asset_key) const +{ + char uuid_str[UUID_STR_LENGTH]; + fprintf(fp, "\tinv_item\t0\n\t{\n"); + mUUID.toString(uuid_str); + fprintf(fp, "\t\titem_id\t%s\n", uuid_str); + mParentUUID.toString(uuid_str); + fprintf(fp, "\t\tparent_id\t%s\n", uuid_str); + mPermissions.exportFile(fp); + + // Check for permissions to see the asset id, and if so write it + // out as an asset id. Otherwise, apply our cheesy encryption. + if(include_asset_key) + { + U32 mask = mPermissions.getMaskBase(); + if(((mask & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED) + || (mAssetUUID.isNull())) + { + mAssetUUID.toString(uuid_str); + fprintf(fp, "\t\tasset_id\t%s\n", uuid_str); + } + else + { + LLUUID shadow_id(mAssetUUID); + LLXORCipher cipher(MAGIC_ID.mData, UUID_BYTES); + cipher.encrypt(shadow_id.mData, UUID_BYTES); + shadow_id.toString(uuid_str); + fprintf(fp, "\t\tshadow_id\t%s\n", uuid_str); + } + } + else + { + LLUUID::null.toString(uuid_str); + fprintf(fp, "\t\tasset_id\t%s\n", uuid_str); + } + fprintf(fp, "\t\ttype\t%s\n", LLAssetType::lookup(mType)); + const char* inv_type_str = LLInventoryType::lookup(mInventoryType); + if(inv_type_str) fprintf(fp, "\t\tinv_type\t%s\n", inv_type_str); + fprintf(fp, "\t\tflags\t%08x\n", mFlags); + mSaleInfo.exportFile(fp); + fprintf(fp, "\t\tname\t%s|\n", mName.c_str()); + fprintf(fp, "\t\tdesc\t%s|\n", mDescription.c_str()); + fprintf(fp, "\t\tcreation_date\t%d\n", mCreationDate); + fprintf(fp,"\t}\n"); + return TRUE; +} + +// virtual +BOOL LLInventoryItem::importLegacyStream(std::istream& input_stream) +{ + char buffer[MAX_STRING]; + char keyword[MAX_STRING]; + char valuestr[MAX_STRING]; + char junk[MAX_STRING]; + BOOL success = TRUE; + + keyword[0] = '\0'; + valuestr[0] = '\0'; + + mInventoryType = LLInventoryType::IT_NONE; + mAssetUUID.setNull(); + while(success && input_stream.good()) + { + input_stream.getline(buffer, MAX_STRING); + sscanf(buffer, " %s %s", keyword, valuestr); + if(!keyword) + { + continue; + } + if(0 == strcmp("{",keyword)) + { + continue; + } + if(0 == strcmp("}", keyword)) + { + break; + } + else if(0 == strcmp("item_id", keyword)) + { + mUUID.set(valuestr); + } + else if(0 == strcmp("parent_id", keyword)) + { + mParentUUID.set(valuestr); + } + else if(0 == strcmp("permissions", keyword)) + { + success = mPermissions.importLegacyStream(input_stream); + } + else if(0 == strcmp("sale_info", keyword)) + { + // Sale info used to contain next owner perm. It is now in + // the permissions. Thus, we read that out, and fix legacy + // objects. It's possible this op would fail, but it + // should pick up the vast majority of the tasks. + BOOL has_perm_mask = FALSE; + U32 perm_mask = 0; + success = mSaleInfo.importLegacyStream(input_stream, has_perm_mask, perm_mask); + if(has_perm_mask) + { + if(perm_mask == PERM_NONE) + { + perm_mask = mPermissions.getMaskOwner(); + } + // fair use fix. + if(!(perm_mask & PERM_COPY)) + { + perm_mask |= PERM_TRANSFER; + } + mPermissions.setMaskNext(perm_mask); + } + } + else if(0 == strcmp("shadow_id", keyword)) + { + mAssetUUID.set(valuestr); + LLXORCipher cipher(MAGIC_ID.mData, UUID_BYTES); + cipher.decrypt(mAssetUUID.mData, UUID_BYTES); + } + else if(0 == strcmp("asset_id", keyword)) + { + mAssetUUID.set(valuestr); + } + else if(0 == strcmp("type", keyword)) + { + mType = LLAssetType::lookup(valuestr); + } + else if(0 == strcmp("inv_type", keyword)) + { + mInventoryType = LLInventoryType::lookup(valuestr); + } + else if(0 == strcmp("flags", keyword)) + { + sscanf(valuestr, "%x", &mFlags); + } + else if(0 == strcmp("name", keyword)) + { + //strcpy(valuestr, buffer + strlen(keyword) + 3); + // *NOTE: Not ANSI C, but widely supported. + sscanf(buffer, " %s%[\t]%[^|]", keyword, junk, valuestr); + + // IW: sscanf chokes and puts | in valuestr if there's no name + if (valuestr[0] == '|') + { + valuestr[0] = '\000'; + } + + mName.assign(valuestr); + LLString::replaceNonstandardASCII(mName, ' '); + LLString::replaceChar(mName, '|', ' '); + } + else if(0 == strcmp("desc", keyword)) + { + //strcpy(valuestr, buffer + strlen(keyword) + 3); + // *NOTE: Not ANSI C, but widely supported. + sscanf(buffer, " %s%[\t]%[^|]", keyword, junk, valuestr); + + if (valuestr[0] == '|') + { + valuestr[0] = '\000'; + } + + mDescription.assign(valuestr); + LLString::replaceNonstandardASCII(mDescription, ' '); + /* TODO -- ask Ian about this code + const char *donkey = mDescription.c_str(); + if (donkey[0] == '|') + { + llerrs << "Donkey" << llendl; + } + */ + } + else if(0 == strcmp("creation_date", keyword)) + { + sscanf(valuestr, "%d", &mCreationDate); + } + else + { + llwarns << "unknown keyword '" << keyword + << "' in inventory import of item " << mUUID << llendl; + } + } + + // Need to convert 1.0 simstate files to a useful inventory type + // and potentially deal with bad inventory tyes eg, a landmark + // marked as a texture. + if((LLInventoryType::IT_NONE == mInventoryType) + || !inventory_and_asset_types_match(mInventoryType, mType)) + { + lldebugs << "Resetting inventory type for " << mUUID << llendl; + mInventoryType = LLInventoryType::defaultForAssetType(mType); + } + return success; +} + +BOOL LLInventoryItem::exportLegacyStream(std::ostream& output_stream, BOOL include_asset_key) const +{ + char uuid_str[UUID_STR_LENGTH]; + output_stream << "\tinv_item\t0\n\t{\n"; + mUUID.toString(uuid_str); + output_stream << "\t\titem_id\t" << uuid_str << "\n"; + mParentUUID.toString(uuid_str); + output_stream << "\t\tparent_id\t" << uuid_str << "\n"; + mPermissions.exportLegacyStream(output_stream); + + // Check for permissions to see the asset id, and if so write it + // out as an asset id. Otherwise, apply our cheesy encryption. + if(include_asset_key) + { + U32 mask = mPermissions.getMaskBase(); + if(((mask & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED) + || (mAssetUUID.isNull())) + { + mAssetUUID.toString(uuid_str); + output_stream << "\t\tasset_id\t" << uuid_str << "\n"; + } + else + { + LLUUID shadow_id(mAssetUUID); + LLXORCipher cipher(MAGIC_ID.mData, UUID_BYTES); + cipher.encrypt(shadow_id.mData, UUID_BYTES); + shadow_id.toString(uuid_str); + output_stream << "\t\tshadow_id\t" << uuid_str << "\n"; + } + } + else + { + LLUUID::null.toString(uuid_str); + output_stream << "\t\tasset_id\t" << uuid_str << "\n"; + } + output_stream << "\t\ttype\t" << LLAssetType::lookup(mType) << "\n"; + const char* inv_type_str = LLInventoryType::lookup(mInventoryType); + if(inv_type_str) + output_stream << "\t\tinv_type\t" << inv_type_str << "\n"; + char buffer[32]; + sprintf(buffer, "\t\tflags\t%08x\n", mFlags); + output_stream << buffer; + mSaleInfo.exportLegacyStream(output_stream); + output_stream << "\t\tname\t" << mName.c_str() << "|\n"; + output_stream << "\t\tdesc\t" << mDescription.c_str() << "|\n"; + output_stream << "\t\tcreation_date\t" << mCreationDate << "\n"; + output_stream << "\t}\n"; + return TRUE; +} + +LLSD LLInventoryItem::asLLSD() const +{ + LLSD sd = LLSD(); + sd["item_id"] = mUUID; + sd["parent_id"] = mParentUUID; + sd["permissions"] = ll_create_sd_from_permissions(mPermissions); + + U32 mask = mPermissions.getMaskBase(); + if(((mask & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED) + || (mAssetUUID.isNull())) + { + sd["asset_id"] = mAssetUUID; + } + else + { + LLUUID shadow_id(mAssetUUID); + LLXORCipher cipher(MAGIC_ID.mData, UUID_BYTES); + cipher.encrypt(shadow_id.mData, UUID_BYTES); + sd["shadow_id"] = shadow_id; + } + sd["type"] = LLAssetType::lookup(mType); + const char* inv_type_str = LLInventoryType::lookup(mInventoryType); + if(inv_type_str) + { + sd["inv_type"] = inv_type_str; + } + sd["flags"] = ll_sd_from_U32(mFlags); + sd["sale_info"] = mSaleInfo; + sd["name"] = mName; + sd["desc"] = mDescription; + sd["creation_date"] = mCreationDate; + + return sd; +} + +bool LLInventoryItem::fromLLSD(LLSD& sd) +{ + mInventoryType = LLInventoryType::IT_NONE; + mAssetUUID.setNull(); + const char *w; + + w = "item_id"; + if (sd.has(w)) + { + mUUID = sd[w]; + } + w = "parent_id"; + if (sd.has(w)) + { + mParentUUID = sd[w]; + } + w = "permissions"; + if (sd.has(w)) + { + mPermissions = ll_permissions_from_sd(sd[w]); + } + w = "sale_info"; + if (sd.has(w)) + { + // Sale info used to contain next owner perm. It is now in + // the permissions. Thus, we read that out, and fix legacy + // objects. It's possible this op would fail, but it + // should pick up the vast majority of the tasks. + BOOL has_perm_mask = FALSE; + U32 perm_mask = 0; + if (!mSaleInfo.fromLLSD(sd[w], has_perm_mask, perm_mask)) + { + goto fail; + } + if (has_perm_mask) + { + if(perm_mask == PERM_NONE) + { + perm_mask = mPermissions.getMaskOwner(); + } + // fair use fix. + if(!(perm_mask & PERM_COPY)) + { + perm_mask |= PERM_TRANSFER; + } + mPermissions.setMaskNext(perm_mask); + } + } + w = "shadow_id"; + if (sd.has(w)) + { + mAssetUUID = sd[w]; + LLXORCipher cipher(MAGIC_ID.mData, UUID_BYTES); + cipher.decrypt(mAssetUUID.mData, UUID_BYTES); + } + w = "asset_id"; + if (sd.has(w)) + { + mAssetUUID = sd[w]; + } + w = "type"; + if (sd.has(w)) + { + mType = LLAssetType::lookup(sd[w].asString().c_str()); + } + w = "inv_type"; + if (sd.has(w)) + { + mInventoryType = LLInventoryType::lookup(sd[w].asString().c_str()); + } + w = "flags"; + if (sd.has(w)) + { + mFlags = ll_U32_from_sd(sd[w]); + } + w = "name"; + if (sd.has(w)) + { + mName = sd[w].asString(); + LLString::replaceNonstandardASCII(mName, ' '); + LLString::replaceChar(mName, '|', ' '); + } + w = "desc"; + if (sd.has(w)) + { + mDescription = sd[w].asString(); + LLString::replaceNonstandardASCII(mDescription, ' '); + } + w = "creation_date"; + if (sd.has(w)) + { + mCreationDate = sd[w]; + } + + // Need to convert 1.0 simstate files to a useful inventory type + // and potentially deal with bad inventory tyes eg, a landmark + // marked as a texture. + if((LLInventoryType::IT_NONE == mInventoryType) + || !inventory_and_asset_types_match(mInventoryType, mType)) + { + lldebugs << "Resetting inventory type for " << mUUID << llendl; + mInventoryType = LLInventoryType::defaultForAssetType(mType); + } + + return true; +fail: + return false; + +} + +LLXMLNode *LLInventoryItem::exportFileXML(BOOL include_asset_key) const +{ + LLMemType m1(LLMemType::MTYPE_INVENTORY); + LLXMLNode *ret = new LLXMLNode("item", FALSE); + + ret->createChild("uuid", TRUE)->setUUIDValue(1, &mUUID); + ret->createChild("parent_uuid", TRUE)->setUUIDValue(1, &mParentUUID); + + mPermissions.exportFileXML()->setParent(ret); + + // Check for permissions to see the asset id, and if so write it + // out as an asset id. Otherwise, apply our cheesy encryption. + if(include_asset_key) + { + U32 mask = mPermissions.getMaskBase(); + if(((mask & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED) + || (mAssetUUID.isNull())) + { + ret->createChild("asset_id", FALSE)->setUUIDValue(1, &mAssetUUID); + } + else + { + LLUUID shadow_id(mAssetUUID); + LLXORCipher cipher(MAGIC_ID.mData, UUID_BYTES); + cipher.encrypt(shadow_id.mData, UUID_BYTES); + + ret->createChild("shadow_id", FALSE)->setUUIDValue(1, &shadow_id); + } + } + + LLString type_str = LLAssetType::lookup(mType); + LLString inv_type_str = LLInventoryType::lookup(mInventoryType); + + ret->createChild("asset_type", FALSE)->setStringValue(1, &type_str); + ret->createChild("inventory_type", FALSE)->setStringValue(1, &inv_type_str); + S32 tmp_flags = (S32) mFlags; + ret->createChild("flags", FALSE)->setByteValue(4, (U8*)(&tmp_flags), LLXMLNode::ENCODING_HEX); + + mSaleInfo.exportFileXML()->setParent(ret); + + LLString temp; + temp.assign(mName); + ret->createChild("name", FALSE)->setStringValue(1, &temp); + temp.assign(mDescription); + ret->createChild("description", FALSE)->setStringValue(1, &temp); + ret->createChild("creation_date", FALSE)->setIntValue(1, &mCreationDate); + + return ret; +} + +BOOL LLInventoryItem::importXML(LLXMLNode* node) +{ + BOOL success = FALSE; + if (node) + { + success = TRUE; + LLXMLNodePtr sub_node; + if (node->getChild("uuid", sub_node)) + success = (1 == sub_node->getUUIDValue(1, &mUUID)); + if (node->getChild("parent_uuid", sub_node)) + success = success && (1 == sub_node->getUUIDValue(1, &mParentUUID)); + if (node->getChild("permissions", sub_node)) + success = success && mPermissions.importXML(sub_node); + if (node->getChild("asset_id", sub_node)) + success = success && (1 == sub_node->getUUIDValue(1, &mAssetUUID)); + if (node->getChild("shadow_id", sub_node)) + { + success = success && (1 == sub_node->getUUIDValue(1, &mAssetUUID)); + LLXORCipher cipher(MAGIC_ID.mData, UUID_BYTES); + cipher.decrypt(mAssetUUID.mData, UUID_BYTES); + } + if (node->getChild("asset_type", sub_node)) + mType = LLAssetType::lookup(sub_node->getValue().c_str()); + if (node->getChild("inventory_type", sub_node)) + mInventoryType = LLInventoryType::lookup(sub_node->getValue().c_str()); + if (node->getChild("flags", sub_node)) + { + S32 tmp_flags = 0; + success = success && (1 == sub_node->getIntValue(1, &tmp_flags)); + mFlags = (U32) tmp_flags; + } + if (node->getChild("sale_info", sub_node)) + success = success && mSaleInfo.importXML(sub_node); + if (node->getChild("name", sub_node)) + mName = sub_node->getValue(); + if (node->getChild("description", sub_node)) + mDescription = sub_node->getValue(); + if (node->getChild("creation_date", sub_node)) + success = success && (1 == sub_node->getIntValue(1, &mCreationDate)); + if (!success) + { + lldebugs << "LLInventory::importXML() failed for node named '" + << node->getName() << "'" << llendl; + } + } + return success; +} + +S32 LLInventoryItem::packBinaryBucket(U8* bin_bucket, LLPermissions* perm_override) const +{ + // Figure out which permissions to use. + LLPermissions perm; + if (perm_override) + { + // Use the permissions override. + perm = *perm_override; + } + else + { + // Use the current permissions. + perm = getPermissions(); + } + + // describe the inventory item + char* buffer = (char*) bin_bucket; + char creator_id_str[UUID_STR_LENGTH]; + + perm.getCreator().toString(creator_id_str); + char owner_id_str[UUID_STR_LENGTH]; + perm.getOwner().toString(owner_id_str); + char last_owner_id_str[UUID_STR_LENGTH]; + perm.getLastOwner().toString(last_owner_id_str); + char group_id_str[UUID_STR_LENGTH]; + perm.getGroup().toString(group_id_str); + char asset_id_str[UUID_STR_LENGTH]; + getAssetUUID().toString(asset_id_str); + S32 size = sprintf(buffer, + "%d|%d|%s|%s|%s|%s|%s|%x|%x|%x|%x|%x|%s|%s|%d|%d|%x", + getType(), + getInventoryType(), + getName().c_str(), + creator_id_str, + owner_id_str, + last_owner_id_str, + group_id_str, + perm.getMaskBase(), + perm.getMaskOwner(), + perm.getMaskGroup(), + perm.getMaskEveryone(), + perm.getMaskNextOwner(), + asset_id_str, + getDescription().c_str(), + getSaleInfo().getSaleType(), + getSaleInfo().getSalePrice(), + getFlags()) + 1; + + return size; +} + +void LLInventoryItem::unpackBinaryBucket(U8* bin_bucket, S32 bin_bucket_size) +{ + // Early exit on an empty binary bucket. + if (bin_bucket_size <= 1) return; + + // Convert the bin_bucket into a string. + char* item_buffer = new char[bin_bucket_size+1]; + memcpy(item_buffer, bin_bucket, bin_bucket_size); + item_buffer[bin_bucket_size] = '\0'; + std::string str(item_buffer); + + lldebugs << "item buffer: " << item_buffer << llendl; + delete[] item_buffer; + + // Tokenize the string. + typedef boost::tokenizer<boost::char_separator<char> > tokenizer; + boost::char_separator<char> sep("|", "", boost::keep_empty_tokens); + tokenizer tokens(str, sep); + tokenizer::iterator iter = tokens.begin(); + + // Extract all values. + LLUUID item_id; + item_id.generate(); + setUUID(item_id); + + LLAssetType::EType type; + type = (LLAssetType::EType)(atoi((*(iter++)).c_str())); + setType( type ); + + LLInventoryType::EType inv_type; + inv_type = (LLInventoryType::EType)(atoi((*(iter++)).c_str())); + setInventoryType( inv_type ); + + LLString name((*(iter++)).c_str()); + rename( name ); + + LLUUID creator_id((*(iter++)).c_str()); + LLUUID owner_id((*(iter++)).c_str()); + LLUUID last_owner_id((*(iter++)).c_str()); + LLUUID group_id((*(iter++)).c_str()); + PermissionMask mask_base = strtoul((*(iter++)).c_str(), NULL, 16); + PermissionMask mask_owner = strtoul((*(iter++)).c_str(), NULL, 16); + PermissionMask mask_group = strtoul((*(iter++)).c_str(), NULL, 16); + PermissionMask mask_every = strtoul((*(iter++)).c_str(), NULL, 16); + PermissionMask mask_next = strtoul((*(iter++)).c_str(), NULL, 16); + LLPermissions perm; + perm.init(creator_id, owner_id, last_owner_id, group_id); + perm.initMasks(mask_base, mask_owner, mask_group, mask_every, mask_next); + setPermissions(perm); + //lldebugs << "perm: " << perm << llendl; + + LLUUID asset_id((*(iter++)).c_str()); + setAssetUUID(asset_id); + + LLString desc((*(iter++)).c_str()); + setDescription(desc); + + LLSaleInfo::EForSale sale_type; + sale_type = (LLSaleInfo::EForSale)(atoi((*(iter++)).c_str())); + S32 price = atoi((*(iter++)).c_str()); + LLSaleInfo sale_info(sale_type, price); + setSaleInfo(sale_info); + + U32 flags = strtoul((*(iter++)).c_str(), NULL, 16); + setFlags(flags); + + time_t now = time(NULL); + setCreationDate(now); +} + +// returns TRUE if a should appear before b +BOOL item_dictionary_sort( LLInventoryItem* a, LLInventoryItem* b ) +{ + return (LLString::compareDict( a->getName().c_str(), b->getName().c_str() ) < 0); +} + +// returns TRUE if a should appear before b +BOOL item_date_sort( LLInventoryItem* a, LLInventoryItem* b ) +{ + return a->getCreationDate() < b->getCreationDate(); +} + + +///---------------------------------------------------------------------------- +/// Class LLInventoryCategory +///---------------------------------------------------------------------------- + +LLInventoryCategory::LLInventoryCategory( + const LLUUID& uuid, + const LLUUID& parent_uuid, + LLAssetType::EType preferred_type, + const LLString& name) : + LLInventoryObject(uuid, parent_uuid, LLAssetType::AT_CATEGORY, name), + mPreferredType(preferred_type) +{ +} + +LLInventoryCategory::LLInventoryCategory() : + mPreferredType(LLAssetType::AT_NONE) +{ + mType = LLAssetType::AT_CATEGORY; +} + +LLInventoryCategory::LLInventoryCategory(const LLInventoryCategory* other) : + LLInventoryObject() +{ + copy(other); +} + +LLInventoryCategory::~LLInventoryCategory() +{ +} + +// virtual +void LLInventoryCategory::copy(const LLInventoryCategory* other) +{ + LLInventoryObject::copy(other); + mPreferredType = other->mPreferredType; +} + +LLAssetType::EType LLInventoryCategory::getPreferredType() const +{ + return mPreferredType; +} + +void LLInventoryCategory::setPreferredType(LLAssetType::EType type) +{ + mPreferredType = type; +} + +// virtual +void LLInventoryCategory::packMessage(LLMessageSystem* msg) const +{ + msg->addUUIDFast(_PREHASH_FolderID, mUUID); + msg->addUUIDFast(_PREHASH_ParentID, mParentUUID); + S8 type = static_cast<S8>(mPreferredType); + msg->addS8Fast(_PREHASH_Type, type); + msg->addStringFast(_PREHASH_Name, mName); +} + +// virtual +void LLInventoryCategory::unpackMessage(LLMessageSystem* msg, + const char* block, + S32 block_num) +{ + msg->getUUIDFast(block, _PREHASH_FolderID, mUUID, block_num); + msg->getUUIDFast(block, _PREHASH_ParentID, mParentUUID, block_num); + S8 type; + msg->getS8Fast(block, _PREHASH_Type, type, block_num); + mPreferredType = static_cast<LLAssetType::EType>(type); + char name[DB_INV_ITEM_NAME_BUF_SIZE]; + msg->getStringFast(block, _PREHASH_Name, DB_INV_ITEM_NAME_BUF_SIZE, name, block_num); + mName.assign(name); + LLString::replaceNonstandardASCII(mName, ' '); +} + +// virtual +BOOL LLInventoryCategory::importFile(FILE* fp) +{ + char buffer[MAX_STRING]; + char keyword[MAX_STRING]; + char valuestr[MAX_STRING]; + + keyword[0] = '\0'; + valuestr[0] = '\0'; + while(!feof(fp)) + { + fgets(buffer, MAX_STRING, fp); + sscanf(buffer, " %s %s", keyword, valuestr); + if(!keyword) + { + continue; + } + 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 = LLAssetType::lookup(valuestr); + } + else if(0 == strcmp("name", keyword)) + { + //strcpy(valuestr, buffer + strlen(keyword) + 3); + // *NOTE: Not ANSI C, but widely supported. + sscanf(buffer, " %s %[^|]", keyword, valuestr); + mName.assign(valuestr); + LLString::replaceNonstandardASCII(mName, ' '); + LLString::replaceChar(mName, '|', ' '); + } + else + { + llwarns << "unknown keyword '" << keyword + << "' in inventory import category " << mUUID << llendl; + } + } + return TRUE; +} + +BOOL LLInventoryCategory::exportFile(FILE* fp, BOOL) const +{ + char uuid_str[UUID_STR_LENGTH]; + fprintf(fp, "\tinv_category\t0\n\t{\n"); + mUUID.toString(uuid_str); + fprintf(fp, "\t\tcat_id\t%s\n", uuid_str); + mParentUUID.toString(uuid_str); + fprintf(fp, "\t\tparent_id\t%s\n", uuid_str); + fprintf(fp, "\t\ttype\t%s\n", LLAssetType::lookup(mType)); + fprintf(fp, "\t\tpref_type\t%s\n", LLAssetType::lookup(mPreferredType)); + fprintf(fp, "\t\tname\t%s|\n", mName.c_str()); + fprintf(fp,"\t}\n"); + return TRUE; +} + + +// virtual +BOOL LLInventoryCategory::importLegacyStream(std::istream& input_stream) +{ + char buffer[MAX_STRING]; + char keyword[MAX_STRING]; + char valuestr[MAX_STRING]; + + keyword[0] = '\0'; + valuestr[0] = '\0'; + while(input_stream.good()) + { + input_stream.getline(buffer, MAX_STRING); + sscanf(buffer, " %s %s", keyword, valuestr); + if(!keyword) + { + continue; + } + 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 = LLAssetType::lookup(valuestr); + } + else if(0 == strcmp("name", keyword)) + { + //strcpy(valuestr, buffer + strlen(keyword) + 3); + // *NOTE: Not ANSI C, but widely supported. + sscanf(buffer, " %s %[^|]", keyword, valuestr); + mName.assign(valuestr); + LLString::replaceNonstandardASCII(mName, ' '); + LLString::replaceChar(mName, '|', ' '); + } + else + { + llwarns << "unknown keyword '" << keyword + << "' in inventory import category " << mUUID << llendl; + } + } + return TRUE; +} + +BOOL LLInventoryCategory::exportLegacyStream(std::ostream& output_stream, BOOL) const +{ + char uuid_str[UUID_STR_LENGTH]; + output_stream << "\tinv_category\t0\n\t{\n"; + mUUID.toString(uuid_str); + output_stream << "\t\tcat_id\t" << uuid_str << "\n"; + mParentUUID.toString(uuid_str); + output_stream << "\t\tparent_id\t" << uuid_str << "\n"; + output_stream << "\t\ttype\t" << LLAssetType::lookup(mType) << "\n"; + output_stream << "\t\tpref_type\t" << LLAssetType::lookup(mPreferredType) << "\n"; + output_stream << "\t\tname\t" << mName.c_str() << "|\n"; + output_stream << "\t}\n"; + return TRUE; +} + +///---------------------------------------------------------------------------- +/// Local function definitions +///---------------------------------------------------------------------------- + +bool inventory_and_asset_types_match( + LLInventoryType::EType inventory_type, + LLAssetType::EType asset_type) +{ + bool rv = false; + if((inventory_type >= 0) && (inventory_type < LLInventoryType::IT_COUNT)) + { + for(S32 i = 0; i < MAX_POSSIBLE_ASSET_TYPES; ++i) + { + if(INVENTORY_TO_ASSET_TYPE[inventory_type][i] == asset_type) + { + rv = true; + break; + } + } + } + return rv; +} + +///---------------------------------------------------------------------------- +/// exported functions +///---------------------------------------------------------------------------- + +static const std::string INV_ITEM_ID_LABEL("item_id"); +static const std::string INV_FOLDER_ID_LABEL("folder_id"); +static const std::string INV_PARENT_ID_LABEL("parent_id"); +static const std::string INV_ASSET_TYPE_LABEL("asset_type"); +static const std::string INV_PREFERRED_TYPE_LABEL("preferred_type"); +static const std::string INV_INVENTORY_TYPE_LABEL("inv_type"); +static const std::string INV_NAME_LABEL("name"); +static const std::string INV_DESC_LABEL("description"); +static const std::string INV_PERMISSIONS_LABEL("permissions"); +static const std::string INV_ASSET_ID_LABEL("asset_id"); +static const std::string INV_SALE_INFO_LABEL("sale_info"); +static const std::string INV_FLAGS_LABEL("flags"); +static const std::string INV_CREATION_DATE_LABEL("created_at"); + +LLSD ll_create_sd_from_inventory_item(LLPointer<LLInventoryItem> item) +{ + LLSD rv; + if(item.isNull()) return rv; + if (item->getType() == LLAssetType::AT_NONE) + { + llwarns << "ll_create_sd_from_inventory_item() for item with AT_NONE" + << llendl; + return rv; + } + rv[INV_ITEM_ID_LABEL] = item->getUUID(); + rv[INV_PARENT_ID_LABEL] = item->getParentUUID(); + rv[INV_NAME_LABEL] = item->getName(); + rv[INV_ASSET_TYPE_LABEL] = LLAssetType::lookup(item->getType()); + rv[INV_ASSET_ID_LABEL] = item->getAssetUUID(); + rv[INV_DESC_LABEL] = item->getDescription(); + rv[INV_SALE_INFO_LABEL] = ll_create_sd_from_sale_info(item->getSaleInfo()); + rv[INV_PERMISSIONS_LABEL] = + ll_create_sd_from_permissions(item->getPermissions()); + rv[INV_INVENTORY_TYPE_LABEL] = + LLInventoryType::lookup(item->getInventoryType()); + rv[INV_FLAGS_LABEL] = (S32)item->getFlags(); + rv[INV_CREATION_DATE_LABEL] = item->getCreationDate(); + return rv; +} + +LLPointer<LLInventoryItem> ll_create_item_from_sd(const LLSD& sd_item) +{ + LLPointer<LLInventoryItem> rv = new LLInventoryItem; + rv->setUUID(sd_item[INV_ITEM_ID_LABEL].asUUID()); + rv->setParent(sd_item[INV_PARENT_ID_LABEL].asUUID()); + rv->rename(sd_item[INV_NAME_LABEL].asString()); + rv->setType( + LLAssetType::lookup(sd_item[INV_ASSET_TYPE_LABEL].asString().c_str())); + rv->setAssetUUID(sd_item[INV_ASSET_ID_LABEL].asUUID()); + rv->setDescription(sd_item[INV_DESC_LABEL].asString()); + rv->setSaleInfo(ll_sale_info_from_sd(sd_item[INV_SALE_INFO_LABEL])); + rv->setPermissions(ll_permissions_from_sd(sd_item[INV_PERMISSIONS_LABEL])); + rv->setInventoryType( + LLInventoryType::lookup( + sd_item[INV_INVENTORY_TYPE_LABEL].asString().c_str())); + rv->setFlags((U32)(sd_item[INV_FLAGS_LABEL].asInteger())); + rv->setCreationDate(sd_item[INV_CREATION_DATE_LABEL].asInteger()); + return rv; +} + +LLSD ll_create_sd_from_inventory_category(LLPointer<LLInventoryCategory> cat) +{ + LLSD rv; + if(cat.isNull()) return rv; + if (cat->getType() == LLAssetType::AT_NONE) + { + llwarns << "ll_create_sd_from_inventory_category() for cat with AT_NONE" + << llendl; + return rv; + } + rv[INV_FOLDER_ID_LABEL] = cat->getUUID(); + rv[INV_PARENT_ID_LABEL] = cat->getParentUUID(); + rv[INV_NAME_LABEL] = cat->getName(); + rv[INV_ASSET_TYPE_LABEL] = LLAssetType::lookup(cat->getType()); + if(LLAssetType::AT_NONE != cat->getPreferredType()) + { + rv[INV_PREFERRED_TYPE_LABEL] = + LLAssetType::lookup(cat->getPreferredType()); + } + return rv; +} + +LLPointer<LLInventoryCategory> ll_create_category_from_sd(const LLSD& sd_cat) +{ + LLPointer<LLInventoryCategory> rv = new LLInventoryCategory; + rv->setUUID(sd_cat[INV_FOLDER_ID_LABEL].asUUID()); + rv->setParent(sd_cat[INV_PARENT_ID_LABEL].asUUID()); + rv->rename(sd_cat[INV_NAME_LABEL].asString()); + rv->setType( + LLAssetType::lookup(sd_cat[INV_ASSET_TYPE_LABEL].asString().c_str())); + rv->setPreferredType( + LLAssetType::lookup( + sd_cat[INV_PREFERRED_TYPE_LABEL].asString().c_str())); + return rv; +} diff --git a/indra/llinventory/llinventory.h b/indra/llinventory/llinventory.h new file mode 100644 index 0000000000..9f750c46b2 --- /dev/null +++ b/indra/llinventory/llinventory.h @@ -0,0 +1,411 @@ +/** + * @file llinventory.h + * @brief LLInventoryItem and LLInventoryCategory class declaration. + * + * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LL_LLINVENTORY_H +#define LL_LLINVENTORY_H + +#include <functional> + +#include "llassetstorage.h" +#include "lldarray.h" +#include "llpermissions.h" +#include "llsaleinfo.h" +#include "llsd.h" +#include "lluuid.h" +#include "llxmlnode.h" + +// consts for Key field in the task inventory update message +extern const U8 TASK_INVENTORY_ITEM_KEY; +extern const U8 TASK_INVENTORY_ASSET_KEY; + +// anonymous enumeration to specify a max inventory buffer size for +// use in packBinaryBucket() +enum +{ + MAX_INVENTORY_BUFFER_SIZE = 1024 +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryType +// +// Class used to encapsulate operations around inventory type. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLInventoryType +{ +public: + enum EType + { + IT_TEXTURE = 0, + IT_SOUND = 1, + IT_CALLINGCARD = 2, + IT_LANDMARK = 3, + //IT_SCRIPT = 4, + //IT_CLOTHING = 5, + IT_OBJECT = 6, + IT_NOTECARD = 7, + IT_CATEGORY = 8, + IT_ROOT_CATEGORY = 9, + IT_LSL = 10, + //IT_LSL_BYTECODE = 11, + //IT_TEXTURE_TGA = 12, + //IT_BODYPART = 13, + //IT_TRASH = 14, + IT_SNAPSHOT = 15, + //IT_LOST_AND_FOUND = 16, + IT_ATTACHMENT = 17, + IT_WEARABLE = 18, + IT_ANIMATION = 19, + IT_GESTURE = 20, + IT_COUNT = 21, + + IT_NONE = -1 + }; + + // machine transation between type and strings + static EType lookup(const char* name); + static const char* lookup(EType type); + + // translation from a type to a human readable form. + static const char* lookupHumanReadable(EType type); + + // return the default inventory for the given asset type. + static EType defaultForAssetType(LLAssetType::EType asset_type); + +private: + // don't instantiate or derive one of these objects + LLInventoryType( void ) {} + ~LLInventoryType( void ) {} + +//private: +// static const char* mInventoryTypeNames[]; +// static const char* mInventoryTypeHumanNames[]; +// static LLInventoryType::EType mInventoryAssetType[]; +}; + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryObject +// +// This is the base class for inventory objects that handles the +// common code between items and categories. The 'mParentUUID' member +// means the parent category since all inventory objects except each +// user's root category are in some category. Each user's root +// category will have mParentUUID==LLUUID::null. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLMessageSystem; + +class LLInventoryObject : public LLRefCount +{ +protected: + LLUUID mUUID; + LLUUID mParentUUID; + LLAssetType::EType mType; + LLString mName; + +protected: + virtual ~LLInventoryObject( void ); + +public: + MEM_TYPE_NEW(LLMemType::MTYPE_INVENTORY); + LLInventoryObject(const LLUUID& uuid, const LLUUID& parent_uuid, + LLAssetType::EType type, const LLString& name); + LLInventoryObject(); + virtual void copy(const LLInventoryObject* other); // LLRefCount requires custom copy + + // accessors + const LLUUID& getUUID() const; + const LLUUID& getParentUUID() const; + const LLString& getName() const; + LLAssetType::EType getType() const; + + // mutators - will not call updateServer(); + void setUUID(const LLUUID& new_uuid); + void rename(const LLString& new_name); + void setParent(const LLUUID& new_parent); + void setType(LLAssetType::EType type); + + // file support - implemented here so that a minimal information + // set can be transmitted between simulator and viewer. +// virtual BOOL importFile(FILE* fp); + virtual BOOL exportFile(FILE* fp, BOOL include_asset_key = TRUE) const; + + virtual BOOL importLegacyStream(std::istream& input_stream); + virtual BOOL exportLegacyStream(std::ostream& output_stream, BOOL include_asset_key = TRUE) const; + + // virtual methods + virtual void removeFromServer(); + virtual void updateParentOnServer(BOOL) const; + virtual void updateServer(BOOL) const; +}; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryItem +// +// An inventory item represents something that the current user has in +// their inventory. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLInventoryItem : public LLInventoryObject +{ +public: + typedef LLDynamicArray<LLPointer<LLInventoryItem> > item_array_t; + +protected: + LLPermissions mPermissions; + LLUUID mAssetUUID; + LLString mDescription; + LLSaleInfo mSaleInfo; + LLInventoryType::EType mInventoryType; + U32 mFlags; + S32 mCreationDate; // seconds from 1/1/1970, UTC + +public: + enum + { + // The meaning of LLInventoryItem::mFlags is distinct for each + // inventory type. + II_FLAGS_NONE = 0, + + // landmark flags + II_FLAGS_LANDMARK_VISITED = 1, + + // flag to indicate that object permissions should have next + // owner perm be more restrictive on rez. We bump this into + // the second byte of the flags since the low byte is used to + // track attachment points. + II_FLAGS_OBJECT_SLAM_PERM = 0x100, + + // These flags specify which permissions masks to overwrite + // upon rez. Normally, if no permissions slam (above) or + // overwrite flags are set, the asset's permissions are + // used and the inventory's permissions are ignored. If + // any of these flags are set, the inventory's permissions + // take precedence. + II_FLAGS_OBJECT_PERM_OVERWRITE_BASE = 0x010000, + II_FLAGS_OBJECT_PERM_OVERWRITE_OWNER = 0x020000, + II_FLAGS_OBJECT_PERM_OVERWRITE_GROUP = 0x040000, + II_FLAGS_OBJECT_PERM_OVERWRITE_EVERYONE = 0x080000, + II_FLAGS_OBJECT_PERM_OVERWRITE_NEXT_OWNER = 0x100000, + + // wearables use the low order byte of flags to store the + // EWearableType enumeration found in newview/llwearable.h + }; + +protected: + ~LLInventoryItem(); // ref counted + +public: + MEM_TYPE_NEW(LLMemType::MTYPE_INVENTORY); + LLInventoryItem(const LLUUID& uuid, + const LLUUID& parent_uuid, + const LLPermissions& permissions, + const LLUUID& asset_uuid, + LLAssetType::EType type, + LLInventoryType::EType inv_type, + const LLString& name, + const LLString& desc, + const LLSaleInfo& sale_info, + U32 flags, + S32 creation_date_utc); + LLInventoryItem(); + // Create a copy of an inventory item from a pointer to another item + // Note: Because InventoryItems are ref counted, reference copy (a = b) + // is prohibited + LLInventoryItem(const LLInventoryItem* other); + virtual void copy(const LLInventoryItem* other); // LLRefCount requires custom copy + + // As a constructor alternative, the clone() method works like a + // copy constructor, but gens a new UUID. + // It is up to the caller to delete (unref) the item. + virtual void clone(LLPointer<LLInventoryItem>& newitem) const; + + // accessors + const LLPermissions& getPermissions() const; + const LLUUID& getCreatorUUID() const; + const LLUUID& getAssetUUID() const; + const LLString& getDescription() const; + const LLSaleInfo& getSaleInfo() const; + LLInventoryType::EType getInventoryType() const; + U32 getFlags() const; + S32 getCreationDate() const; + U32 getCRC32() const; // really more of a checksum. + + // mutators - will not call updateServer(), and will never fail + // (though it may correct to sane values) + void setAssetUUID(const LLUUID& asset_id); + void setDescription(const LLString& new_desc); + void setSaleInfo(const LLSaleInfo& sale_info); + void setPermissions(const LLPermissions& perm); + void setInventoryType(LLInventoryType::EType inv_type); + void setFlags(U32 flags); + void setCreationDate(S32 creation_date_utc); + + // Put this inventory item onto the current outgoing mesage. It + // assumes you have already called nextBlock(). + virtual void packMessage(LLMessageSystem* msg) const; + + // unpack returns TRUE if the inventory item came through the + // network ok. It uses a simple crc check which is defeatable, but + // we want to detect network mangling somehow. + virtual BOOL unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num = 0); + + // file support + virtual BOOL importFile(FILE* fp); + virtual BOOL exportFile(FILE* fp, BOOL include_asset_key = TRUE) const; + + virtual BOOL importLegacyStream(std::istream& input_stream); + virtual BOOL exportLegacyStream(std::ostream& output_stream, BOOL include_asset_key = TRUE) const; + + virtual LLXMLNode *exportFileXML(BOOL include_asset_key = TRUE) const; + BOOL importXML(LLXMLNode* node); + + // helper functions + + // pack all information needed to reconstruct this item into the given binary bucket. + // perm_override is optional + S32 packBinaryBucket(U8* bin_bucket, LLPermissions* perm_override = NULL) const; + void unpackBinaryBucket(U8* bin_bucket, S32 bin_bucket_size); + LLSD asLLSD() const; + bool fromLLSD(LLSD& sd); + +}; + +BOOL item_dictionary_sort(LLInventoryItem* a,LLInventoryItem* b); +BOOL item_date_sort(LLInventoryItem* a, LLInventoryItem* b); + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLInventoryCategory +// +// An instance of this class represents a category of inventory +// items. Users come with a set of default categories, and can create +// new ones as needed. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLInventoryCategory : public LLInventoryObject +{ +public: + typedef LLDynamicArray<LLPointer<LLInventoryCategory> > cat_array_t; + +protected: + ~LLInventoryCategory(); + +public: + MEM_TYPE_NEW(LLMemType::MTYPE_INVENTORY); + LLInventoryCategory(const LLUUID& uuid, const LLUUID& parent_uuid, + LLAssetType::EType preferred_type, + const LLString& name); + LLInventoryCategory(); + LLInventoryCategory(const LLInventoryCategory* other); + virtual void copy(const LLInventoryCategory* other); // LLRefCount requires custom copy + + // accessors and mutators + LLAssetType::EType getPreferredType() const; + void setPreferredType(LLAssetType::EType type); + // For messaging system support + virtual void packMessage(LLMessageSystem* msg) const; + virtual void unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num = 0); + + // file support + virtual BOOL importFile(FILE* fp); + virtual BOOL exportFile(FILE* fp, BOOL include_asset_key = TRUE) const; + + virtual BOOL importLegacyStream(std::istream& input_stream); + virtual BOOL exportLegacyStream(std::ostream& output_stream, BOOL include_asset_key = TRUE) const; + +protected: + // The type of asset that this category was "meant" to hold + // (although it may in fact hold any type). + LLAssetType::EType mPreferredType; + +}; + + +//----------------------------------------------------------------------------- +// Useful bits +//----------------------------------------------------------------------------- + +// This functor tests if an item is transferrable and returns true if +// it is. Derived from unary_function<> so that the object can be used +// in stl-compliant adaptable predicates (eg, not1<>). You might want +// to use this in std::partition() or similar logic. +struct IsItemTransferable : public std::unary_function<LLInventoryItem*, bool> +{ + LLUUID mDestID; + IsItemTransferable(const LLUUID& dest_id) : mDestID(dest_id) {} + bool operator()(const LLInventoryItem* item) const + { + return (item->getPermissions().allowTransferTo(mDestID)) ? true:false; + } +}; + +// This functor is used to set the owner and group of inventory items, +// for example, in a simple std::for_each() loop. Note that the call +// to setOwnerAndGroup can fail if authority_id != LLUUID::null. +struct SetItemOwnerAndGroup +{ + LLUUID mAuthorityID; + LLUUID mOwnerID; + LLUUID mGroupID; + SetItemOwnerAndGroup(const LLUUID& authority_id, + const LLUUID& owner_id, + const LLUUID& group_id) : + mAuthorityID(authority_id), mOwnerID(owner_id), mGroupID(group_id) {} + void operator()(LLInventoryItem* item) const + { + LLPermissions perm = item->getPermissions(); + bool is_atomic = (LLAssetType::AT_OBJECT == item->getType()) ? false : true; + perm.setOwnerAndGroup(mAuthorityID, mOwnerID, mGroupID, is_atomic); + item->setPermissions(perm); + } +}; + +// This functor is used to unset the share with group, everyone perms, and +// for sale info for objects being sold through contents. +struct SetNotForSale +{ + LLUUID mAgentID; + LLUUID mGroupID; + SetNotForSale(const LLUUID& agent_id, + const LLUUID& group_id) : + mAgentID(agent_id), mGroupID(group_id) {} + void operator()(LLInventoryItem* item) const + { + // Clear group & everyone permissions. + LLPermissions perm = item->getPermissions(); + perm.setGroupBits(mAgentID, mGroupID, FALSE, PERM_MODIFY | PERM_MOVE | PERM_COPY); + perm.setEveryoneBits(mAgentID, mGroupID, FALSE, PERM_MOVE | PERM_COPY); + item->setPermissions(perm); + + // Mark group & everyone permissions for overwrite on the next + // rez if it is an object. + if(LLAssetType::AT_OBJECT == item->getType()) + { + U32 flags = item->getFlags(); + flags |= LLInventoryItem::II_FLAGS_OBJECT_PERM_OVERWRITE_GROUP; + flags |= LLInventoryItem::II_FLAGS_OBJECT_PERM_OVERWRITE_EVERYONE; + item->setFlags(flags); + } + + // Clear for sale info. + item->setSaleInfo(LLSaleInfo::DEFAULT); + } +}; + +typedef std::list<LLPointer<LLInventoryObject> > InventoryObjectList; + +// These functions convert between structured data and an inventroy +// item, appropriate for serialization. +LLSD ll_create_sd_from_inventory_item(LLPointer<LLInventoryItem> item); +LLPointer<LLInventoryItem> ll_create_item_from_sd(const LLSD& sd_item); +LLSD ll_create_sd_from_inventory_category(LLPointer<LLInventoryCategory> cat); +LLPointer<LLInventoryCategory> ll_create_category_from_sd(const LLSD& sd_cat); + +#endif // LL_LLINVENTORY_H diff --git a/indra/llinventory/llnotecard.cpp b/indra/llinventory/llnotecard.cpp new file mode 100644 index 0000000000..3e994a61aa --- /dev/null +++ b/indra/llinventory/llnotecard.cpp @@ -0,0 +1,286 @@ +/** + * @file llnotecard.cpp + * @brief LLNotecard class definition + * + * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "linden_common.h" +#include "llinventory.h" +#include "llnotecard.h" +#include "llstreamtools.h" + +LLNotecard::LLNotecard(U32 max_text) +: mMaxText(max_text) +{ +} + +LLNotecard::~LLNotecard() +{ +} + +bool LLNotecard::importEmbeddedItemsStream(std::istream& str) +{ + // Version 1 format: + // LLEmbeddedItems version 1 + // { + // count <number of entries being used and not deleted> + // { + // ext char index <index> + // <InventoryItem chunk> + // } + // } + + S32 i; + S32 count = 0; + + str >> std::ws >> "LLEmbeddedItems version" >> mEmbeddedVersion >> "\n"; + if (str.fail()) + { + llwarns << "Invalid Linden text file header" << llendl; + goto import_file_failed; + } + + if( 1 != mEmbeddedVersion ) + { + llwarns << "Invalid LLEmbeddedItems version: " << mEmbeddedVersion << llendl; + goto import_file_failed; + } + + str >> std::ws >> "{\n"; + if(str.fail()) + { + llwarns << "Invalid Linden text file format: missing {" << llendl; + goto import_file_failed; + } + + str >> std::ws >> "count " >> count >> "\n"; + if(str.fail()) + { + llwarns << "Invalid LLEmbeddedItems count" << llendl; + goto import_file_failed; + } + + if((count < 0)) + { + llwarns << "Invalid LLEmbeddedItems count value: " << count << llendl; + goto import_file_failed; + } + + for(i = 0; i < count; i++) + { + str >> std::ws >> "{\n"; + if(str.fail()) + { + llwarns << "Invalid LLEmbeddedItems file format: missing {" << llendl; + goto import_file_failed; + } + + U32 index = 0; + str >> std::ws >> "ext char index " >> index >> "\n"; + if(str.fail()) + { + llwarns << "Invalid LLEmbeddedItems file format: missing ext char index" << llendl; + goto import_file_failed; + } + + if( (index < 0) ) + { + llwarns << "Invalid LLEmbeddedItems file format: invalid ext char index: " << index << llendl; + goto import_file_failed; + } + + str >> std::ws >> "inv_item\t0\n"; + if(str.fail()) + { + llwarns << "Invalid LLEmbeddedItems file format: missing inv_item" << llendl; + goto import_file_failed; + } + + LLPointer<LLInventoryItem> item = new LLInventoryItem; + if (!item->importLegacyStream(str)) + { + llinfos << "notecard import failed" << llendl; + goto import_file_failed; + } + mItems.push_back(item); + + str >> std::ws >> "}\n"; + if(str.fail()) + { + llwarns << "Invalid LLEmbeddedItems file format: missing }" << llendl; + goto import_file_failed; + } + } + + str >> std::ws >> "}\n"; + if(str.fail()) + { + llwarns << "Invalid LLEmbeddedItems file format: missing }" << llendl; + goto import_file_failed; + } + + return true; + + import_file_failed: + return false; +} + +bool LLNotecard::importStream(std::istream& str) +{ + // Version 1 format: + // Linden text version 1 + // { + // <EmbeddedItemList chunk> + // Text length + // <ASCII text; 0x80 | index = embedded item> + // } + + // Version 2 format: (NOTE: Imports identically to version 1) + // Linden text version 2 + // { + // <EmbeddedItemList chunk> + // Text length + // <UTF8 text; FIRST_EMBEDDED_CHAR + index = embedded item> + // } + + str >> std::ws >> "Linden text version " >> mVersion >> "\n"; + if(str.fail()) + { + llwarns << "Invalid Linden text file header " << llendl; + return FALSE; + } + + if( 1 != mVersion && 2 != mVersion) + { + llwarns << "Invalid Linden text file version: " << mVersion << llendl; + return FALSE; + } + + str >> std::ws >> "{\n"; + if(str.fail()) + { + llwarns << "Invalid Linden text file format" << llendl; + return FALSE; + } + + if(!importEmbeddedItemsStream(str)) + { + return FALSE; + } + + char line_buf[STD_STRING_BUF_SIZE]; + str.getline(line_buf, STD_STRING_BUF_SIZE); + if(str.fail()) + { + llwarns << "Invalid Linden text length field" << llendl; + return FALSE; + } + line_buf[STD_STRING_STR_LEN] = '\0'; + + U32 text_len = 0; + if( 1 != sscanf(line_buf, "Text length %d", &text_len) ) + { + llwarns << "Invalid Linden text length field" << llendl; + return FALSE; + } + + if(text_len > mMaxText) + { + llwarns << "Invalid Linden text length: " << text_len << llendl; + return FALSE; + } + + BOOL success = TRUE; + + char* text = new char[text_len + 1]; + fullread(str, text, text_len); + if(str.fail()) + { + llwarns << "Invalid Linden text: text shorter than text length: " << text_len << llendl; + success = FALSE; + } + text[text_len] = '\0'; + + if(success) + { + // Actually set the text + mText = text; + } + + delete[] text; + + return success; +} + +//////////////////////////////////////////////////////////////////////////// + +bool LLNotecard::exportEmbeddedItemsStream( std::ostream& out_stream ) +{ + out_stream << "LLEmbeddedItems version 1\n"; + out_stream << "{\n"; + + out_stream << llformat("count %d\n", mItems.size() ); + + S32 idx = 0; + for (std::vector<LLPointer<LLInventoryItem> >::iterator iter = mItems.begin(); + iter != mItems.end(); ++iter) + { + LLInventoryItem* item = *iter; + if (item) + { + out_stream << "{\n"; + out_stream << llformat("ext char index %d\n", idx ); + if( !item->exportLegacyStream( out_stream ) ) + { + return FALSE; + } + out_stream << "}\n"; + } + ++idx; + } + + out_stream << "}\n"; + + return TRUE; +} + +bool LLNotecard::exportStream( std::ostream& out_stream ) +{ + out_stream << "Linden text version 2\n"; + out_stream << "{\n"; + + if( !exportEmbeddedItemsStream( out_stream ) ) + { + return FALSE; + } + + out_stream << llformat("Text length %d\n", mText.length() ); + out_stream << mText; + out_stream << "}\n"; + + return TRUE; +} + +//////////////////////////////////////////////////////////////////////////// + +const std::vector<LLPointer<LLInventoryItem> >& LLNotecard::getItems() const +{ + return mItems; +} + +LLString& LLNotecard::getText() +{ + return mText; +} + +void LLNotecard::setItems(const std::vector<LLPointer<LLInventoryItem> >& items) +{ + mItems = items; +} + +void LLNotecard::setText(const LLString& text) +{ + mText = text; +} diff --git a/indra/llinventory/llnotecard.h b/indra/llinventory/llnotecard.h new file mode 100644 index 0000000000..5f510a674f --- /dev/null +++ b/indra/llinventory/llnotecard.h @@ -0,0 +1,41 @@ +/** + * @file llnotecard.h + * @brief LLNotecard class declaration + * + * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LL_NOTECARD_H +#define LL_NOTECARD_H + +const S32 MAX_NOTECARD_SIZE = 65536; + +class LLNotecard +{ +public: + LLNotecard(U32 max_text); + virtual ~LLNotecard(); + + bool importStream(std::istream& str); + bool exportStream(std::ostream& str); + + const std::vector<LLPointer<LLInventoryItem> >& getItems() const; + LLString& getText(); + + void setItems(const std::vector<LLPointer<LLInventoryItem> >& items); + void setText(const LLString& text); + S32 getVersion() { return mVersion; } + S32 getEmbeddedVersion() { return mEmbeddedVersion; } + +private: + bool importEmbeddedItemsStream(std::istream& str); + bool exportEmbeddedItemsStream(std::ostream& str); + std::vector<LLPointer<LLInventoryItem> > mItems; + LLString mText; + U32 mMaxText; + S32 mVersion; + S32 mEmbeddedVersion; +}; + +#endif /* LL_NOTECARD_H */ diff --git a/indra/llinventory/llparcel.cpp b/indra/llinventory/llparcel.cpp new file mode 100644 index 0000000000..a19c2216df --- /dev/null +++ b/indra/llinventory/llparcel.cpp @@ -0,0 +1,1813 @@ +/** + * @file llparcel.cpp + * @brief A land parcel. + * + * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "linden_common.h" + +#include "indra_constants.h" +#include <iostream> + +#include "llparcel.h" +#include "llstreamtools.h" + +#include "llmath.h" +#include "lltransactiontypes.h" +#include "lltransactionflags.h" +#include "message.h" +#include "u64.h" + +static const F32 SOME_BIG_NUMBER = 1000.0f; +static const F32 SOME_BIG_NEG_NUMBER = -1000.0f; +static const char* PARCEL_OWNERSHIP_STATUS_STRING[LLParcel::OS_COUNT] = +{ + "leased", + "lease_pending", + "abandoned" +}; + +// NOTE: Adding parcel categories also requires updating: +// * newview/app_settings/floater_directory.xml category combobox +// * Web site "create event" tools +static const char* PARCEL_CATEGORY_STRING[LLParcel::C_COUNT] = +{ + "none", + "linden", + "adult", + "arts", + "store", // "business" legacy name + "educational", + "game", // "gaming" legacy name + "gather", // "hangout" legacy name + "newcomer", + "park", + "home", // "residential" legacy name + "shopping", + "stage", + "other", +}; +static const char* PARCEL_CATEGORY_UI_STRING[LLParcel::C_COUNT + 1] = +{ + "None", + "Linden Location", + "Adult", + "Arts & Culture", + "Business", + "Educational", + "Gaming", + "Hangout", + "Newcomer Friendly", + "Parks & Nature", + "Residential", + "Shopping", + "Stage", + "Other", + "Any", // valid string for parcel searches +}; + +static const char* PARCEL_ACTION_STRING[LLParcel::A_COUNT + 1] = +{ + "create", + "release", + "absorb", + "absorbed", + "divide", + "division", + "acquire", + "relinquish", + "confirm", + "unknown" +}; + +// Timeouts for parcels +// default is 21 days * 24h/d * 60m/h * 60s/m *1000000 usec/s = 1814400000000 +const U64 DEFAULT_USEC_CONVERSION_TIMEOUT = U64L(1814400000000); +// ***** TESTING is 10 minutes +//const U64 DEFAULT_USEC_CONVERSION_TIMEOUT = U64L(600000000); + +// group is 60 days * 24h/d * 60m/h * 60s/m *1000000 usec/s = 5184000000000 +const U64 GROUP_USEC_CONVERSION_TIMEOUT = U64L(5184000000000); +// ***** TESTING is 10 minutes +//const U64 GROUP_USEC_CONVERSION_TIMEOUT = U64L(600000000); + +// default sale timeout is 2 days -> 172800000000 +const U64 DEFAULT_USEC_SALE_TIMEOUT = U64L(172800000000); +// ***** TESTING is 10 minutes +//const U64 DEFAULT_USEC_SALE_TIMEOUT = U64L(600000000); + +// more grace period extensions. +const U64 SEVEN_DAYS_IN_USEC = U64L(604800000000); + +// if more than 100,000s before sale revert, and no extra extension +// has been given, go ahead and extend it more. That's about 1.2 days. +const S32 EXTEND_GRACE_IF_MORE_THAN_SEC = 100000; + + +const char* ownership_status_to_string(LLParcel::EOwnershipStatus status); +LLParcel::EOwnershipStatus ownership_string_to_status(const char* s); +//const char* revert_action_to_string(LLParcel::ESaleTimerExpireAction action); +//LLParcel::ESaleTimerExpireAction revert_string_to_action(const char* s); +const char* category_to_string(LLParcel::ECategory category); +const char* category_to_ui_string(LLParcel::ECategory category); +LLParcel::ECategory category_string_to_category(const char* s); +LLParcel::ECategory category_ui_string_to_category(const char* s); + +LLParcel::LLParcel() +{ + init(LLUUID::null, TRUE, FALSE, FALSE, 0, 0, 0, 0, 0, 1.f, 0); +} + + +LLParcel::LLParcel(const LLUUID &owner_id, + BOOL modify, BOOL terraform, BOOL damage, + time_t claim_date, S32 claim_price_per_meter, + S32 rent_price_per_meter, S32 area, S32 sim_object_limit, F32 parcel_object_bonus, + BOOL is_group_owned) +{ + init( owner_id, modify, terraform, damage, claim_date, + claim_price_per_meter, rent_price_per_meter, area, sim_object_limit, parcel_object_bonus, + is_group_owned); +} + + +// virtual +LLParcel::~LLParcel() +{ + // user list cleaned up by LLDynamicArray destructor. +} + +void LLParcel::init(const LLUUID &owner_id, + BOOL modify, BOOL terraform, BOOL damage, + time_t claim_date, S32 claim_price_per_meter, + S32 rent_price_per_meter, S32 area, S32 sim_object_limit, F32 parcel_object_bonus, + BOOL is_group_owned) +{ + mID.setNull(); + mOwnerID = owner_id; + mGroupOwned = is_group_owned; + mClaimDate = claim_date; + mClaimPricePerMeter = claim_price_per_meter; + mRentPricePerMeter = rent_price_per_meter; + mArea = area; + mDiscountRate = 1.0f; + mDrawDistance = 512.f; + + mUserLookAt.setVec(0.0f, 0.f, 0.f); + // Default to using the parcel's landing point, if any. + mLandingType = L_LANDING_POINT; + + // *FIX: if owner_id != null, should be owned or sale pending, + // investigate init callers. + mStatus = OS_NONE; + mCategory = C_NONE; + mAuthBuyerID.setNull(); + //mBuyerID.setNull(); + //mJoinNeighbors = 0x0; + mSaleTimerExpires.setTimerExpirySec(0); + mSaleTimerExpires.stop(); + mGraceExtension = 0; + //mExpireAction = STEA_REVERT; + mRecordTransaction = FALSE; + + mAuctionID = 0; + mIsReservedForNewbie = FALSE; + mInEscrow = false; + + mParcelFlags = PF_DEFAULT; + setParcelFlag(PF_CREATE_OBJECTS, modify); + setParcelFlag(PF_ALLOW_TERRAFORM, terraform); + setParcelFlag(PF_ALLOW_DAMAGE, damage); + + mSalePrice = 10000; + setName(NULL); + setDesc(NULL); + setMusicURL(NULL); + setMediaURL(NULL); + mMediaID.setNull(); + mMediaAutoScale = 0; + + mGroupID.setNull(); + + mPassPrice = PARCEL_PASS_PRICE_DEFAULT; + mPassHours = PARCEL_PASS_HOURS_DEFAULT; + + mAABBMin.setVec(SOME_BIG_NUMBER, SOME_BIG_NUMBER, SOME_BIG_NUMBER); + mAABBMax.setVec(SOME_BIG_NEG_NUMBER, SOME_BIG_NEG_NUMBER, SOME_BIG_NEG_NUMBER); + + mLocalID = 0; + + //mSimWidePrimCorrection = 0; + setMaxPrimCapacity((S32)(sim_object_limit * area / (F32)(REGION_WIDTH_METERS * REGION_WIDTH_METERS))); + setSimWideMaxPrimCapacity(0); + setSimWidePrimCount(0); + setOwnerPrimCount(0); + setGroupPrimCount(0); + setOtherPrimCount(0); + setSelectedPrimCount(0); + setTempPrimCount(0); + setCleanOtherTime(0); + setParcelPrimBonus(parcel_object_bonus); + + setPreviousOwnerID(LLUUID::null); + setPreviouslyGroupOwned(FALSE); +} + +void LLParcel::overrideOwner(const LLUUID& owner_id, BOOL is_group_owned) +{ + // Override with system permission (LLUUID::null) + // Overridden parcels have no group + mOwnerID = owner_id; + mGroupOwned = is_group_owned; + if(mGroupOwned) + { + mGroupID = mOwnerID; + } + else + { + mGroupID.setNull(); + } + mInEscrow = false; +} + +void LLParcel::overrideParcelFlags(U32 flags) +{ + mParcelFlags = flags; +} + +void set_std_string(const char* src, std::string& dest) +{ + if(src) + { + dest.assign(src); + } + else + { +#if (LL_LINUX && __GNUC__ < 3) + dest.assign(std::string("")); +#else + dest.clear(); +#endif + } +} + +void LLParcel::setName(const char* name) +{ + // The escaping here must match the escaping in the database + // abstraction layer. + set_std_string(name, mName); + LLStringFn::replace_nonprintable(mName, LL_UNKNOWN_CHAR); +} + +void LLParcel::setDesc(const char* desc) +{ + // The escaping here must match the escaping in the database + // abstraction layer. + set_std_string(desc, mDesc); + mDesc = rawstr_to_utf8(mDesc); +} + +void LLParcel::setMusicURL(const char* url) +{ + set_std_string(url, mMusicURL); + + // The escaping here must match the escaping in the database + // abstraction layer. + // This should really filter the url in some way. Other than + // simply requiring non-printable. + LLStringFn::replace_nonprintable(mMusicURL, LL_UNKNOWN_CHAR); +} + +void LLParcel::setMediaURL(const char* url) +{ + set_std_string(url, mMediaURL); + + // The escaping here must match the escaping in the database + // abstraction layer if it's ever added. + // This should really filter the url in some way. Other than + // simply requiring non-printable. + LLStringFn::replace_nonprintable(mMediaURL, LL_UNKNOWN_CHAR); +} + +// virtual +void LLParcel::setLocalID(S32 local_id) +{ + mLocalID = local_id; +} + +void LLParcel::setParcelFlag(U32 flag, BOOL b) +{ + if (b) + { + mParcelFlags |= flag; + } + else + { + mParcelFlags &= ~flag; + } +} + + +BOOL LLParcel::allowModifyBy(const LLUUID &agent_id, const LLUUID &group_id) const +{ + if (agent_id == LLUUID::null) + { + // system always can enter + return TRUE; + } + else if (isPublic()) + { + return TRUE; + } + else if (agent_id == mOwnerID) + { + // owner can always perform operations + return TRUE; + } + else if (mParcelFlags & PF_CREATE_OBJECTS) + { + return TRUE; + } + else if ((mParcelFlags & PF_CREATE_GROUP_OBJECTS) + && group_id.notNull() ) + { + return (getGroupID() == group_id); + } + + return FALSE; +} + +BOOL LLParcel::allowTerraformBy(const LLUUID &agent_id) const +{ + if (agent_id == LLUUID::null) + { + // system always can enter + return TRUE; + } + else if(OS_LEASED == mStatus) + { + if(agent_id == mOwnerID) + { + // owner can modify leased land + return TRUE; + } + else + { + // otherwise check other people + return mParcelFlags & PF_ALLOW_TERRAFORM; + } + } + else + { + return FALSE; + } +} + + +bool LLParcel::isAgentBlockedFromParcel(LLParcel* parcelp, + const LLUUID& agent_id, + const std::vector<LLUUID>& group_ids, + const BOOL is_agent_identified, + const BOOL is_agent_transacted) +{ + S32 current_group_access = parcelp->blockAccess(agent_id, LLUUID::null, is_agent_identified, is_agent_transacted); + S32 count; + bool is_allowed = (current_group_access == BA_ALLOWED) ? true: false; + LLUUID group_id; + + count = group_ids.size(); + for (int i = 0; i < count && !is_allowed; i++) + { + group_id = group_ids[i]; + current_group_access = parcelp->blockAccess(agent_id, group_id, is_agent_identified, is_agent_transacted); + + if (current_group_access == BA_ALLOWED) is_allowed = true; + } + + return !is_allowed; +} + +BOOL LLParcel::isAgentBanned(const LLUUID& agent_id) const +{ + // Test ban list + if (getParcelFlag(PF_USE_BAN_LIST) + && (mBanList.find(agent_id) != mBanList.end())) + { + return TRUE; + } + + return FALSE; +} +S32 LLParcel::blockAccess(const LLUUID& agent_id, const LLUUID& group_id, + const BOOL is_agent_identified, + const BOOL is_agent_transacted) const +{ + // Test ban list + if (isAgentBanned(agent_id)) + { + return BA_BANNED; + } + + // Always allow owner on (unless he banned himself, useful for + // testing). We will also allow estate owners/managers in if they + // are not explicitly banned. + if (agent_id == mOwnerID) + { + return BA_ALLOWED; + } + + // Special case when using pass list where group access is being restricted but not + // using access list. In this case group members are allowed only if they buy a pass. + // We return BA_NOT_IN_LIST if not in list + BOOL passWithGroup = getParcelFlag(PF_USE_PASS_LIST) && !getParcelFlag(PF_USE_ACCESS_LIST) + && getParcelFlag(PF_USE_ACCESS_GROUP) && !mGroupID.isNull() && group_id == mGroupID; + + + // Test group list + if (getParcelFlag(PF_USE_ACCESS_GROUP) + && !mGroupID.isNull() + && group_id == mGroupID + && !passWithGroup) + { + return BA_ALLOWED; + } + + // Test access list + if (getParcelFlag(PF_USE_ACCESS_LIST) || passWithGroup ) + { + if (mAccessList.find(agent_id) != mAccessList.end()) + { + return BA_ALLOWED; + } + + return BA_NOT_ON_LIST; + } + + // If we're not doing any other limitations, all users + // can enter, unless + if ( !getParcelFlag(PF_USE_ACCESS_GROUP) + && !getParcelFlag(PF_USE_ACCESS_LIST)) + { + //If the land is group owned, and you are in the group, bypass these checks + if(getIsGroupOwned() && group_id == mGroupID) + { + return BA_ALLOWED; + } + + // Test for "payment" access levels + // Anonymous - No Payment Info on File + if(getParcelFlag(PF_DENY_ANONYMOUS) && !is_agent_identified && !is_agent_transacted) + { + return BA_NO_ACCESS_LEVEL; + } + // Identified - Payment Info on File + // Must check to make sure we're only banning Identified, since Transacted accounts + // also have their identified flag set + if(getParcelFlag(PF_DENY_IDENTIFIED) && is_agent_identified && !is_agent_transacted) + { + return BA_NO_ACCESS_LEVEL; + } + // Transacted - Payment Info Used + if(getParcelFlag(PF_DENY_TRANSACTED) && is_agent_transacted) + { + return BA_NO_ACCESS_LEVEL; + } + return BA_ALLOWED; + } + + return BA_NOT_IN_GROUP; + +} + + +void LLParcel::setArea(S32 area, S32 sim_object_limit) +{ + mArea = area; + setMaxPrimCapacity((S32)(sim_object_limit * area / (F32)(REGION_WIDTH_METERS * REGION_WIDTH_METERS))); +} + +void LLParcel::setDiscountRate(F32 rate) +{ + // this is to make sure that the rate is at least sane - this is + // not intended to enforce economy rules. It only enfoces that the + // rate is a scaler between 0 and 1. + mDiscountRate = llclampf(rate); +} + + +//----------------------------------------------------------- +// File input and output +//----------------------------------------------------------- + + +// WARNING: Area will be wrong until you calculate it. +BOOL LLParcel::importStream(std::istream& input_stream) +{ + U32 setting; + S32 secs_until_revert = 0; + + skip_to_end_of_next_keyword("{", input_stream); + if (!input_stream.good()) + { + llwarns << "LLParcel::importStream() - bad input_stream" << llendl; + return FALSE; + } + + while (input_stream.good()) + { + skip_comments_and_emptyspace(input_stream); + LLString line, keyword, value; + get_line(line, input_stream, MAX_STRING); + get_keyword_and_value(keyword, value, line); + + if ("}" == keyword) + { + break; + } + else if ("parcel_id" == keyword) + { + mID.set(value.c_str()); + } + else if ("status" == keyword) + { + mStatus = ownership_string_to_status(value.c_str()); + } + else if ("category" == keyword) + { + mCategory = category_string_to_category(value.c_str()); + } + else if ("local_id" == keyword) + { + LLString::convertToS32(value, mLocalID); + } + else if ("name" == keyword) + { + setName( value.c_str() ); + } + else if ("desc" == keyword) + { + setDesc( value.c_str() ); + } + else if ("music_url" == keyword) + { + setMusicURL( value.c_str() ); + } + else if ("media_url" == keyword) + { + setMediaURL( value.c_str() ); + } + else if ("media_id" == keyword) + { + mMediaID.set( value.c_str() ); + } + else if ("media_auto_scale" == keyword) + { + LLString::convertToU8(value, mMediaAutoScale); + } + else if ("owner_id" == keyword) + { + mOwnerID.set( value.c_str() ); + } + else if ("group_owned" == keyword) + { + LLString::convertToBOOL(value, mGroupOwned); + } + else if ("clean_other_time" == keyword) + { + S32 time; + LLString::convertToS32(value, time); + setCleanOtherTime(time); + } + else if ("auth_buyer_id" == keyword) + { + mAuthBuyerID.set(value.c_str()); + } + else if ("snapshot_id" == keyword) + { + mSnapshotID.set(value.c_str()); + } + else if ("user_location" == keyword) + { + sscanf(value.c_str(), "%f %f %f", + &mUserLocation.mV[VX], + &mUserLocation.mV[VY], + &mUserLocation.mV[VZ]); + } + else if ("user_look_at" == keyword) + { + sscanf(value.c_str(), "%f %f %f", + &mUserLookAt.mV[VX], + &mUserLookAt.mV[VY], + &mUserLookAt.mV[VZ]); + } + else if ("landing_type" == keyword) + { + S32 landing_type = 0; + LLString::convertToS32(value, landing_type); + mLandingType = (ELandingType) landing_type; + } + else if ("join_neighbors" == keyword) + { + llinfos << "found deprecated keyword join_neighbors" << llendl; + } + else if ("revert_sale" == keyword) + { + LLString::convertToS32(value, secs_until_revert); + if (secs_until_revert > 0) + { + mSaleTimerExpires.start(); + mSaleTimerExpires.setTimerExpirySec((F32)secs_until_revert); + } + } + else if("extended_grace" == keyword) + { + LLString::convertToS32(value, mGraceExtension); + } + else if ("user_list_type" == keyword) + { + // deprecated + } + else if("auction_id" == keyword) + { + LLString::convertToU32(value, mAuctionID); + } + else if("reserved_newbie" == keyword) + { + LLString::convertToBOOL(value, mIsReservedForNewbie); + } + else if ("allow_modify" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_CREATE_OBJECTS, setting); + } + else if ("allow_group_modify" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_CREATE_GROUP_OBJECTS, setting); + } + else if ("allow_all_object_entry" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_ALLOW_ALL_OBJECT_ENTRY, setting); + } + else if ("allow_group_object_entry" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_ALLOW_GROUP_OBJECT_ENTRY, setting); + } + else if ("allow_deed_to_group" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_ALLOW_DEED_TO_GROUP, setting); + } + else if("contribute_with_deed" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_CONTRIBUTE_WITH_DEED, setting); + } + else if ("allow_terraform" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_ALLOW_TERRAFORM, setting); + } + else if ("allow_damage" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_ALLOW_DAMAGE, setting); + } + else if ("allow_fly" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_ALLOW_FLY, setting); + } + else if ("allow_landmark" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_ALLOW_LANDMARK, setting); + } + else if ("sound_local" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_SOUND_LOCAL, setting); + } + else if ("allow_group_scripts" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_ALLOW_GROUP_SCRIPTS, setting); + } + else if ("allow_scripts" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_ALLOW_OTHER_SCRIPTS, setting); + } + else if ("for_sale" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_FOR_SALE, setting); + } + else if ("sell_w_objects" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_SELL_PARCEL_OBJECTS, setting); + } + else if ("use_pass_list" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_USE_PASS_LIST, setting); + } + else if ("show_directory" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_SHOW_DIRECTORY, setting); + } + else if ("allow_publish" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_ALLOW_PUBLISH, setting); + } + else if ("mature_publish" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_MATURE_PUBLISH, setting); + } + else if ("claim_date" == keyword) + { + // BUG: This will fail when time rolls over in 2038. + S32 time; + LLString::convertToS32(value, time); + mClaimDate = time; + } + else if ("claim_price" == keyword) + { + LLString::convertToS32(value, mClaimPricePerMeter); + } + else if ("rent_price" == keyword) + { + LLString::convertToS32(value, mRentPricePerMeter); + } + else if ("discount_rate" == keyword) + { + LLString::convertToF32(value, mDiscountRate); + } + else if ("draw_distance" == keyword) + { + LLString::convertToF32(value, mDrawDistance); + } + else if ("sale_price" == keyword) + { + LLString::convertToS32(value, mSalePrice); + } + else if ("pass_price" == keyword) + { + LLString::convertToS32(value, mPassPrice); + } + else if ("pass_hours" == keyword) + { + LLString::convertToF32(value, mPassHours); + } + else if ("box" == keyword) + { + // deprecated + } + else if ("aabb_min" == keyword) + { + sscanf(value.c_str(), "%f %f %f", + &mAABBMin.mV[VX], &mAABBMin.mV[VY], &mAABBMin.mV[VZ]); + } + else if ("use_access_group" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_USE_ACCESS_GROUP, setting); + } + else if ("use_access_list" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_USE_ACCESS_LIST, setting); + } + else if ("use_ban_list" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_USE_BAN_LIST, setting); + } + else if ("group_name" == keyword) + { + llinfos << "found deprecated keyword group_name" << llendl; + } + else if ("group_id" == keyword) + { + mGroupID.set( value.c_str() ); + } + // TODO: DEPRECATED FLAG + // Flag removed from simstate files in 1.11.1 + // Remove at some point where we have guarenteed this flag + // no longer exists anywhere in simstate files. + else if ("require_identified" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_DENY_ANONYMOUS, setting); + } + // TODO: DEPRECATED FLAG + // Flag removed from simstate files in 1.11.1 + // Remove at some point where we have guarenteed this flag + // no longer exists anywhere in simstate files. + else if ("require_transacted" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_DENY_ANONYMOUS, setting); + setParcelFlag(PF_DENY_IDENTIFIED, setting); + } + else if ("restrict_pushobject" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_RESTRICT_PUSHOBJECT, setting); + } + else if ("deny_anonymous" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_DENY_ANONYMOUS, setting); + } + else if ("deny_identified" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_DENY_IDENTIFIED, setting); + } + else if ("deny_transacted" == keyword) + { + LLString::convertToU32(value, setting); + setParcelFlag(PF_DENY_TRANSACTED, setting); + } + else if ("access_list" == keyword) + { + S32 entry_count = 0; + LLString::convertToS32(value, entry_count); + for (S32 i = 0; i < entry_count; i++) + { + LLAccessEntry entry; + if (importAccessEntry(input_stream, &entry)) + { + mAccessList[entry.mID] = entry; + } + } + } + else if ("ban_list" == keyword) + { + S32 entry_count = 0; + LLString::convertToS32(value, entry_count); + for (S32 i = 0; i < entry_count; i++) + { + LLAccessEntry entry; + if (importAccessEntry(input_stream, &entry)) + { + mBanList[entry.mID] = entry; + } + } + } + else if ("renter_list" == keyword) + { + /* + S32 entry_count = 0; + LLString::convertToS32(value, entry_count); + for (S32 i = 0; i < entry_count; i++) + { + LLAccessEntry entry; + if (importAccessEntry(input_stream, &entry)) + { + mRenterList.put(entry); + } + }*/ + } + else if ("pass_list" == keyword) + { + // legacy - put into access list + S32 entry_count = 0; + LLString::convertToS32(value, entry_count); + for (S32 i = 0; i < entry_count; i++) + { + LLAccessEntry entry; + if (importAccessEntry(input_stream, &entry)) + { + mAccessList[entry.mID] = entry; + } + } + } + + else + { + llwarns << "Unknown keyword in parcel section: <" + << keyword << ">" << llendl; + } + } + + // this code block detects if we have loaded a 1.1 simstate file, + // and follows the conversion rules specified in + // design_docs/land/pay_for_parcel.txt. + F32 time_to_expire = 0.0f; + if(mID.isNull()) + { + mID.generate(); + mStatus = OS_LEASE_PENDING; + //mBuyerID = mOwnerID; + if(getIsGroupOwned()) + { + time_to_expire += GROUP_USEC_CONVERSION_TIMEOUT / SEC_TO_MICROSEC; + } + else + { + time_to_expire += DEFAULT_USEC_CONVERSION_TIMEOUT / SEC_TO_MICROSEC; + } + //mExpireAction = STEA_PUBLIC; + mRecordTransaction = TRUE; + } + + // this code block deals with giving an extension to pending + // parcels to the midday of 2004-01-19 if they were originally set + // for some time on 2004-01-12. + if((0 == mGraceExtension) + && (EXTEND_GRACE_IF_MORE_THAN_SEC < secs_until_revert)) + { + const S32 NEW_CONVERSION_DATE = 1074538800; // 2004-01-19T11:00:00 + time_t now = time(NULL); // now in epoch + secs_until_revert = (S32)(NEW_CONVERSION_DATE - now); + time_to_expire = (F32)secs_until_revert; + mGraceExtension = 1; + } + + // This code block adds yet another week to the deadline. :( + if(1 == mGraceExtension) + { + time_to_expire += SEVEN_DAYS_IN_USEC / SEC_TO_MICROSEC; + mGraceExtension = 2; + } + + if (time_to_expire > 0) + { + mSaleTimerExpires.setTimerExpirySec(time_to_expire); + mSaleTimerExpires.start(); + } + + // successful import + return TRUE; +} + + +BOOL LLParcel::importAccessEntry(std::istream& input_stream, LLAccessEntry* entry) +{ + skip_to_end_of_next_keyword("{", input_stream); + while (input_stream.good()) + { + skip_comments_and_emptyspace(input_stream); + LLString line, keyword, value; + get_line(line, input_stream, MAX_STRING); + get_keyword_and_value(keyword, value, line); + + if ("}" == keyword) + { + break; + } + else if ("id" == keyword) + { + entry->mID.set( value.c_str() ); + } + else if ("name" == keyword) + { + // deprecated + } + else if ("time" == keyword) + { + S32 when; + LLString::convertToS32(value, when); + entry->mTime = when; + } + else if ("flags" == keyword) + { + U32 setting; + LLString::convertToU32(value, setting); + entry->mFlags = setting; + } + else + { + llwarns << "Unknown keyword in parcel access entry section: <" + << keyword << ">" << llendl; + } + } + return input_stream.good(); +} + +BOOL LLParcel::exportStream(std::ostream& output_stream) +{ + S32 setting; + char id_string[MAX_STRING]; + + std::ios::fmtflags old_flags = output_stream.flags(); + output_stream.setf(std::ios::showpoint); + output_stream << "\t{\n"; + + mID.toString(id_string); + output_stream << "\t\t parcel_id " << id_string << "\n"; + output_stream << "\t\t status " << ownership_status_to_string(mStatus) << "\n"; + output_stream << "\t\t category " << category_to_string(mCategory) << "\n"; + + output_stream << "\t\t local_id " << mLocalID << "\n"; + + const char* name = (mName.empty() ? "" : mName.c_str() ); + output_stream << "\t\t name " << name << "\n"; + + const char* desc = (mDesc.empty() ? "" : mDesc.c_str() ); + output_stream << "\t\t desc " << desc << "\n"; + + const char* music_url = (mMusicURL.empty() ? "" : mMusicURL.c_str() ); + output_stream << "\t\t music_url " << music_url << "\n"; + + const char* media_url = (mMediaURL.empty() ? "" : mMediaURL.c_str() ); + output_stream << "\t\t media_url " << media_url << "\n"; + + output_stream << "\t\t media_auto_scale " << (mMediaAutoScale ? 1 : 0) << "\n"; + + mMediaID.toString(id_string); + output_stream << "\t\t media_id " << id_string << "\n"; + + mOwnerID.toString(id_string); + output_stream << "\t\t owner_id " << id_string << "\n"; + output_stream << "\t\t group_owned " << (mGroupOwned ? 1 : 0) << "\n"; + output_stream << "\t\t clean_other_time " << getCleanOtherTime() << "\n"; + + if(!mAuthBuyerID.isNull()) + { + mAuthBuyerID.toString(id_string); + output_stream << "\t\t auth_buyer_id " << id_string << "\n"; + } + if (!mSnapshotID.isNull()) + { + mSnapshotID.toString(id_string); + output_stream << "\t\t snapshot_id " << id_string << "\n"; + } + if (!mUserLocation.isExactlyZero()) + { + output_stream << "\t\t user_location " + << (F64)mUserLocation.mV[VX] + << " " << (F64)mUserLocation.mV[VY] + << " " << (F64)mUserLocation.mV[VZ] << "\n"; + output_stream << "\t\t user_look_at " + << (F64)mUserLookAt.mV[VX] + << " " << (F64)mUserLookAt.mV[VY] + << " " << (F64)mUserLookAt.mV[VZ] << "\n"; + } + output_stream << "\t\t landing_type " << mLandingType << "\n"; + //if(mJoinNeighbors) + //{ + // output_stream << "\t\t join_neighbors " << mJoinNeighbors << "\n"; + //} + if(mSaleTimerExpires.getStarted()) + { + S32 dt_sec = (S32) mSaleTimerExpires.getRemainingTimeF32()+60; // Add a minute to prevent race conditions + output_stream << "\t\t revert_sale " << dt_sec << "\n"; + //output_stream << "\t\t revert_action " << revert_action_to_string(mExpireAction) << "\n"; + output_stream << "\t\t extended_grace " << mGraceExtension << "\n"; + } + + if(0 != mAuctionID) + { + output_stream << "\t\t auction_id " << mAuctionID << "\n"; + } + if(mIsReservedForNewbie) + { + output_stream << "\t\t reserved_newbie " << mIsReservedForNewbie << "\n"; + } + + output_stream << "\t\t allow_modify " << getAllowModify() << "\n"; + output_stream << "\t\t allow_group_modify " << getAllowGroupModify() << "\n"; + output_stream << "\t\t allow_all_object_entry " << getAllowAllObjectEntry() << "\n"; + output_stream << "\t\t allow_group_object_entry " << getAllowGroupObjectEntry() << "\n"; + output_stream << "\t\t allow_terraform " << getAllowTerraform() << "\n"; + output_stream << "\t\t allow_deed_to_group " << getAllowDeedToGroup() << "\n"; + output_stream << "\t\t contribute_with_deed " << getContributeWithDeed() << "\n"; + output_stream << "\t\t allow_damage " << getAllowDamage() << "\n"; + output_stream << "\t\t claim_date " << (S32)mClaimDate << "\n"; + output_stream << "\t\t claim_price " << mClaimPricePerMeter << "\n"; + output_stream << "\t\t rent_price " << mRentPricePerMeter << "\n"; + output_stream << "\t\t discount_rate " << mDiscountRate << "\n"; + output_stream << "\t\t allow_fly " << (getAllowFly() ? 1 : 0) << "\n"; + output_stream << "\t\t allow_landmark " << (getAllowLandmark() ? 1 : 0) << "\n"; + output_stream << "\t\t sound_local " << (getSoundLocal() ? 1 : 0) << "\n"; + output_stream << "\t\t allow_scripts " << (getAllowOtherScripts() ? 1 : 0) << "\n"; + output_stream << "\t\t allow_group_scripts " << (getAllowGroupScripts() ? 1 : 0) << "\n"; + output_stream << "\t\t for_sale " << (getForSale() ? 1 : 0) << "\n"; + output_stream << "\t\t sell_w_objects " << (getSellWithObjects() ? 1 : 0) << "\n"; + output_stream << "\t\t draw_distance " << mDrawDistance << "\n"; + output_stream << "\t\t sale_price " << mSalePrice << "\n"; + + setting = (getParcelFlag(PF_USE_ACCESS_GROUP) ? 1 : 0); + output_stream << "\t\t use_access_group " << setting << "\n"; + + setting = (getParcelFlag(PF_USE_ACCESS_LIST) ? 1 : 0); + output_stream << "\t\t use_access_list " << setting << "\n"; + + setting = (getParcelFlag(PF_USE_BAN_LIST) ? 1 : 0); + output_stream << "\t\t use_ban_list " << setting << "\n"; + + mGroupID.toString(id_string); + output_stream << "\t\t group_id " << id_string << "\n"; + + //const char* group_name + // = (mGroupName.isEmpty() ? "" : mGroupName.c_str() ); + //output_stream << "\t\t group_name " << group_name << "\n"; + + setting = (getParcelFlag(PF_USE_PASS_LIST) ? 1 : 0); + output_stream << "\t\t use_pass_list " << setting << "\n"; + + output_stream << "\t\t pass_price " << mPassPrice << "\n"; + output_stream << "\t\t pass_hours " << mPassHours << "\n"; + + setting = (getParcelFlag(PF_SHOW_DIRECTORY) ? 1 : 0); + output_stream << "\t\t show_directory " << setting << "\n"; + + setting = (getParcelFlag(PF_ALLOW_PUBLISH) ? 1 : 0); + output_stream << "\t\t allow_publish " << setting << "\n"; + + setting = (getParcelFlag(PF_MATURE_PUBLISH) ? 1 : 0); + output_stream << "\t\t mature_publish " << setting << "\n"; + + setting = (getParcelFlag(PF_DENY_ANONYMOUS) ? 1 : 0); + output_stream << "\t\t deny_anonymous " << setting << "\n"; + + setting = (getParcelFlag(PF_DENY_IDENTIFIED) ? 1 : 0); + output_stream << "\t\t deny_identified " << setting << "\n"; + + setting = (getParcelFlag(PF_DENY_TRANSACTED) ? 1 : 0); + output_stream << "\t\t deny_transacted " << setting << "\n"; + + setting = (getParcelFlag(PF_RESTRICT_PUSHOBJECT) ? 1 : 0); + output_stream << "\t\t restrict_pushobject " << setting << "\n"; + + output_stream << "\t\t aabb_min " + << mAABBMin.mV[VX] + << " " << mAABBMin.mV[VY] + << " " << mAABBMin.mV[VZ] << "\n"; + + if (!mAccessList.empty()) + { + output_stream << "\t\t access_list " << mAccessList.size() << "\n"; + access_map_const_iterator cit = mAccessList.begin(); + access_map_const_iterator end = mAccessList.end(); + + for ( ; cit != end; ++cit) + { + output_stream << "\t\t{\n"; + const LLAccessEntry& entry = (*cit).second; + entry.mID.toString(id_string); + output_stream << "\t\t\tid " << id_string << "\n"; + output_stream << "\t\t\ttime " << entry.mTime << "\n"; + output_stream << "\t\t\tflags " << entry.mFlags << "\n"; + output_stream << "\t\t}\n"; + } + } + + if (!mBanList.empty()) + { + output_stream << "\t\t ban_list " << mBanList.size() << "\n"; + access_map_const_iterator cit = mBanList.begin(); + access_map_const_iterator end = mBanList.end(); + + for ( ; cit != end; ++cit) + { + output_stream << "\t\t{\n"; + const LLAccessEntry& entry = (*cit).second; + entry.mID.toString(id_string); + output_stream << "\t\t\tid " << id_string << "\n"; + output_stream << "\t\t\ttime " << entry.mTime << "\n"; + output_stream << "\t\t\tflags " << entry.mFlags << "\n"; + output_stream << "\t\t}\n"; + } + } + + /*if (mRenterList.count() > 0) + { + output_stream << "\t\t renter_list " << mRenterList.count() << "\n"; + for (i = 0; i < mRenterList.count(); i++) + { + output_stream << "\t\t{\n"; + const LLAccessEntry& entry = mRenterList.get(i); + entry.mID.toString(id_string); + output_stream << "\t\t\tid " << id_string << "\n"; + output_stream << "\t\t\ttime " << entry.mTime << "\n"; + output_stream << "\t\t\tflags " << entry.mFlags << "\n"; + output_stream << "\t\t}\n"; + } + }*/ + + output_stream << "\t}\n"; + output_stream.flags(old_flags); + + return TRUE; +} + + +// Assumes we are in a block "ParcelData" +void LLParcel::packMessage(LLMessageSystem* msg) +{ + msg->addU32Fast( _PREHASH_ParcelFlags, getParcelFlags() ); + msg->addS32Fast( _PREHASH_SalePrice, getSalePrice() ); + msg->addStringFast( _PREHASH_Name, getName() ); + msg->addStringFast( _PREHASH_Desc, getDesc() ); + msg->addStringFast( _PREHASH_MusicURL, getMusicURL() ); + msg->addStringFast( _PREHASH_MediaURL, getMediaURL() ); + msg->addU8 ( "MediaAutoScale", getMediaAutoScale () ); + msg->addUUIDFast( _PREHASH_MediaID, getMediaID() ); + msg->addUUIDFast( _PREHASH_GroupID, getGroupID() ); + msg->addS32Fast( _PREHASH_PassPrice, mPassPrice ); + msg->addF32Fast( _PREHASH_PassHours, mPassHours ); + msg->addU8Fast( _PREHASH_Category, (U8)mCategory); + msg->addUUIDFast( _PREHASH_AuthBuyerID, mAuthBuyerID); + msg->addUUIDFast( _PREHASH_SnapshotID, mSnapshotID); + msg->addVector3Fast(_PREHASH_UserLocation, mUserLocation); + msg->addVector3Fast(_PREHASH_UserLookAt, mUserLookAt); + msg->addU8Fast( _PREHASH_LandingType, (U8)mLandingType); +} + + +void LLParcel::unpackMessage(LLMessageSystem* msg) +{ + char buffer[256]; + + msg->getU32Fast( _PREHASH_ParcelData,_PREHASH_ParcelFlags, mParcelFlags ); + msg->getS32Fast( _PREHASH_ParcelData,_PREHASH_SalePrice, mSalePrice ); + msg->getStringFast( _PREHASH_ParcelData,_PREHASH_Name, 256, buffer ); + setName(buffer); + msg->getStringFast( _PREHASH_ParcelData,_PREHASH_Desc, 256, buffer ); + setDesc(buffer); + msg->getStringFast( _PREHASH_ParcelData,_PREHASH_MusicURL, 256, buffer ); + setMusicURL(buffer); + msg->getStringFast( _PREHASH_ParcelData,_PREHASH_MediaURL, 256, buffer ); + setMediaURL(buffer); + + // non-optimized version + msg->getU8 ( "ParcelData", "MediaAutoScale", mMediaAutoScale ); + + msg->getUUIDFast( _PREHASH_ParcelData,_PREHASH_MediaID, mMediaID ); + msg->getUUIDFast( _PREHASH_ParcelData,_PREHASH_GroupID, mGroupID ); + msg->getS32Fast( _PREHASH_ParcelData,_PREHASH_PassPrice, mPassPrice ); + msg->getF32Fast( _PREHASH_ParcelData,_PREHASH_PassHours, mPassHours ); + U8 category; + msg->getU8Fast( _PREHASH_ParcelData,_PREHASH_Category, category); + mCategory = (ECategory)category; + msg->getUUIDFast( _PREHASH_ParcelData,_PREHASH_AuthBuyerID, mAuthBuyerID); + msg->getUUIDFast( _PREHASH_ParcelData,_PREHASH_SnapshotID, mSnapshotID); + msg->getVector3Fast(_PREHASH_ParcelData,_PREHASH_UserLocation, mUserLocation); + msg->getVector3Fast(_PREHASH_ParcelData,_PREHASH_UserLookAt, mUserLookAt); + U8 landing_type; + msg->getU8Fast( _PREHASH_ParcelData,_PREHASH_LandingType, landing_type); + mLandingType = (ELandingType)landing_type; +} + + +void LLParcel::packAccessEntries(LLMessageSystem* msg, + const std::map<LLUUID,LLAccessEntry>& list) +{ + access_map_const_iterator cit = list.begin(); + access_map_const_iterator end = list.end(); + + if (cit == end) + { + msg->nextBlockFast(_PREHASH_List); + msg->addUUIDFast(_PREHASH_ID, LLUUID::null ); + msg->addS32Fast(_PREHASH_Time, 0 ); + msg->addU32Fast(_PREHASH_Flags, 0 ); + return; + } + + for ( ; cit != end; ++cit) + { + const LLAccessEntry& entry = (*cit).second; + + msg->nextBlockFast(_PREHASH_List); + msg->addUUIDFast(_PREHASH_ID, entry.mID ); + msg->addS32Fast(_PREHASH_Time, entry.mTime ); + msg->addU32Fast(_PREHASH_Flags, entry.mFlags ); + } +} + + +void LLParcel::unpackAccessEntries(LLMessageSystem* msg, + std::map<LLUUID,LLAccessEntry>* list) +{ + LLUUID id; + S32 time; + U32 flags; + + S32 i; + S32 count = msg->getNumberOfBlocksFast(_PREHASH_List); + for (i = 0; i < count; i++) + { + msg->getUUIDFast(_PREHASH_List, _PREHASH_ID, id, i); + msg->getS32Fast( _PREHASH_List, _PREHASH_Time, time, i); + msg->getU32Fast( _PREHASH_List, _PREHASH_Flags, flags, i); + + if (id.notNull()) + { + LLAccessEntry entry; + entry.mID = id; + entry.mTime = time; + entry.mFlags = flags; + + (*list)[entry.mID] = entry; + } + } +} + + +void LLParcel::expirePasses(S32 now) +{ + access_map_iterator itor = mAccessList.begin(); + while (itor != mAccessList.end()) + { + const LLAccessEntry& entry = (*itor).second; + + if (entry.mTime != 0 && entry.mTime < now) + { + mAccessList.erase(itor++); + } + else + { + ++itor; + } + } +} + + +bool LLParcel::operator==(const LLParcel &rhs) const +{ + if (mOwnerID != rhs.mOwnerID) + return FALSE; + + if (mParcelFlags != rhs.mParcelFlags) + return FALSE; + + if (mClaimDate != rhs.mClaimDate) + return FALSE; + + if (mClaimPricePerMeter != rhs.mClaimPricePerMeter) + return FALSE; + + if (mRentPricePerMeter != rhs.mRentPricePerMeter) + return FALSE; + + return TRUE; +} + +// Calculate rent +S32 LLParcel::getTotalRent() const +{ + return (S32)floor(0.5f + (F32)mArea * (F32)mRentPricePerMeter * (1.0f - mDiscountRate)); +} + +F32 LLParcel::getAdjustedRentPerMeter() const +{ + return ((F32)mRentPricePerMeter * (1.0f - mDiscountRate)); +} + +LLVector3 LLParcel::getCenterpoint() const +{ + LLVector3 rv; + rv.mV[VX] = (getAABBMin().mV[VX] + getAABBMax().mV[VX]) * 0.5f; + rv.mV[VY] = (getAABBMin().mV[VY] + getAABBMax().mV[VY]) * 0.5f; + rv.mV[VZ] = 0.0f; + return rv; +} + +void LLParcel::extendAABB(const LLVector3& box_min, const LLVector3& box_max) +{ + // Patch up min corner of AABB + S32 i; + for (i=0; i<3; i++) + { + if (box_min.mV[i] < mAABBMin.mV[i]) + { + mAABBMin.mV[i] = box_min.mV[i]; + } + } + + // Patch up max corner of AABB + for (i=0; i<3; i++) + { + if (box_max.mV[i] > mAABBMax.mV[i]) + { + mAABBMax.mV[i] = box_max.mV[i]; + } + } +} + +BOOL LLParcel::addToAccessList(const LLUUID& agent_id, S32 time) +{ + if (!((mParcelFlags & PF_USE_ACCESS_LIST) || (mParcelFlags & PF_USE_PASS_LIST)) + || mAccessList.size() >= (U32) PARCEL_MAX_ACCESS_LIST) + { + // Not using access list, so not a rational thing to do + return FALSE; + } + if (agent_id == getOwnerID()) + { + // Can't add owner to these lists + return FALSE; + } + access_map_iterator itor = mAccessList.begin(); + while (itor != mAccessList.end()) + { + const LLAccessEntry& entry = (*itor).second; + if (entry.mID == agent_id) + { + if (time == 0 || (entry.mTime != 0 && entry.mTime < time)) + { + mAccessList.erase(itor++); + } + else + { + // existing one expires later + return FALSE; + } + } + else + { + ++itor; + } + } + + removeFromBanList(agent_id); + + LLAccessEntry new_entry; + new_entry.mID = agent_id; + new_entry.mTime = time; + new_entry.mFlags = 0x0; + mAccessList[new_entry.mID] = new_entry; + return TRUE; +} + +BOOL LLParcel::addToBanList(const LLUUID& agent_id, S32 time) +{ + if (!(mParcelFlags & PF_USE_BAN_LIST) + || mBanList.size() >= (U32) PARCEL_MAX_ACCESS_LIST) + { + // Not using ban list, so not a rational thing to do + return FALSE; + } + if (agent_id == getOwnerID()) + { + // Can't add owner to these lists + return FALSE; + } + + access_map_iterator itor = mBanList.begin(); + while (itor != mBanList.end()) + { + const LLAccessEntry& entry = (*itor).second; + if (entry.mID == agent_id) + { + if (time == 0 || (entry.mTime != 0 && entry.mTime < time)) + { + mBanList.erase(itor++); + } + else + { + // existing one expires later + return FALSE; + } + } + else + { + ++itor; + } + } + + removeFromAccessList(agent_id); + + LLAccessEntry new_entry; + new_entry.mID = agent_id; + new_entry.mTime = time; + new_entry.mFlags = 0x0; + mBanList[new_entry.mID] = new_entry; + return TRUE; +} + +BOOL remove_from_access_array(std::map<LLUUID,LLAccessEntry>* list, + const LLUUID& agent_id) +{ + BOOL removed = FALSE; + access_map_iterator itor = list->begin(); + while (itor != list->end()) + { + const LLAccessEntry& entry = (*itor).second; + if (entry.mID == agent_id) + { + list->erase(itor++); + removed = TRUE; + } + else + { + ++itor; + } + } + return removed; +} + +BOOL LLParcel::removeFromAccessList(const LLUUID& agent_id) +{ + return remove_from_access_array(&mAccessList, agent_id); +} + +BOOL LLParcel::removeFromBanList(const LLUUID& agent_id) +{ + return remove_from_access_array(&mBanList, agent_id); +} + +// static +const char* LLParcel::getOwnershipStatusString(EOwnershipStatus status) +{ + return ownership_status_to_string(status); +} + +// static +const char* LLParcel::getCategoryString(ECategory category) +{ + return category_to_string(category); +} + +// static +const char* LLParcel::getCategoryUIString(ECategory category) +{ + return category_to_ui_string(category); +} + +// static +LLParcel::ECategory LLParcel::getCategoryFromString(const char* string) +{ + return category_string_to_category(string); +} + +// static +LLParcel::ECategory LLParcel::getCategoryFromUIString(const char* string) +{ + return category_ui_string_to_category(string); +} + +// static +const char* LLParcel::getActionString(LLParcel::EAction action) +{ + S32 index = 0; + if((action >= 0) && (action < LLParcel::A_COUNT)) + { + index = action; + } + else + { + index = A_COUNT; + } + return PARCEL_ACTION_STRING[index]; +} + +BOOL LLParcel::isSaleTimerExpired(const U64& time) +{ + if (mSaleTimerExpires.getStarted() == FALSE) + { + return FALSE; + } + BOOL expired = mSaleTimerExpires.checkExpirationAndReset(0.0); + if (expired) + { + mSaleTimerExpires.stop(); + } + return expired; +} + + +void LLParcel::startSale(const LLUUID& buyer_id, BOOL is_buyer_group) +{ + // TODO -- this and all Sale related methods need to move out of the LLParcel + // base class and into server-side-only LLSimParcel class + setPreviousOwnerID(mOwnerID); + setPreviouslyGroupOwned(mGroupOwned); + + mOwnerID = buyer_id; + mGroupOwned = is_buyer_group; + if(mGroupOwned) + { + mGroupID = mOwnerID; + } + else + { + mGroupID.setNull(); + } + mSaleTimerExpires.start(); + mSaleTimerExpires.setTimerExpirySec(DEFAULT_USEC_SALE_TIMEOUT / SEC_TO_MICROSEC); + mStatus = OS_LEASE_PENDING; + mClaimDate = time(NULL); + mAuctionID = 0; + // clear the autoreturn whenever land changes hands + setCleanOtherTime(0); +} + +void LLParcel::expireSale(U32& type, U8& flags, LLUUID& from_id, LLUUID& to_id) +{ + mSaleTimerExpires.setTimerExpirySec(0.0); + mSaleTimerExpires.stop(); + setPreviousOwnerID(LLUUID::null); + setPreviouslyGroupOwned(FALSE); + setSellWithObjects(FALSE); + type = TRANS_LAND_RELEASE; + mStatus = OS_NONE; + mIsReservedForNewbie = FALSE; + flags = pack_transaction_flags(mGroupOwned, FALSE); + mAuthBuyerID.setNull(); + from_id = mOwnerID; + mOwnerID.setNull(); + to_id.setNull(); +} + +void LLParcel::completeSale(U32& type, U8& flags, + LLUUID& to_id) +{ + mSaleTimerExpires.setTimerExpirySec(0.0); + mSaleTimerExpires.stop(); + mStatus = OS_LEASED; + type = TRANS_LAND_SALE; + flags = pack_transaction_flags(mGroupOwned, mGroupOwned); + to_id = mOwnerID; + mAuthBuyerID.setNull(); + mIsReservedForNewbie = FALSE; + + // Purchased parcels are assumed to no longer be for sale. + // Otherwise someone can snipe the sale. + setForSale(FALSE); + + // Turn off show directory, since it's a recurring fee that + // the buyer may not want. + setParcelFlag(PF_SHOW_DIRECTORY, FALSE); + + //should be cleared on sale. + mAccessList.clear(); + mBanList.clear(); + +} + +void LLParcel::clearSale() +{ + mSaleTimerExpires.setTimerExpirySec(0.0); + mSaleTimerExpires.stop(); + if(isPublic()) + { + mStatus = OS_NONE; + } + else + { + mStatus = OS_LEASED; + } + mAuthBuyerID.setNull(); + setForSale(FALSE); + setPreviousOwnerID(LLUUID::null); + setPreviouslyGroupOwned(FALSE); + setSellWithObjects(FALSE); + mIsReservedForNewbie = FALSE; +} + +BOOL LLParcel::isPublic() const +{ + return (mOwnerID.isNull()); +} + +BOOL LLParcel::isBuyerAuthorized(const LLUUID& buyer_id) const +{ + if(mAuthBuyerID.isNull()) + { + return TRUE; + } + return (mAuthBuyerID == buyer_id); +} + +void LLParcel::clearParcel() +{ + overrideParcelFlags(PF_DEFAULT); + setName(NULL); + setDesc(NULL); + setMusicURL(NULL); + setMediaURL(NULL); + setMediaID(LLUUID::null); + setMediaAutoScale(0); + setInEscrow(FALSE); + setAuthorizedBuyerID(LLUUID::null); + setCategory(C_NONE); + setSnapshotID(LLUUID::null); + setUserLocation(LLVector3::zero); + setUserLookAt(LLVector3::x_axis); + setLandingType(L_LANDING_POINT); + setAuctionID(0); + setReservedForNewbie(FALSE); + setGroupID(LLUUID::null); + setPassPrice(0); + setPassHours(0.f); + mAccessList.clear(); + mBanList.clear(); + //mRenterList.reset(); +} + +void LLParcel::dump() +{ + llinfos << "parcel " << mLocalID << " area " << mArea << llendl; + llinfos << " name <" << mName << ">" << llendl; + llinfos << " desc <" << mDesc << ">" << llendl; +} + +const char* ownership_status_to_string(LLParcel::EOwnershipStatus status) +{ + if(status >= 0 && status < LLParcel::OS_COUNT) + { + return PARCEL_OWNERSHIP_STATUS_STRING[status]; + } + return "none"; +} + +LLParcel::EOwnershipStatus ownership_string_to_status(const char* s) +{ + for(S32 i = 0; i < LLParcel::OS_COUNT; ++i) + { + if(0 == strcmp(s, PARCEL_OWNERSHIP_STATUS_STRING[i])) + { + return (LLParcel::EOwnershipStatus)i; + } + } + return LLParcel::OS_NONE; +} + +//const char* revert_action_to_string(LLParcel::ESaleTimerExpireAction action) +//{ +// S32 index = 0; +// if(action >= 0 && action < LLParcel::STEA_COUNT) +// { +// index = action; +// } +// return PARCEL_SALE_TIMER_ACTION[index]; +//} + +//LLParcel::ESaleTimerExpireAction revert_string_to_action(const char* s) +//{ +// for(S32 i = 0; i < LLParcel::STEA_COUNT; ++i) +// { +// if(0 == strcmp(s, PARCEL_SALE_TIMER_ACTION[i])) +// { +// return (LLParcel::ESaleTimerExpireAction)i; +// } +// } +// return LLParcel::STEA_REVERT; +//} + +const char* category_to_string(LLParcel::ECategory category) +{ + S32 index = 0; + if((category >= 0) && (category < LLParcel::C_COUNT)) + { + index = category; + } + return PARCEL_CATEGORY_STRING[index]; +} + +const char* category_to_ui_string(LLParcel::ECategory category) +{ + S32 index = 0; + if((category >= 0) && (category < LLParcel::C_COUNT)) + { + index = category; + } + else + { + // C_ANY = -1 , but the "Any" string is at the end of the list + index = ((S32) LLParcel::C_COUNT) + 1; + } + return PARCEL_CATEGORY_UI_STRING[index]; +} + +LLParcel::ECategory category_string_to_category(const char* s) +{ + for(S32 i = 0; i < LLParcel::C_COUNT; ++i) + { + if(0 == strcmp(s, PARCEL_CATEGORY_STRING[i])) + { + return (LLParcel::ECategory)i; + } + } + llwarns << "Parcel category outside of possibilities " << s << llendl; + return LLParcel::C_NONE; +} + +LLParcel::ECategory category_ui_string_to_category(const char* s) +{ + for(S32 i = 0; i < LLParcel::C_COUNT; ++i) + { + if(0 == strcmp(s, PARCEL_CATEGORY_UI_STRING[i])) + { + return (LLParcel::ECategory)i; + } + } + // "Any" is a valid category for searches, and + // is a distinct option from "None" and "Other" + return LLParcel::C_ANY; +} + diff --git a/indra/llinventory/llparcel.h b/indra/llinventory/llparcel.h new file mode 100644 index 0000000000..16d3cb01e5 --- /dev/null +++ b/indra/llinventory/llparcel.h @@ -0,0 +1,576 @@ +/** + * @file llparcel.h + * + * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LL_LLPARCEL_H +#define LL_LLPARCEL_H + +#include <time.h> +#include <iostream> + +#include "lldarray.h" +#include "lluuid.h" +#include "llparcelflags.h" +#include "llpermissions.h" +#include "v3math.h" + + +// Grid out of which parcels taken is stepped every 4 meters. +const F32 PARCEL_GRID_STEP_METERS = 4.f; + +// Area of one "square" of parcel +const S32 PARCEL_UNIT_AREA = 16; + +// Height _above_ground_ that parcel boundary ends +const F32 PARCEL_HEIGHT = 50.f; + +//Height above ground which parcel boundries exist for explicitly banned avatars +const F32 BAN_HEIGHT = 768.f; + +// Maximum number of entries in an access list +const S32 PARCEL_MAX_ACCESS_LIST = 300; +//Maximum number of entires in an update packet +//for access/ban lists. +const F32 PARCEL_MAX_ENTRIES_PER_PACKET = 48.f; + +// Weekly charge for listing a parcel in the directory +const S32 PARCEL_DIRECTORY_FEE = 30; + +const S32 PARCEL_PASS_PRICE_DEFAULT = 10; +const F32 PARCEL_PASS_HOURS_DEFAULT = 1.f; + +// Number of "chunks" in which parcel overlay data is sent +// Chunk 0 = southern rows, entire width +const S32 PARCEL_OVERLAY_CHUNKS = 4; + +// Bottom three bits are a color index for the land overlay +const U8 PARCEL_COLOR_MASK = 0x07; +const U8 PARCEL_PUBLIC = 0x00; +const U8 PARCEL_OWNED = 0x01; +const U8 PARCEL_GROUP = 0x02; +const U8 PARCEL_SELF = 0x03; +const U8 PARCEL_FOR_SALE = 0x04; +const U8 PARCEL_AUCTION = 0x05; +// unused 0x06 +// unused 0x07 +// flag, unused 0x08 +// flag, unused 0x10 +const U8 PARCEL_SOUND_LOCAL = 0x20; +const U8 PARCEL_WEST_LINE = 0x40; // flag, property line on west edge +const U8 PARCEL_SOUTH_LINE = 0x80; // flag, property line on south edge + +// Transmission results for parcel properties +const S32 PARCEL_RESULT_NO_DATA = -1; +const S32 PARCEL_RESULT_SUCCESS = 0; // got exactly one parcel +const S32 PARCEL_RESULT_MULTIPLE = 1; // got multiple parcels + +const S32 SELECTED_PARCEL_SEQ_ID = -10000; +const S32 COLLISION_NOT_IN_GROUP_PARCEL_SEQ_ID = -20000; +const S32 COLLISION_BANNED_PARCEL_SEQ_ID = -30000; +const S32 COLLISION_NOT_ON_LIST_PARCEL_SEQ_ID = -40000; +const S32 HOVERED_PARCEL_SEQ_ID = -50000; + +const U32 RT_NONE = 0x1 << 0; +const U32 RT_OWNER = 0x1 << 1; +const U32 RT_GROUP = 0x1 << 2; +const U32 RT_OTHER = 0x1 << 3; +const U32 RT_LIST = 0x1 << 4; +const U32 RT_SELL = 0x1 << 5; + +class LLMessageSystem; + +class LLAccessEntry +{ +public: + LLUUID mID; + S32 mTime; + U32 mFlags; +}; + +typedef std::map<LLUUID,LLAccessEntry>::iterator access_map_iterator; +typedef std::map<LLUUID,LLAccessEntry>::const_iterator access_map_const_iterator; + +class LLParcel +{ +public: + enum EOwnershipStatus + { + OS_LEASED = 0, + OS_LEASE_PENDING = 1, + OS_ABANDONED = 2, + OS_COUNT = 3, + OS_NONE = -1 + }; + enum ECategory + { + C_NONE = 0, + C_LINDEN, + C_ADULT, + C_ARTS, // "arts & culture" + C_BUSINESS, // was "store" + C_EDUCATIONAL, + C_GAMING, // was "game" + C_HANGOUT, // was "gathering place" + C_NEWCOMER, + C_PARK, // "parks & nature" + C_RESIDENTIAL, // was "homestead" + C_SHOPPING, + C_STAGE, + C_OTHER, + C_COUNT, + C_ANY = -1 // only useful in queries + }; + enum EAction + { + A_CREATE = 0, + A_RELEASE = 1, + A_ABSORB = 2, + A_ABSORBED = 3, + A_DIVIDE = 4, + A_DIVISION = 5, + A_ACQUIRE = 6, + A_RELINQUISH = 7, + A_CONFIRM = 8, + A_COUNT = 9, + A_UNKNOWN = -1 + }; + + enum ELandingType + { + L_NONE = 0, + L_LANDING_POINT = 1, + L_DIRECT = 2 + }; + + // CREATORS + LLParcel(); + LLParcel( const LLUUID &owner_id, + BOOL modify, BOOL terraform, BOOL damage, + time_t claim_date, S32 claim_price, S32 rent_price, S32 area, S32 sim_object_limit, F32 parcel_object_bonus, + BOOL is_group_owned = FALSE); + virtual ~LLParcel(); + + void init( const LLUUID &owner_id, + BOOL modify, BOOL terraform, BOOL damage, + time_t claim_date, S32 claim_price, S32 rent_price, + S32 area, S32 sim_object_limit, F32 parcel_object_bonus, BOOL is_group_owned = FALSE); + + // TODO: make an actual copy constructor for this + void overrideParcelFlags(U32 flags); + // if you specify an agent id here, the group id will be zeroed + void overrideOwner(const LLUUID& owner_id, BOOL is_group_owned = FALSE); + void overrideSaleTimerExpires(F32 secs_left) { mSaleTimerExpires.setTimerExpirySec(secs_left); } + + // MANIPULATORS + void generateNewID() { mID.generate(); } + void setName(const char* name); + void setDesc(const char* desc); + void setMusicURL(const char* url); + void setMediaURL(const char* url); + void setMediaID(const LLUUID& id) { mMediaID = id; } + void setMediaAutoScale ( U8 flagIn ) { mMediaAutoScale = flagIn; } + virtual void setLocalID(S32 local_id); + + // blow away all the extra crap lurking in parcels, including urls, access lists, etc + void clearParcel(); + + // This value is not persisted out to the parcel file, it is only + // a per-process blocker for attempts to purchase. + void setInEscrow(bool in_escrow) { mInEscrow = in_escrow; } + + void setAuthorizedBuyerID(const LLUUID& id) { mAuthBuyerID = id; } + //void overrideBuyerID(const LLUUID& id) { mBuyerID = id; } + void setCategory(ECategory category) { mCategory = category; } + void setSnapshotID(const LLUUID& id) { mSnapshotID = id; } + void setUserLocation(const LLVector3& pos) { mUserLocation = pos; } + void setUserLookAt(const LLVector3& rot) { mUserLookAt = rot; } + void setLandingType(const ELandingType type) { mLandingType = type; } + + void setAuctionID(U32 auction_id) { mAuctionID = auction_id;} + void setReservedForNewbie(BOOL reserve) { mIsReservedForNewbie = reserve; } + + void setAllParcelFlags(U32 flags) { mParcelFlags = flags; } + void setParcelFlag(U32 flag, BOOL b); + + void setArea(S32 area, S32 sim_object_limit); + void setDiscountRate(F32 rate); + + void setAllowModify(BOOL b) { setParcelFlag(PF_CREATE_OBJECTS, b); } + void setAllowGroupModify(BOOL b) { setParcelFlag(PF_CREATE_GROUP_OBJECTS, b); } + void setAllowAllObjectEntry(BOOL b) { setParcelFlag(PF_ALLOW_ALL_OBJECT_ENTRY, b); } + void setAllowGroupObjectEntry(BOOL b) { setParcelFlag(PF_ALLOW_GROUP_OBJECT_ENTRY, b); } + void setAllowTerraform(BOOL b){setParcelFlag(PF_ALLOW_TERRAFORM, b); } + void setAllowDamage(BOOL b) { setParcelFlag(PF_ALLOW_DAMAGE, b); } + void setAllowFly(BOOL b) { setParcelFlag(PF_ALLOW_FLY, b); } + void setAllowLandmark(BOOL b){ setParcelFlag(PF_ALLOW_LANDMARK, b); } + void setAllowGroupScripts(BOOL b) { setParcelFlag(PF_ALLOW_GROUP_SCRIPTS, b); } + void setAllowOtherScripts(BOOL b) { setParcelFlag(PF_ALLOW_OTHER_SCRIPTS, b); } + void setAllowDeedToGroup(BOOL b) { setParcelFlag(PF_ALLOW_DEED_TO_GROUP, b); } + void setContributeWithDeed(BOOL b) { setParcelFlag(PF_CONTRIBUTE_WITH_DEED, b); } + void setForSale(BOOL b) { setParcelFlag(PF_FOR_SALE, b); } + void setSoundOnly(BOOL b) { setParcelFlag(PF_SOUND_LOCAL, b); } + void setDenyAnonymous(BOOL b) { setParcelFlag(PF_DENY_ANONYMOUS, b); } + void setDenyIdentified(BOOL b) { setParcelFlag(PF_DENY_IDENTIFIED, b); } + void setDenyTransacted(BOOL b) { setParcelFlag(PF_DENY_TRANSACTED, b); } + void setRestrictPushObject(BOOL b) { setParcelFlag(PF_RESTRICT_PUSHOBJECT, b); } + + void setDrawDistance(F32 dist) { mDrawDistance = dist; } + void setSalePrice(S32 price) { mSalePrice = price; } + void setGroupID(const LLUUID& id) { mGroupID = id; } + //void setGroupName(const char* s) { mGroupName.assign(s); } + void setPassPrice(S32 price) { mPassPrice = price; } + void setPassHours(F32 hours) { mPassHours = hours; } + + BOOL importStream(std::istream& input_stream); + BOOL importAccessEntry(std::istream& input_stream, LLAccessEntry* entry); + BOOL exportStream(std::ostream& output_stream); + + void packMessage(LLMessageSystem* msg); + void unpackMessage(LLMessageSystem* msg); + + void packAccessEntries(LLMessageSystem* msg, + const std::map<LLUUID,LLAccessEntry>& list); + void unpackAccessEntries(LLMessageSystem* msg, + std::map<LLUUID,LLAccessEntry>* list); + + void setAABBMin(const LLVector3& min) { mAABBMin = min; } + void setAABBMax(const LLVector3& max) { mAABBMax = max; } + + // Extend AABB to include rectangle from min to max. + void extendAABB(const LLVector3& box_min, const LLVector3& box_max); + + void dump(); + + // Scans the pass list and removes any items with an expiration + // time earlier than "now". + void expirePasses(S32 now); + + // Add to list, suppressing duplicates. Returns TRUE if added. + BOOL addToAccessList(const LLUUID& agent_id, S32 time); + BOOL addToBanList(const LLUUID& agent_id, S32 time); + BOOL removeFromAccessList(const LLUUID& agent_id); + BOOL removeFromBanList(const LLUUID& agent_id); + + // ACCESSORS + const LLUUID& getID() { return mID; } + const char* getName() const { return mName.c_str(); } + const char* getDesc() const { return mDesc.c_str(); } + const char* getMusicURL() const { return mMusicURL.c_str(); } + const char* getMediaURL() const { return mMediaURL.c_str(); } + const LLUUID& getMediaID() const { return mMediaID; } + const U8 getMediaAutoScale() const { return mMediaAutoScale; } + S32 getLocalID() const { return mLocalID; } + const LLUUID& getOwnerID() const { return mOwnerID; } + const LLUUID& getGroupID() const { return mGroupID; } + //const char* getGroupName() const { return mGroupName.c_str(); } + S32 getPassPrice() const { return mPassPrice; } + F32 getPassHours() const { return mPassHours; } + BOOL getIsGroupOwned() const { return mGroupOwned; } + + U32 getAuctionID() { return mAuctionID; } + BOOL getReservedForNewbie() { return mIsReservedForNewbie; } + bool isInEscrow() const { return mInEscrow; } + + BOOL isPublic() const; + + // Region-local user-specified position + const LLVector3& getUserLocation() const { return mUserLocation; } + const LLVector3& getUserLookAt() const { return mUserLookAt; } + ELandingType getLandingType() const { return mLandingType; } + + // User-specified snapshot + const LLUUID& getSnapshotID() const { return mSnapshotID; } + + // the authorized buyer id is the person who is the only + // agent/group that has authority to purchase. (ie, ui specified a + // particular agent could buy the plot). + const LLUUID& getAuthorizedBuyerID() const { return mAuthBuyerID; } + + // helper function + BOOL isBuyerAuthorized(const LLUUID& buyer_id) const; + + // The buyer of a plot is set when someone indicates they want to + // buy the plot, and the system is simply waiting for tier-up + // approval + //const LLUUID& getBuyerID() const { return mBuyerID; } + + // functions to deal with ownership status. + EOwnershipStatus getOwnershipStatus() const { return mStatus; } + static const char* getOwnershipStatusString(EOwnershipStatus status); + void setOwnershipStatus(EOwnershipStatus status) { mStatus = status; } + + // dealing with parcel category information + ECategory getCategory() const {return mCategory; } + static const char* getCategoryString(ECategory category); + static const char* getCategoryUIString(ECategory category); + static ECategory getCategoryFromString(const char* string); + static ECategory getCategoryFromUIString(const char* string); + + // functions for parcel action (used for logging) + static const char* getActionString(EAction action); + + // dealing with sales and parcel conversion. + // + // the isSaleTimerExpired will trivially return FALSE if there is + // no sale going on. Pass in the current time in usec which will + // be used for comparison. + BOOL isSaleTimerExpired(const U64& time); + + F32 getSaleTimerExpires() { return mSaleTimerExpires.getRemainingTimeF32(); } + + // should the parcel join on complete? + //U32 getJoinNeighbors() const { return mJoinNeighbors; } + + // need to record a few things with the parcel when a sale + // starts. + void startSale(const LLUUID& buyer_id, BOOL is_buyer_group); + + // do the expiration logic, which needs to return values usable in + // a money transaction. + void expireSale(U32& type, U8& flags, LLUUID& from_id, LLUUID& to_id); + void completeSale(U32& type, U8& flags, LLUUID& to_id); + void clearSale(); + + // this function returns TRUE if the parcel needs conversion to a + // lease from a non-owned-status state. + BOOL getRecordTransaction() const { return mRecordTransaction; } + void setRecordTransaction(BOOL record) { mRecordTransaction = record; } + + + // more accessors + U32 getParcelFlags() const { return mParcelFlags; } + + BOOL getParcelFlag(U32 flag) const + { return (mParcelFlags & flag) ? TRUE : FALSE; } + + // objects can be added or modified by anyone (only parcel owner if disabled) + BOOL getAllowModify() const + { return (mParcelFlags & PF_CREATE_OBJECTS) ? TRUE : FALSE; } + + // objects can be added or modified by group members + BOOL getAllowGroupModify() const + { return (mParcelFlags & PF_CREATE_GROUP_OBJECTS) ? TRUE : FALSE; } + + // the parcel can be deeded to the group + BOOL getAllowDeedToGroup() const + { return (mParcelFlags & PF_ALLOW_DEED_TO_GROUP) ? TRUE : FALSE; } + + // Does the owner want to make a contribution along with the deed. + BOOL getContributeWithDeed() const + { return (mParcelFlags & PF_CONTRIBUTE_WITH_DEED) ? TRUE : FALSE; } + + // heightfield can be modified + BOOL getAllowTerraform() const + { return (mParcelFlags & PF_ALLOW_TERRAFORM) ? TRUE : FALSE; } + + // avatars can be hurt here + BOOL getAllowDamage() const + { return (mParcelFlags & PF_ALLOW_DAMAGE) ? TRUE : FALSE; } + + BOOL getAllowFly() const + { return (mParcelFlags & PF_ALLOW_FLY) ? TRUE : FALSE; } + + BOOL getAllowLandmark() const + { return (mParcelFlags & PF_ALLOW_LANDMARK) ? TRUE : FALSE; } + + BOOL getAllowGroupScripts() const + { return (mParcelFlags & PF_ALLOW_GROUP_SCRIPTS) ? TRUE : FALSE; } + + BOOL getAllowOtherScripts() const + { return (mParcelFlags & PF_ALLOW_OTHER_SCRIPTS) ? TRUE : FALSE; } + + BOOL getAllowAllObjectEntry() const + { return (mParcelFlags & PF_ALLOW_ALL_OBJECT_ENTRY) ? TRUE : FALSE; } + + BOOL getAllowGroupObjectEntry() const + { return (mParcelFlags & PF_ALLOW_GROUP_OBJECT_ENTRY) ? TRUE : FALSE; } + + BOOL getForSale() const + { return (mParcelFlags & PF_FOR_SALE) ? TRUE : FALSE; } + BOOL getSoundLocal() const + { return (mParcelFlags & PF_SOUND_LOCAL) ? TRUE : FALSE; } + BOOL getAllowPublish() const + { return (mParcelFlags & PF_ALLOW_PUBLISH) ? TRUE : FALSE; } + BOOL getMaturePublish() const + { return (mParcelFlags & PF_MATURE_PUBLISH) ? TRUE : FALSE; } + BOOL getRestrictPushObject() const + { return (mParcelFlags & PF_RESTRICT_PUSHOBJECT) ? TRUE : FALSE; } + BOOL getRegionPushOverride() const + { return mRegionPushOverride; } + BOOL getRegionDenyAnonymousOverride() const + { return mRegionDenyAnonymousOverride; } + BOOL getRegionDenyIdentifiedOverride() const + { return mRegionDenyIdentifiedOverride; } + BOOL getRegionDenyTransactedOverride() const + { return mRegionDenyTransactedOverride; } + + F32 getDrawDistance() const { return mDrawDistance; } + S32 getSalePrice() const { return mSalePrice; } + time_t getClaimDate() const { return mClaimDate; } + S32 getClaimPricePerMeter() const { return mClaimPricePerMeter; } + S32 getRentPricePerMeter() const { return mRentPricePerMeter; } + + // Area is NOT automatically calculated. You must calculate it + // and store it with setArea. + S32 getArea() const { return mArea; } + + // deprecated 12/11/2003 + //F32 getDiscountRate() const { return mDiscountRate; } + + S32 getClaimPrice() const { return mClaimPricePerMeter * mArea; } + + // Can this agent create objects here? + BOOL allowModifyBy(const LLUUID &agent_id, const LLUUID &group_id) const; + + // Can this agent change the shape of the land? + BOOL allowTerraformBy(const LLUUID &agent_id) const; + + // Returns 0 if access is OK, otherwise a BA_ return code above. + S32 blockAccess(const LLUUID& agent_id, const LLUUID& group_id, const BOOL is_agent_identified, const BOOL is_agent_transacted) const; + + // Only checks if the agent is explicitly banned from this parcel + BOOL isAgentBanned(const LLUUID& agent_id) const; + + static bool isAgentBlockedFromParcel(LLParcel* parcelp, + const LLUUID& agent_id, + const std::vector<LLUUID>& group_ids, + const BOOL is_agent_identified, + const BOOL is_agent_transacted); + + bool operator==(const LLParcel &rhs) const; + + // Calculate rent - area * rent * discount rate + S32 getTotalRent() const; + F32 getAdjustedRentPerMeter() const; + + const LLVector3& getAABBMin() const { return mAABBMin; } + const LLVector3& getAABBMax() const { return mAABBMax; } + LLVector3 getCenterpoint() const; + + // simwide + S32 getSimWideMaxPrimCapacity() const { return mSimWideMaxPrimCapacity; } + S32 getSimWidePrimCount() const { return mSimWidePrimCount; } + + // this parcel only (not simwide) + S32 getMaxPrimCapacity() const { return mMaxPrimCapacity; } + S32 getPrimCount() const { return mOwnerPrimCount + mGroupPrimCount + mOtherPrimCount + mSelectedPrimCount; } + S32 getOwnerPrimCount() const { return mOwnerPrimCount; } + S32 getGroupPrimCount() const { return mGroupPrimCount; } + S32 getOtherPrimCount() const { return mOtherPrimCount; } + S32 getSelectedPrimCount() const{ return mSelectedPrimCount; } + S32 getTempPrimCount() const { return mTempPrimCount; } + F32 getParcelPrimBonus() const { return mParcelPrimBonus; } + + S32 getCleanOtherTime() const { return mCleanOtherTime; } + + void setMaxPrimCapacity(S32 max) { mMaxPrimCapacity = max; } + // simwide + void setSimWideMaxPrimCapacity(S32 current) { mSimWideMaxPrimCapacity = current; } + void setSimWidePrimCount(S32 current) { mSimWidePrimCount = current; } + + // this parcel only (not simwide) + void setOwnerPrimCount(S32 current) { mOwnerPrimCount = current; } + void setGroupPrimCount(S32 current) { mGroupPrimCount = current; } + void setOtherPrimCount(S32 current) { mOtherPrimCount = current; } + void setSelectedPrimCount(S32 current) { mSelectedPrimCount = current; } + void setTempPrimCount(S32 current) { mTempPrimCount = current; } + void setParcelPrimBonus(F32 bonus) { mParcelPrimBonus = bonus; } + + void setCleanOtherTime(S32 time) { mCleanOtherTime = time; } + void setRegionPushOverride(BOOL override) {mRegionPushOverride = override; } + void setRegionDenyAnonymousOverride(BOOL override) { mRegionDenyAnonymousOverride = override; } + void setRegionDenyIdentifiedOverride(BOOL override) { mRegionDenyIdentifiedOverride = override; } + void setRegionDenyTransactedOverride(BOOL override) { mRegionDenyTransactedOverride = override; } + + // Accessors for parcel sellWithObjects + void setPreviousOwnerID(LLUUID prev_owner) { mPreviousOwnerID = prev_owner; } + void setPreviouslyGroupOwned(BOOL b) { mPreviouslyGroupOwned = b; } + void setSellWithObjects(BOOL b) { setParcelFlag(PF_SELL_PARCEL_OBJECTS, b); } + + LLUUID getPreviousOwnerID() const { return mPreviousOwnerID; } + BOOL getPreviouslyGroupOwned() const { return mPreviouslyGroupOwned; } + BOOL getSellWithObjects() const { return (mParcelFlags & PF_SELL_PARCEL_OBJECTS) ? TRUE : FALSE; } + +protected: + LLUUID mID; + LLUUID mOwnerID; + LLUUID mGroupID; + BOOL mGroupOwned; // TRUE if mOwnerID is a group_id + LLUUID mPreviousOwnerID; + BOOL mPreviouslyGroupOwned; + + EOwnershipStatus mStatus; + ECategory mCategory; + LLUUID mAuthBuyerID; + LLUUID mSnapshotID; + LLVector3 mUserLocation; + LLVector3 mUserLookAt; + ELandingType mLandingType; + LLTimer mSaleTimerExpires; + S32 mGraceExtension; + BOOL mRecordTransaction; + + + // This value is non-zero if there is an auction associated with + // the parcel. + U32 mAuctionID; + + // This value is TRUE if the land is reserved for a newbie. + BOOL mIsReservedForNewbie; + + // value used to temporarily lock attempts to purchase the parcel. + bool mInEscrow; + + time_t mClaimDate; // UTC Unix-format time + S32 mClaimPricePerMeter; // meter squared + S32 mRentPricePerMeter; // meter squared + S32 mArea; // meter squared + F32 mDiscountRate; // 0.0-1.0 + F32 mDrawDistance; + U32 mParcelFlags; + S32 mSalePrice; // linden dollars + std::string mName; + std::string mDesc; + std::string mMusicURL; + std::string mMediaURL; + U8 mMediaAutoScale; + LLUUID mMediaID; + S32 mPassPrice; + F32 mPassHours; + LLVector3 mAABBMin; + LLVector3 mAABBMax; + S32 mMaxPrimCapacity; + S32 mSimWidePrimCount; + S32 mSimWideMaxPrimCapacity; + //S32 mSimWidePrimCorrection; + S32 mOwnerPrimCount; + S32 mGroupPrimCount; + S32 mOtherPrimCount; + S32 mSelectedPrimCount; + S32 mTempPrimCount; + F32 mParcelPrimBonus; + S32 mCleanOtherTime; + BOOL mRegionPushOverride; + BOOL mRegionDenyAnonymousOverride; + BOOL mRegionDenyIdentifiedOverride; + BOOL mRegionDenyTransactedOverride; + + +public: + // HACK, make private + S32 mLocalID; + LLUUID mBanListTransactionID; + LLUUID mAccessListTransactionID; + std::map<LLUUID,LLAccessEntry> mAccessList; + std::map<LLUUID,LLAccessEntry> mBanList; + std::map<LLUUID,LLAccessEntry> mTempBanList; + std::map<LLUUID,LLAccessEntry> mTempAccessList; + + //LLDynamicArray<LLAccessEntry> mRenterList; +}; + + +#endif diff --git a/indra/llinventory/llparcelflags.h b/indra/llinventory/llparcelflags.h new file mode 100644 index 0000000000..43571abe77 --- /dev/null +++ b/indra/llinventory/llparcelflags.h @@ -0,0 +1,105 @@ +/** + * @file llparcelflags.h + * + * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LL_LLPARCEL_FLAGS_H +#define LL_LLPARCEL_FLAGS_H + +//--------------------------------------------------------------------------- +// Parcel Flags (PF) constants +//--------------------------------------------------------------------------- +const U32 PF_ALLOW_FLY = 1 << 0;// Can start flying +const U32 PF_ALLOW_OTHER_SCRIPTS= 1 << 1;// Scripts by others can run. +const U32 PF_FOR_SALE = 1 << 2;// Can buy this land +const U32 PF_FOR_SALE_OBJECTS = 1 << 7;// Can buy all objects on this land +const U32 PF_ALLOW_LANDMARK = 1 << 3; +const U32 PF_ALLOW_TERRAFORM = 1 << 4; +const U32 PF_ALLOW_DAMAGE = 1 << 5; +const U32 PF_CREATE_OBJECTS = 1 << 6; +// 7 is moved above +const U32 PF_USE_ACCESS_GROUP = 1 << 8; +const U32 PF_USE_ACCESS_LIST = 1 << 9; +const U32 PF_USE_BAN_LIST = 1 << 10; +const U32 PF_USE_PASS_LIST = 1 << 11; +const U32 PF_SHOW_DIRECTORY = 1 << 12; +const U32 PF_ALLOW_DEED_TO_GROUP = 1 << 13; +const U32 PF_CONTRIBUTE_WITH_DEED = 1 << 14; +const U32 PF_SOUND_LOCAL = 1 << 15; // Hear sounds in this parcel only +const U32 PF_SELL_PARCEL_OBJECTS = 1 << 16; // Objects on land are included as part of the land when the land is sold +const U32 PF_ALLOW_PUBLISH = 1 << 17; // Allow publishing of parcel information on the web +const U32 PF_MATURE_PUBLISH = 1 << 18; // The information on this parcel is mature +const U32 PF_URL_WEB_PAGE = 1 << 19; // The "media URL" is an HTML page +const U32 PF_URL_RAW_HTML = 1 << 20; // The "media URL" is a raw HTML string like <H1>Foo</H1> +const U32 PF_RESTRICT_PUSHOBJECT = 1 << 21; // Restrict push object to either on agent or on scripts owned by parcel owner +const U32 PF_DENY_ANONYMOUS = 1 << 22; // Deny all non identified/transacted accounts +const U32 PF_DENY_IDENTIFIED = 1 << 23; // Deny identified accounts +const U32 PF_DENY_TRANSACTED = 1 << 24; // Deny identified accounts +const U32 PF_ALLOW_GROUP_SCRIPTS = 1 << 25; // Allow scripts owned by group +const U32 PF_CREATE_GROUP_OBJECTS = 1 << 26; // Allow object creation by group members or objects +const U32 PF_ALLOW_ALL_OBJECT_ENTRY = 1 << 27; // Allow all objects to enter a parcel +const U32 PF_ALLOW_GROUP_OBJECT_ENTRY = 1 << 28; // Only allow group (and owner) objects to enter the parcel + + +const U32 PF_RESERVED = 1 << 31; + +// If any of these are true the parcel is restricting access in some maner. +const U32 PF_USE_RESTRICTED_ACCESS = PF_USE_ACCESS_GROUP + | PF_USE_ACCESS_LIST + | PF_USE_BAN_LIST + | PF_USE_PASS_LIST + | PF_DENY_ANONYMOUS + | PF_DENY_IDENTIFIED + | PF_DENY_TRANSACTED; +const U32 PF_NONE = 0x00000000; +const U32 PF_ALL = 0x7FFFFFFF; +const U32 PF_DEFAULT = PF_ALLOW_FLY + | PF_ALLOW_OTHER_SCRIPTS + | PF_ALLOW_GROUP_SCRIPTS + | PF_ALLOW_LANDMARK + | PF_CREATE_OBJECTS + | PF_CREATE_GROUP_OBJECTS + | PF_USE_BAN_LIST + | PF_ALLOW_ALL_OBJECT_ENTRY + | PF_ALLOW_GROUP_OBJECT_ENTRY; + +// Access list flags +const U32 AL_ACCESS = (1 << 0); +const U32 AL_BAN = (1 << 1); +//const U32 AL_RENTER = (1 << 2); + +// Block access return values. BA_ALLOWED is the only success case +// since some code in the simulator relies on that assumption. All +// other BA_ values should be reasons why you are not allowed. +const S32 BA_ALLOWED = 0; +const S32 BA_NOT_IN_GROUP = 1; +const S32 BA_NOT_ON_LIST = 2; +const S32 BA_BANNED = 3; +const S32 BA_NO_ACCESS_LEVEL = 4; + +// ParcelRelease flags +const U32 PR_NONE = 0x0; +const U32 PR_GOD_FORCE = (1 << 0); + +enum EObjectCategory +{ + OC_INVALID = -1, + OC_NONE = 0, + OC_TOTAL = 0, // yes zero, like OC_NONE + OC_OWNER, + OC_GROUP, + OC_OTHER, + OC_SELECTED, + OC_TEMP, + OC_COUNT +}; + +const S32 PARCEL_DETAILS_NAME = 0; +const S32 PARCEL_DETAILS_DESC = 1; +const S32 PARCEL_DETAILS_OWNER = 2; +const S32 PARCEL_DETAILS_GROUP = 3; +const S32 PARCEL_DETAILS_AREA = 4; + +#endif diff --git a/indra/llinventory/llpermissions.cpp b/indra/llinventory/llpermissions.cpp new file mode 100644 index 0000000000..2063ac33d6 --- /dev/null +++ b/indra/llinventory/llpermissions.cpp @@ -0,0 +1,1171 @@ +/** + * @file llpermissions.cpp + * @author Phoenix + * @brief Permissions for objects and inventory. + * + * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "linden_common.h" + +#include "llpermissions.h" + +// library includes +#include "message.h" +#include "metapropertyt.h" + +///---------------------------------------------------------------------------- +/// Class LLPermissions +///---------------------------------------------------------------------------- + +const LLPermissions LLPermissions::DEFAULT; + +// No creator = created by system +LLPermissions::LLPermissions() +{ + init(LLUUID::null, LLUUID::null, LLUUID::null, LLUUID::null); +} + + +// Default to created by system +void LLPermissions::init(const LLUUID& creator, const LLUUID& owner, const LLUUID& last_owner, const LLUUID& group) +{ + mCreator = creator; + mOwner = owner; + mLastOwner = last_owner; + mGroup = group; + + mMaskBase = PERM_ALL; + mMaskOwner = PERM_ALL; + mMaskEveryone = PERM_ALL; + mMaskGroup = PERM_ALL; + mMaskNextOwner = PERM_ALL; + fixOwnership(); +} + + +void LLPermissions::initMasks(PermissionMask base, PermissionMask owner, + PermissionMask everyone, PermissionMask group, + PermissionMask next) +{ + mMaskBase = base; + mMaskOwner = owner; + mMaskEveryone = everyone; + mMaskGroup = group; + mMaskNextOwner = next; + fixFairUse(); + fix(); +} + +BOOL LLPermissions::getOwnership(LLUUID& owner_id, BOOL& is_group_owned) const +{ + if(mOwner.notNull()) + { + owner_id = mOwner; + is_group_owned = FALSE; + return TRUE; + } + else if(mIsGroupOwned) + { + owner_id = mGroup; + is_group_owned = TRUE; + return TRUE; + } + return FALSE; +} + +LLUUID LLPermissions::getSafeOwner() const +{ + if(mOwner.notNull()) + { + return mOwner; + } + else if(mIsGroupOwned) + { + return mGroup; + } + else + { + llwarns << "LLPermissions::getSafeOwner() called with no valid owner!" << llendl; + LLUUID unused_uuid; + unused_uuid.generate(); + + return unused_uuid; + } +} + +U32 LLPermissions::getCRC32() const +{ + U32 rv = mCreator.getCRC32(); + rv += mOwner.getCRC32(); + rv += mLastOwner.getCRC32(); + rv += mGroup.getCRC32(); + rv += mMaskBase + mMaskOwner + mMaskEveryone + mMaskGroup; + return rv; +} + +void LLPermissions::set(const LLPermissions& from) +{ + mCreator = from.mCreator; + mOwner = from.mOwner; + mLastOwner = from.mLastOwner; + mGroup = from.mGroup; + + mMaskBase = from.mMaskBase; + mMaskOwner = from.mMaskOwner; + mMaskEveryone = from.mMaskEveryone; + mMaskGroup = from.mMaskGroup; + mMaskNextOwner = from.mMaskNextOwner; + mIsGroupOwned = from.mIsGroupOwned; +} + +// Fix hierarchy of permissions. +void LLPermissions::fix() +{ + mMaskOwner &= mMaskBase; + mMaskGroup &= mMaskOwner; + // next owner uses base, since you may want to sell locked objects. + mMaskNextOwner &= mMaskBase; + mMaskEveryone &= mMaskOwner; + mMaskEveryone &= ~PERM_MODIFY; + if(!(mMaskBase & PERM_TRANSFER) && !mIsGroupOwned) + { + mMaskGroup &= ~PERM_COPY; + mMaskEveryone &= ~PERM_COPY; + // Do not set mask next owner to too restrictive because if we + // rez an object, it may require an ownership transfer during + // rez, which will note the overly restrictive perms, and then + // fix them to allow fair use, which may be different than the + // original intention. + } +} + +// Correct for fair use - you can never take away the right to move +// stuff you own, and you can never take away the right to transfer +// something you cannot otherwise copy. +void LLPermissions::fixFairUse() +{ + mMaskBase |= PERM_MOVE; + if(!(mMaskBase & PERM_COPY)) + { + mMaskBase |= PERM_TRANSFER; + } + // (mask next owner == PERM_NONE) iff mask base is no transfer + if(mMaskNextOwner != PERM_NONE) + { + mMaskNextOwner |= PERM_MOVE; + } +} + +void LLPermissions::fixOwnership() +{ + if(mOwner.isNull() && mGroup.notNull()) + { + mIsGroupOwned = true; + } + else + { + mIsGroupOwned = false; + } +} + +// Allow accumulation of permissions. Results in the tightest +// permissions possible. In the case of clashing UUIDs, it sets the ID +// to LLUUID::null. +void LLPermissions::accumulate(const LLPermissions& perm) +{ + if(perm.mCreator != mCreator) + { + mCreator = LLUUID::null; + } + if(perm.mOwner != mOwner) + { + mOwner = LLUUID::null; + } + if(perm.mLastOwner != mLastOwner) + { + mLastOwner = LLUUID::null; + } + if(perm.mGroup != mGroup) + { + mGroup = LLUUID::null; + } + + mMaskBase &= perm.mMaskBase; + mMaskOwner &= perm.mMaskOwner; + mMaskGroup &= perm.mMaskGroup; + mMaskEveryone &= perm.mMaskEveryone; + mMaskNextOwner &= perm.mMaskNextOwner; + fix(); +} + +// saves last owner, sets current owner, and sets the group. note +// that this function has to more cleverly apply the fair use +// permissions. +BOOL LLPermissions::setOwnerAndGroup( + const LLUUID& agent, + const LLUUID& owner, + const LLUUID& group, + bool is_atomic) +{ + BOOL allowed = FALSE; + + if( agent.isNull() || mOwner.isNull() + || ((agent == mOwner) && ((owner == mOwner) || (mMaskOwner & PERM_TRANSFER)) ) ) + { + // ...system can alway set owner + // ...public objects can be claimed by anyone + // ...otherwise, agent must own it and have transfer ability + allowed = TRUE; + } + + if (allowed) + { + if(mLastOwner.isNull() || (!mOwner.isNull() && (owner != mLastOwner))) + { + mLastOwner = mOwner; + } + if((mOwner != owner) + || (mOwner.isNull() && owner.isNull() && (mGroup != group))) + { + mMaskBase = mMaskNextOwner; + mOwner = owner; + // this is a selective use of fair use for atomic + // permissions. + if(is_atomic && !(mMaskBase & PERM_COPY)) + { + mMaskBase |= PERM_TRANSFER; + } + } + mGroup = group; + fixOwnership(); + // if it's not atomic and we fix fair use, it blows away + //objects as inventory items which have different permissions + //than it's contents. :( + // fixFairUse(); + mMaskBase |= PERM_MOVE; + if(mMaskNextOwner != PERM_NONE) mMaskNextOwner |= PERM_MOVE; + fix(); + } + + return allowed; +} + +BOOL LLPermissions::deedToGroup(const LLUUID& agent, const LLUUID& group) +{ + if(group.notNull() && (agent.isNull() || ((group == mGroup) + && (mMaskOwner & PERM_TRANSFER) + && (mMaskGroup & PERM_MOVE)))) + { + if(mOwner.notNull()) + { + mLastOwner = mOwner; + mOwner.setNull(); + } + mMaskBase = mMaskNextOwner; + mGroup = group; + mIsGroupOwned = true; + fixFairUse(); + fix(); + return TRUE; + } + return FALSE; +} + +BOOL LLPermissions::setBaseBits(const LLUUID& agent, BOOL set, PermissionMask bits) +{ + BOOL ownership = FALSE; + if(agent.isNull()) + { + // only the system is always allowed to change base bits + ownership = TRUE; + } + + if (ownership) + { + if (set) + { + mMaskBase |= bits; // turn on bits + } + else + { + mMaskBase &= ~bits; // turn off bits + } + fix(); + } + + return ownership; +} + + +// Note: If you attempt to set bits that the base bits doesn't allow, +// the function will succeed, but those bits will not be set. +BOOL LLPermissions::setOwnerBits(const LLUUID& agent, BOOL set, PermissionMask bits) +{ + BOOL ownership = FALSE; + + if(agent.isNull()) + { + // ...system always allowed to change things + ownership = TRUE; + } + else if (agent == mOwner) + { + // ...owner bits can only be set by owner + ownership = TRUE; + } + + // If we have correct ownership and + if (ownership) + { + if (set) + { + mMaskOwner |= bits; // turn on bits + } + else + { + mMaskOwner &= ~bits; // turn off bits + } + fix(); + } + + return (ownership); +} + +BOOL LLPermissions::setGroupBits(const LLUUID& agent, const LLUUID& group, BOOL set, PermissionMask bits) +{ + BOOL ownership = FALSE; + if((agent.isNull()) || (agent == mOwner) + || ((group == mGroup) && (!mGroup.isNull()))) + { + // The group bits can be set by the system, the owner, or a + // group member. + ownership = TRUE; + } + + if (ownership) + { + if (set) + { + mMaskGroup |= bits; + } + else + { + mMaskGroup &= ~bits; + } + fix(); + } + return ownership; +} + + +// Note: If you attempt to set bits that the creator or owner doesn't allow, +// the function will succeed, but those bits will not be set. +BOOL LLPermissions::setEveryoneBits(const LLUUID& agent, const LLUUID& group, BOOL set, PermissionMask bits) +{ + BOOL ownership = FALSE; + if((agent.isNull()) || (agent == mOwner) + || ((group == mGroup) && (!mGroup.isNull()))) + { + // The everyone bits can be set by the system, the owner, or a + // group member. + ownership = TRUE; + } + if (ownership) + { + if (set) + { + mMaskEveryone |= bits; + } + else + { + mMaskEveryone &= ~bits; + } + + // Fix hierarchy of permissions + fix(); + } + return ownership; +} + +// Note: If you attempt to set bits that the creator or owner doesn't allow, +// the function will succeed, but those bits will not be set. +BOOL LLPermissions::setNextOwnerBits(const LLUUID& agent, const LLUUID& group, BOOL set, PermissionMask bits) +{ + BOOL ownership = FALSE; + if((agent.isNull()) || (agent == mOwner) + || ((group == mGroup) && (!mGroup.isNull()))) + { + // The next owner bits can be set by the system, the owner, or + // a group member. + ownership = TRUE; + } + if (ownership) + { + if (set) + { + mMaskNextOwner |= bits; + } + else + { + mMaskNextOwner &= ~bits; + } + + // Fix-up permissions + if(!(mMaskNextOwner & PERM_COPY)) + { + mMaskNextOwner |= PERM_TRANSFER; + } + fix(); + } + return ownership; +} + +BOOL LLPermissions::allowOperationBy(PermissionBit op, const LLUUID& requester, const LLUUID& group) const +{ + if(requester.isNull()) + { + // ...system making request + // ...not owned + return TRUE; + } + else if (mIsGroupOwned && (mGroup == requester)) + { + // group checking ownership permissions + return (mMaskOwner & op); + } + else if (!mIsGroupOwned && (mOwner == requester)) + { + // ...owner making request + return (mMaskOwner & op); + } + else if(mGroup.notNull() && (mGroup == group)) + { + // group member making request + return ((mMaskGroup & op) || (mMaskEveryone & op)); + } + return (mMaskEveryone & op); +} + +// +// Messaging support +// +void LLPermissions::packMessage(LLMessageSystem* msg) const +{ + msg->addUUIDFast(_PREHASH_CreatorID, mCreator); + msg->addUUIDFast(_PREHASH_OwnerID, mOwner); + msg->addUUIDFast(_PREHASH_GroupID, mGroup); + + msg->addU32Fast(_PREHASH_BaseMask, mMaskBase ); + msg->addU32Fast(_PREHASH_OwnerMask, mMaskOwner ); + msg->addU32Fast(_PREHASH_GroupMask, mMaskGroup ); + msg->addU32Fast(_PREHASH_EveryoneMask, mMaskEveryone ); + msg->addU32Fast(_PREHASH_NextOwnerMask, mMaskNextOwner ); + msg->addBOOLFast(_PREHASH_GroupOwned, (BOOL)mIsGroupOwned); +} + + +void LLPermissions::unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num) +{ + msg->getUUIDFast(block, _PREHASH_CreatorID, mCreator, block_num); + msg->getUUIDFast(block, _PREHASH_OwnerID, mOwner, block_num); + msg->getUUIDFast(block, _PREHASH_GroupID, mGroup, block_num); + + msg->getU32Fast(block, _PREHASH_BaseMask, mMaskBase, block_num ); + msg->getU32Fast(block, _PREHASH_OwnerMask, mMaskOwner, block_num ); + msg->getU32Fast(block, _PREHASH_GroupMask, mMaskGroup, block_num ); + msg->getU32Fast(block, _PREHASH_EveryoneMask, mMaskEveryone, block_num ); + msg->getU32Fast(block, _PREHASH_NextOwnerMask, mMaskNextOwner, block_num ); + BOOL tmp; + msg->getBOOLFast(block, _PREHASH_GroupOwned, tmp, block_num); + mIsGroupOwned = (bool)tmp; +} + + +// +// File support +// + +BOOL LLPermissions::importFile(FILE *fp) +{ + init(LLUUID::null, LLUUID::null, LLUUID::null, LLUUID::null); + const S32 BUFSIZE = 16384; + + char buffer[BUFSIZE]; + char keyword[256]; + char valuestr[256]; + char uuid_str[256]; + U32 mask; + + keyword[0] = '\0'; + valuestr[0] = '\0'; + + while (!feof(fp)) + { + fgets(buffer, BUFSIZE, fp); + sscanf(buffer, " %s %s", keyword, valuestr); + if (!keyword) + { + continue; + } + if (!strcmp("{", keyword)) + { + continue; + } + if (!strcmp("}",keyword)) + { + break; + } + else if (!strcmp("creator_mask", keyword)) + { + // legacy support for "creator" masks + sscanf(valuestr, "%x", &mask); + mMaskBase = mask; + fixFairUse(); + } + else if (!strcmp("base_mask", keyword)) + { + sscanf(valuestr, "%x", &mask); + mMaskBase = mask; + //fixFairUse(); + } + else if (!strcmp("owner_mask", keyword)) + { + sscanf(valuestr, "%x", &mask); + mMaskOwner = mask; + } + else if (!strcmp("group_mask", keyword)) + { + sscanf(valuestr, "%x", &mask); + mMaskGroup = mask; + } + else if (!strcmp("everyone_mask", keyword)) + { + sscanf(valuestr, "%x", &mask); + mMaskEveryone = mask; + } + else if (!strcmp("next_owner_mask", keyword)) + { + sscanf(valuestr, "%x", &mask); + mMaskNextOwner = mask; + } + else if (!strcmp("creator_id", keyword)) + { + sscanf(valuestr, "%s", uuid_str); + mCreator.set(uuid_str); + } + else if (!strcmp("owner_id", keyword)) + { + sscanf(valuestr, "%s", uuid_str); + mOwner.set(uuid_str); + } + else if (!strcmp("last_owner_id", keyword)) + { + sscanf(valuestr, "%s", uuid_str); + mLastOwner.set(uuid_str); + } + else if (!strcmp("group_id", keyword)) + { + sscanf(valuestr, "%s", uuid_str); + mGroup.set(uuid_str); + } + else if (!strcmp("group_owned", keyword)) + { + sscanf(valuestr, "%d", &mask); + if(mask) mIsGroupOwned = true; + else mIsGroupOwned = false; + } + else + { + llinfos << "unknown keyword " << keyword << " in permissions import" << llendl; + } + } + fix(); + return TRUE; +} + + +BOOL LLPermissions::exportFile(FILE *fp) const +{ + char uuid_str[256]; + + fprintf(fp, "\tpermissions 0\n"); + fprintf(fp, "\t{\n"); + + fprintf(fp, "\t\tbase_mask\t%08x\n", mMaskBase); + fprintf(fp, "\t\towner_mask\t%08x\n", mMaskOwner); + fprintf(fp, "\t\tgroup_mask\t%08x\n", mMaskGroup); + fprintf(fp, "\t\teveryone_mask\t%08x\n", mMaskEveryone); + fprintf(fp, "\t\tnext_owner_mask\t%08x\n", mMaskNextOwner); + + mCreator.toString(uuid_str); + fprintf(fp, "\t\tcreator_id\t%s\n", uuid_str); + + mOwner.toString(uuid_str); + fprintf(fp, "\t\towner_id\t%s\n", uuid_str); + + mLastOwner.toString(uuid_str); + fprintf(fp, "\t\tlast_owner_id\t%s\n", uuid_str); + + mGroup.toString(uuid_str); + fprintf(fp, "\t\tgroup_id\t%s\n", uuid_str); + + if(mIsGroupOwned) + { + fprintf(fp, "\t\tgroup_owned\t1\n"); + } + fprintf(fp,"\t}\n"); + return TRUE; +} + + +BOOL LLPermissions::importLegacyStream(std::istream& input_stream) +{ + init(LLUUID::null, LLUUID::null, LLUUID::null, LLUUID::null); + const S32 BUFSIZE = 16384; + + char buffer[BUFSIZE]; + char keyword[256]; + char valuestr[256]; + char uuid_str[256]; + U32 mask; + + keyword[0] = '\0'; + valuestr[0] = '\0'; + + while (input_stream.good()) + { + input_stream.getline(buffer, BUFSIZE); + sscanf(buffer, " %s %s", keyword, valuestr); + if (!keyword) + { + continue; + } + if (!strcmp("{", keyword)) + { + continue; + } + if (!strcmp("}",keyword)) + { + break; + } + else if (!strcmp("creator_mask", keyword)) + { + // legacy support for "creator" masks + sscanf(valuestr, "%x", &mask); + mMaskBase = mask; + fixFairUse(); + } + else if (!strcmp("base_mask", keyword)) + { + sscanf(valuestr, "%x", &mask); + mMaskBase = mask; + //fixFairUse(); + } + else if (!strcmp("owner_mask", keyword)) + { + sscanf(valuestr, "%x", &mask); + mMaskOwner = mask; + } + else if (!strcmp("group_mask", keyword)) + { + sscanf(valuestr, "%x", &mask); + mMaskGroup = mask; + } + else if (!strcmp("everyone_mask", keyword)) + { + sscanf(valuestr, "%x", &mask); + mMaskEveryone = mask; + } + else if (!strcmp("next_owner_mask", keyword)) + { + sscanf(valuestr, "%x", &mask); + mMaskNextOwner = mask; + } + else if (!strcmp("creator_id", keyword)) + { + sscanf(valuestr, "%s", uuid_str); + mCreator.set(uuid_str); + } + else if (!strcmp("owner_id", keyword)) + { + sscanf(valuestr, "%s", uuid_str); + mOwner.set(uuid_str); + } + else if (!strcmp("last_owner_id", keyword)) + { + sscanf(valuestr, "%s", uuid_str); + mLastOwner.set(uuid_str); + } + else if (!strcmp("group_id", keyword)) + { + sscanf(valuestr, "%s", uuid_str); + mGroup.set(uuid_str); + } + else if (!strcmp("group_owned", keyword)) + { + sscanf(valuestr, "%d", &mask); + if(mask) mIsGroupOwned = true; + else mIsGroupOwned = false; + } + else + { + llinfos << "unknown keyword " << keyword << " in permissions import" << llendl; + } + } + fix(); + return TRUE; +} + + +BOOL LLPermissions::exportLegacyStream(std::ostream& output_stream) const +{ + char uuid_str[256]; + + output_stream << "\tpermissions 0\n"; + output_stream << "\t{\n"; + + char buffer[256]; + sprintf(buffer, "\t\tbase_mask\t%08x\n", mMaskBase); + output_stream << buffer; + sprintf(buffer, "\t\towner_mask\t%08x\n", mMaskOwner); + output_stream << buffer; + sprintf(buffer, "\t\tgroup_mask\t%08x\n", mMaskGroup); + output_stream << buffer; + sprintf(buffer, "\t\teveryone_mask\t%08x\n", mMaskEveryone); + output_stream << buffer; + sprintf(buffer, "\t\tnext_owner_mask\t%08x\n", mMaskNextOwner); + output_stream << buffer; + + mCreator.toString(uuid_str); + output_stream << "\t\tcreator_id\t" << uuid_str << "\n"; + + mOwner.toString(uuid_str); + output_stream << "\t\towner_id\t" << uuid_str << "\n"; + + mLastOwner.toString(uuid_str); + output_stream << "\t\tlast_owner_id\t" << uuid_str << "\n"; + + mGroup.toString(uuid_str); + output_stream << "\t\tgroup_id\t" << uuid_str << "\n"; + + if(mIsGroupOwned) + { + output_stream << "\t\tgroup_owned\t1\n"; + } + output_stream << "\t}\n"; + return TRUE; +} + + +LLXMLNode *LLPermissions::exportFileXML() const +{ + LLXMLNode *ret = new LLXMLNode("permissions", FALSE); + + ret->createChild("group_owned", TRUE)->setBoolValue(1, (const BOOL*)&mIsGroupOwned); + + ret->createChild("base_mask", FALSE)->setByteValue(4, (U8*)&mMaskBase, LLXMLNode::ENCODING_HEX); + ret->createChild("owner_mask", FALSE)->setByteValue(4, (U8*)&mMaskOwner, LLXMLNode::ENCODING_HEX); + ret->createChild("group_mask", FALSE)->setByteValue(4, (U8*)&mMaskGroup, LLXMLNode::ENCODING_HEX); + ret->createChild("everyone_mask", FALSE)->setByteValue(4, (U8*)&mMaskEveryone, LLXMLNode::ENCODING_HEX); + ret->createChild("next_owner_mask", FALSE)->setByteValue(4, (U8*)&mMaskNextOwner, LLXMLNode::ENCODING_HEX); + + ret->createChild("creator_id", FALSE)->setUUIDValue(1, &mCreator); + ret->createChild("owner_id", FALSE)->setUUIDValue(1, &mOwner); + ret->createChild("last_owner_id", FALSE)->setUUIDValue(1, &mLastOwner); + ret->createChild("group_id", FALSE)->setUUIDValue(1, &mGroup); + + return ret; +} + +bool LLPermissions::importXML(LLXMLNode* node) +{ + bool success = false; + if (node) + { + success = true; + LLXMLNodePtr sub_node; + if (node->getChild("base_mask", sub_node)) + success = success && (4 == sub_node->getByteValue(4, (U8*)&mMaskBase)); + if (node->getChild("owner_mask", sub_node)) + success = success && (4 == sub_node->getByteValue(4, (U8*)&mMaskOwner)); + if (node->getChild("group_mask", sub_node)) + success = success && (4 == sub_node->getByteValue(4, (U8*)&mMaskGroup)); + if (node->getChild("everyone_mask", sub_node)) + success = success && (4 == sub_node->getByteValue(4, (U8*)&mMaskEveryone)); + if (node->getChild("next_owner_mask", sub_node)) + success = success && (4 == sub_node->getByteValue(4, (U8*)&mMaskNextOwner)); + + if (node->getChild("creator_id", sub_node)) + success = success && (1 == sub_node->getUUIDValue(1, &mCreator)); + if (node->getChild("owner_id", sub_node)) + success = success && (1 == sub_node->getUUIDValue(1, &mOwner)); + if (node->getChild("last_owner_id", sub_node)) + success = success && (1 == sub_node->getUUIDValue(1, &mLastOwner)); + if (node->getChild("group_id", sub_node)) + success = success && (1 == sub_node->getUUIDValue(1, &mGroup)); + if (node->getChild("group_owned", sub_node)) + success = success && (1 == sub_node->getBoolValue(1, (BOOL*)&mIsGroupOwned)); + if (!success) + { + lldebugs << "LLPermissions::importXML() failed for node named '" + << node->getName() << "'" << llendl; + } + } + return success; +} + +bool LLPermissions::operator==(const LLPermissions &rhs) const +{ + return + (mCreator == rhs.mCreator) && + (mOwner == rhs.mOwner) && + (mLastOwner == rhs.mLastOwner ) && + (mGroup == rhs.mGroup ) && + (mMaskBase == rhs.mMaskBase ) && + (mMaskOwner == rhs.mMaskOwner ) && + (mMaskGroup == rhs.mMaskGroup ) && + (mMaskEveryone == rhs.mMaskEveryone ) && + (mMaskNextOwner == rhs.mMaskNextOwner ) && + (mIsGroupOwned == rhs.mIsGroupOwned); +} + + +bool LLPermissions::operator!=(const LLPermissions &rhs) const +{ + return + (mCreator != rhs.mCreator) || + (mOwner != rhs.mOwner) || + (mLastOwner != rhs.mLastOwner ) || + (mGroup != rhs.mGroup ) || + (mMaskBase != rhs.mMaskBase ) || + (mMaskOwner != rhs.mMaskOwner ) || + (mMaskGroup != rhs.mMaskGroup ) || + (mMaskEveryone != rhs.mMaskEveryone ) || + (mMaskNextOwner != rhs.mMaskNextOwner) || + (mIsGroupOwned != rhs.mIsGroupOwned); +} + +std::ostream& operator<<(std::ostream &s, const LLPermissions &perm) +{ + s << "{Creator=" << perm.getCreator(); + s << ", Owner=" << perm.getOwner(); + s << ", Group=" << perm.getGroup(); + s << std::hex << ", BaseMask=0x" << perm.getMaskBase(); + s << ", OwnerMask=0x" << perm.getMaskOwner(); + s << ", EveryoneMask=0x" << perm.getMaskEveryone(); + s << ", GroupMask=0x" << perm.getMaskGroup(); + s << ", NextOwnerMask=0x" << perm.getMaskNextOwner() << std::dec; + s << "}"; + return s; +} + +template <> +void LLMetaClassT<LLPermissions>::reflectProperties(LLMetaClass& meta_class) +{ + reflectProperty(meta_class, "mCreator", &LLPermissions::mCreator); + reflectProperty(meta_class, "mOwner", &LLPermissions::mOwner); +} + +// virtual +const LLMetaClass& LLPermissions::getMetaClass() const +{ + return LLMetaClassT<LLPermissions>::instance(); +} + +///---------------------------------------------------------------------------- +/// Class LLAggregatePermissions +///---------------------------------------------------------------------------- + +const LLAggregatePermissions LLAggregatePermissions::empty; + + +LLAggregatePermissions::LLAggregatePermissions() +{ + for(S32 i = 0; i < PI_COUNT; ++i) + { + mBits[i] = AP_EMPTY; + } +} + +LLAggregatePermissions::EValue LLAggregatePermissions::getValue(PermissionBit bit) const +{ + EPermIndex idx = perm2PermIndex(bit); + EValue rv = AP_EMPTY; + if(idx != PI_END) + { + rv = (LLAggregatePermissions::EValue)(mBits[idx]); + } + return rv; +} + +// returns the bits compressed into a single byte: 00TTMMCC +// where TT = transfer, MM = modify, and CC = copy +// LSB is to the right +U8 LLAggregatePermissions::getU8() const +{ + U8 byte = mBits[PI_TRANSFER]; + byte <<= 2; + byte |= mBits[PI_MODIFY]; + byte <<= 2; + byte |= mBits[PI_COPY]; + return byte; +} + +BOOL LLAggregatePermissions::isEmpty() const +{ + for(S32 i = 0; i < PI_END; ++i) + { + if(mBits[i] != AP_EMPTY) + { + return FALSE; + } + } + return TRUE; +} + +void LLAggregatePermissions::aggregate(PermissionMask mask) +{ + BOOL is_allowed = mask & PERM_COPY; + aggregateBit(PI_COPY, is_allowed); + is_allowed = mask & PERM_MODIFY; + aggregateBit(PI_MODIFY, is_allowed); + is_allowed = mask & PERM_TRANSFER; + aggregateBit(PI_TRANSFER, is_allowed); +} + +void LLAggregatePermissions::aggregate(const LLAggregatePermissions& ag) +{ + for(S32 idx = PI_COPY; idx != PI_END; ++idx) + { + aggregateIndex((EPermIndex)idx, ag.mBits[idx]); + } +} + +void LLAggregatePermissions::aggregateBit(EPermIndex idx, BOOL allowed) +{ + //if(AP_SOME == mBits[idx]) return; // P4 branch prediction optimization + switch(mBits[idx]) + { + case AP_EMPTY: + mBits[idx] = allowed ? AP_ALL : AP_NONE; + break; + case AP_NONE: + mBits[idx] = allowed ? AP_SOME: AP_NONE; + break; + case AP_SOME: + // no-op + break; + case AP_ALL: + mBits[idx] = allowed ? AP_ALL : AP_SOME; + break; + default: + llwarns << "Bad aggregateBit " << (S32)idx << " " + << (allowed ? "true" : "false") << llendl; + break; + } +} + +void LLAggregatePermissions::aggregateIndex(EPermIndex idx, U8 bits) +{ + switch(mBits[idx]) + { + case AP_EMPTY: + mBits[idx] = bits; + break; + case AP_NONE: + switch(bits) + { + case AP_SOME: + case AP_ALL: + mBits[idx] = AP_SOME; + break; + case AP_EMPTY: + case AP_NONE: + default: + // no-op + break; + } + break; + case AP_SOME: + // no-op + break; + case AP_ALL: + switch(bits) + { + case AP_NONE: + case AP_SOME: + mBits[idx] = AP_SOME; + break; + case AP_EMPTY: + case AP_ALL: + default: + // no-op + break; + } + break; + default: + llwarns << "Bad aggregate index " << (S32)idx << " " + << (S32)bits << llendl; + break; + } +} + +// static +LLAggregatePermissions::EPermIndex LLAggregatePermissions::perm2PermIndex(PermissionBit bit) +{ + EPermIndex idx = PI_END; // past any good value. + switch(bit) + { + case PERM_COPY: + idx = PI_COPY; + break; + case PERM_MODIFY: + idx = PI_MODIFY; + break; + case PERM_TRANSFER: + idx = PI_TRANSFER; + break; + default: + break; + } + return idx; +} + + +void LLAggregatePermissions::packMessage(LLMessageSystem* msg, const char* field) const +{ + msg->addU8Fast(field, getU8()); +} + +void LLAggregatePermissions::unpackMessage(LLMessageSystem* msg, const char* block, const char* field, S32 block_num) +{ + const U8 TWO_BITS = 0x3; // binary 00000011 + U8 bits = 0; + msg->getU8Fast(block, field, bits, block_num); + mBits[PI_COPY] = bits & TWO_BITS; + bits >>= 2; + mBits[PI_MODIFY] = bits & TWO_BITS; + bits >>= 2; + mBits[PI_TRANSFER] = bits & TWO_BITS; +} + +const LLString AGGREGATE_VALUES[4] = + { + LLString( "Empty" ), + LLString( "None" ), + LLString( "Some" ), + LLString( "All" ) + }; + +std::ostream& operator<<(std::ostream &s, const LLAggregatePermissions &perm) +{ + s << "{PI_COPY=" << AGGREGATE_VALUES[perm.mBits[LLAggregatePermissions::PI_COPY]]; + s << ", PI_MODIFY=" << AGGREGATE_VALUES[perm.mBits[LLAggregatePermissions::PI_MODIFY]]; + s << ", PI_TRANSFER=" << AGGREGATE_VALUES[perm.mBits[LLAggregatePermissions::PI_TRANSFER]]; + s << "}"; + return s; +} + +// This converts a permissions mask into a string for debugging use. +void mask_to_string(U32 mask, char* str) +{ + if (mask & PERM_MOVE) + { + *str = 'V'; + } + else + { + *str = ' '; + } + str++; + + if (mask & PERM_MODIFY) + { + *str = 'M'; + } + else + { + *str = ' '; + } + str++; + + if (mask & PERM_COPY) + { + *str = 'C'; + } + else + { + *str = ' '; + } + str++; + + if (mask & PERM_TRANSFER) + { + *str = 'T'; + } + else + { + *str = ' '; + } + str++; + *str = '\0'; +} + + +///---------------------------------------------------------------------------- +/// exported functions +///---------------------------------------------------------------------------- +static const std::string PERM_CREATOR_ID_LABEL("creator_id"); +static const std::string PERM_OWNER_ID_LABEL("owner_id"); +static const std::string PERM_LAST_OWNER_ID_LABEL("last_owner_id"); +static const std::string PERM_GROUP_ID_LABEL("group_id"); +static const std::string PERM_IS_OWNER_GROUP_LABEL("is_owner_group"); +static const std::string PERM_BASE_MASK_LABEL("base_mask"); +static const std::string PERM_OWNER_MASK_LABEL("owner_mask"); +static const std::string PERM_GROUP_MASK_LABEL("group_mask"); +static const std::string PERM_EVERYONE_MASK_LABEL("everyone_mask"); +static const std::string PERM_NEXT_OWNER_MASK_LABEL("next_owner_mask"); + +LLSD ll_create_sd_from_permissions(const LLPermissions& perm) +{ + LLSD rv; + rv[PERM_CREATOR_ID_LABEL] = perm.getCreator(); + rv[PERM_OWNER_ID_LABEL] = perm.getOwner(); + rv[PERM_LAST_OWNER_ID_LABEL] = perm.getLastOwner(); + rv[PERM_GROUP_ID_LABEL] = perm.getGroup(); + rv[PERM_IS_OWNER_GROUP_LABEL] = perm.isGroupOwned(); + rv[PERM_BASE_MASK_LABEL] = (S32)perm.getMaskBase(); + rv[PERM_OWNER_MASK_LABEL] = (S32)perm.getMaskOwner(); + rv[PERM_GROUP_MASK_LABEL] = (S32)perm.getMaskGroup(); + rv[PERM_EVERYONE_MASK_LABEL] = (S32)perm.getMaskEveryone(); + rv[PERM_NEXT_OWNER_MASK_LABEL] = (S32)perm.getMaskNextOwner(); + return rv; +} + +LLPermissions ll_permissions_from_sd(const LLSD& sd_perm) +{ + LLPermissions rv; + rv.init( + sd_perm[PERM_CREATOR_ID_LABEL].asUUID(), + sd_perm[PERM_OWNER_ID_LABEL].asUUID(), + sd_perm[PERM_LAST_OWNER_ID_LABEL].asUUID(), + sd_perm[PERM_GROUP_ID_LABEL].asUUID()); + + // We do a cast to U32 here since LLSD does not attempt to + // represent unsigned ints. + PermissionMask mask; + mask = (U32)(sd_perm[PERM_BASE_MASK_LABEL].asInteger()); + rv.setMaskBase(mask); + mask = (U32)(sd_perm[PERM_OWNER_MASK_LABEL].asInteger()); + rv.setMaskOwner(mask); + mask = (U32)(sd_perm[PERM_EVERYONE_MASK_LABEL].asInteger()); + rv.setMaskEveryone(mask); + mask = (U32)(sd_perm[PERM_GROUP_MASK_LABEL].asInteger()); + rv.setMaskGroup(mask); + mask = (U32)(sd_perm[PERM_NEXT_OWNER_MASK_LABEL].asInteger()); + rv.setMaskNext(mask); + rv.fix(); + return rv; +} diff --git a/indra/llinventory/llpermissions.h b/indra/llinventory/llpermissions.h new file mode 100644 index 0000000000..76794e1ed9 --- /dev/null +++ b/indra/llinventory/llpermissions.h @@ -0,0 +1,426 @@ +/** + * @file llpermissions.h + * @brief Permissions structures for objects. + * + * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LL_LLPERMISSIONS_H +#define LL_LLPERMISSIONS_H + +#include <stdio.h> +#include <iostream> + +#include "llpermissionsflags.h" +#include "llsd.h" +#include "lluuid.h" +#include "llxmlnode.h" +#include "reflective.h" + +// prototypes +class LLMessageSystem; +extern void mask_to_string(U32 mask, char* str); +template<class T> class LLMetaClassT; + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLPermissions +// +// Class which encapsulates object and inventory permissions/ownership/etc. +// +// Permissions where originally a static state creator/owner and set +// of cap bits. Since then, it has grown to include group information, +// last owner, masks for different people. The implementation has been +// chosen such that a uuid is stored for each current/past owner, and +// a bitmask is stored for the base permissions, owner permissions, +// group permissions, and everyone else permissions. +// +// The base permissions represent the most permissive state that the +// permissions can possibly be in. Thus, if the base permissions do +// not allow copying, no one can ever copy the object. The permissions +// also maintain a tree-like hierarchy of permissions, thus, if we +// (for sake of discussions) denote more permissive as '>', then this +// is invariant: +// +// base mask >= owner mask >= group mask +// >= everyone mask +// >= next owner mask +// NOTE: the group mask does not effect everyone or next, everyone +// does not effect group or next, etc. +// +// It is considered a fair use right to move or delete any object you +// own. Another fair use right is the ability to give away anything +// which you cannot copy. One way to look at that is that if you have +// a unique item, you can always give that one copy you have to +// someone else. +// +// Most of the bitmask is easy to understand, PERM_COPY means you can +// copy !PERM_TRANSFER means you cannot transfer, etc. Given that we +// now track the concept of 'next owner' inside of the permissions +// object, we can describe some new meta-meaning to the PERM_MODIFY +// flag. PERM_MODIFY is usually meant to note if you can change an +// item, but since we record next owner permissions, we can interpret +// a no-modify object as 'you cannot modify this object and you cannot +// make derivative works.' When evaluating functionality, and +// comparisons against permissions, keep this concept in mind for +// logical consistency. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLPermissions : public LLReflective +{ +private: + LLUUID mCreator; // null if object created by system + LLUUID mOwner; // null if object "unowned" (owned by system) + LLUUID mLastOwner; // object's last owner + LLUUID mGroup; // The group association + + PermissionMask mMaskBase; // initially permissive, progressively AND restricted by each owner + + PermissionMask mMaskOwner; // set by owner, applies to owner only, restricts lower permissions + PermissionMask mMaskEveryone; // set by owner, applies to everyone else + + PermissionMask mMaskGroup; // set by owner, applies to group that is associated with permissions + + PermissionMask mMaskNextOwner; // set by owner, applied to base on transfer. + + // Usually set in the fixOwnership() method based on current uuid + // values. + bool mIsGroupOwned; + + // Correct for fair use - you can never take away the right to + // move stuff you own, and you can never take away the right to + // transfer something you cannot otherwise copy. + void fixFairUse(); + + // Fix internal consistency for group/agent ownership + void fixOwnership(); + +public: + static const LLPermissions DEFAULT; + + LLPermissions(); // defaults to created by system + //~LLPermissions(); + + // base initialization code + void init(const LLUUID& creator, const LLUUID& owner, + const LLUUID& last_owner, const LLUUID& group); + void initMasks(PermissionMask base, PermissionMask owner, + PermissionMask everyone, PermissionMask group, + PermissionMask next); + + // + // ACCESSORS + // + + // return the agent_id of the agent that created the item + const LLUUID& getCreator() const { return mCreator; } + + // return the agent_id of the owner. returns LLUUID::null if group + // owned or public (a really big group). + const LLUUID& getOwner() const { return mOwner; } + + // return the group_id of the group associated with the + // object. group_id == owner_id if the object is group owned. + const LLUUID& getGroup() const { return mGroup; } + + // return the agent_id of the last agent owner. Only returns + // LLUUID::null if there has never been a previous owner. + const LLUUID& getLastOwner() const { return mLastOwner; } + + U32 getMaskBase() const { return mMaskBase; } + U32 getMaskOwner() const { return mMaskOwner; } + U32 getMaskGroup() const { return mMaskGroup; } + U32 getMaskEveryone() const { return mMaskEveryone; } + U32 getMaskNextOwner() const { return mMaskNextOwner; } + + // return TRUE if the object has any owner + bool isOwned() const { return (mOwner.notNull() || mIsGroupOwned); } + + // return TRUE if group_id is owner. + bool isGroupOwned() const { return mIsGroupOwned; } + + // This API returns TRUE if the object is owned at all, and FALSE + // otherwise. If it is owned at all, owner id is filled with + // either the owner id or the group id, and the is_group_owned + // parameter is appropriately filled. The values of owner_id and + // is_group_owned are not changed if the object is not owned. + BOOL getOwnership(LLUUID& owner_id, BOOL& is_group_owned) const; + + // Gets the 'safe' owner. This should never return LLUUID::null. + // If no group owned, return the agent owner id normally. + // If group owned, return the group id. + // If not owned, return a random uuid which should have no power. + LLUUID getSafeOwner() const; + + // return a cheap crc + U32 getCRC32() const; + + + // + // MANIPULATORS + // + + // Fix hierarchy of permissions, applies appropriate permissions + // at each level to ensure that base permissions are respected, + // and also ensures that if base cannot transfer, then group and + // other cannot copy. + void fix(); + + // All of these methods just do exactly what they say. There is no + // permissions checking to see if the operation is allowed, and do + // not fix the permissions hierarchy. So please only use these + // methods when you are know what you're doing and coding on + // behalf of the system - ie, acting as god. + void set(const LLPermissions& permissions); + void setMaskBase(U32 mask) { mMaskBase = mask; } + void setMaskOwner(U32 mask) { mMaskOwner = mask; } + void setMaskEveryone(U32 mask) { mMaskEveryone = mask;} + void setMaskGroup(U32 mask) { mMaskGroup = mask;} + void setMaskNext(U32 mask) { mMaskNextOwner = mask; } + + // Allow accumulation of permissions. Results in the tightest + // permissions possible. In the case of clashing UUIDs, it sets + // the ID to LLUUID::null. + void accumulate(const LLPermissions& perm); + + // + // CHECKED MANIPULATORS + // + + // These functions return true on success. They return false if + // the given agent isn't allowed to make the change. You can pass + // LLUUID::null as the agent id if the change is being made by the + // simulator itself, not on behalf of any agent - this will always + // succeed. Passing in group id of LLUUID:null means no group, and + // does not offer special permission to do anything. + + // saves last owner, sets current owner, and sets the group. + // set is_atomic = true means that this permission represents + // an atomic permission and not a collection of permissions. + // Currently, the only way to have a collection is when an object + // has inventory and is then itself rolled up into an inventory + // item. + BOOL setOwnerAndGroup(const LLUUID& agent, const LLUUID& owner, const LLUUID& group, bool is_atomic); + + // saves last owner, sets owner to uuid null, sets group + // owned. group_id must be the group of the object (that's who it + // is being deeded to) and the object must be group + // modify. Technically, the agent id and group id are not + // necessary, but I wanted this function to look like the other + // checked manipulators (since that is how it is used.) If the + // agent is the system or (group == mGroup and group modify and + // owner transfer) then this function will deed the permissions, + // set the next owner mask, and return TRUE. Otherwise, no change + // is effected, and the function returns FALSE. + BOOL deedToGroup(const LLUUID& agent, const LLUUID& group); + // Attempt to set or clear the given bitmask. Returns TRUE if you + // are allowed to modify the permissions. If you attempt to turn + // on bits not allowed by the base bits, the function will return + // TRUE, but those bits will not be set. + BOOL setBaseBits( const LLUUID& agent, BOOL set, PermissionMask bits); + BOOL setOwnerBits( const LLUUID& agent, BOOL set, PermissionMask bits); + BOOL setGroupBits( const LLUUID& agent, const LLUUID& group, BOOL set, PermissionMask bits); + BOOL setEveryoneBits(const LLUUID& agent, const LLUUID& group, BOOL set, PermissionMask bits); + BOOL setNextOwnerBits(const LLUUID& agent, const LLUUID& group, BOOL set, PermissionMask bits); + + // + // METHODS + // + + // All the allow* functions return true if the given agent or + // group can perform the function. Prefer using this set of + // operations to check permissions on an object. These return + // true if the given agent or group can perform the function. + // They also return true if the object isn't owned, or the + // requesting agent is a system agent. See llpermissionsflags.h + // for bits. + BOOL allowOperationBy(PermissionBit op, const LLUUID& agent, const LLUUID& group = LLUUID::null) const; + + inline BOOL allowModifyBy(const LLUUID &agent_id) const; + inline BOOL allowCopyBy(const LLUUID& agent_id) const; + inline BOOL allowMoveBy(const LLUUID& agent_id) const; + inline BOOL allowModifyBy(const LLUUID &agent_id, const LLUUID& group) const; + inline BOOL allowCopyBy(const LLUUID& agent_id, const LLUUID& group) const; + inline BOOL allowMoveBy(const LLUUID &agent_id, const LLUUID &group) const; + + // This somewhat specialized function is meant for testing if the + // current owner is allowed to transfer to the specified agent id. + inline BOOL allowTransferTo(const LLUUID &agent_id) const; + + // + // DEPRECATED. + // + // These return true if the given agent can perform the function. + // They also return true if the object isn't owned, or the + // requesting agent is a system agent. See llpermissionsflags.h + // for bits. + //BOOL allowDeleteBy(const LLUUID& agent_id) const { return allowModifyBy(agent_id); } + //BOOL allowEditBy(const LLUUID& agent_id) const { return allowModifyBy(agent_id); } + // saves last owner and sets current owner + //BOOL setOwner(const LLUUID& agent, const LLUUID& owner); + // This method saves the last owner, sets the current owner to the + // one provided, and sets the base mask as indicated. + //BOOL setOwner(const LLUUID& agent, const LLUUID& owner, U32 new_base_mask); + + // Attempt to set or clear the given bitmask. Returns TRUE if you + // are allowed to modify the permissions. If you attempt to turn + // on bits not allowed by the base bits, the function will return + // TRUE, but those bits will not be set. + //BOOL setGroupBits( const LLUUID& agent, BOOL set, PermissionMask bits); + //BOOL setEveryoneBits(const LLUUID& agent, BOOL set, PermissionMask bits); + + // + // MISC METHODS and OPERATORS + // + + // For messaging system support + void packMessage(LLMessageSystem* msg) const; + void unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num = 0); + + // Load/save support + BOOL importFile(FILE* fp); + BOOL exportFile(FILE* fp) const; + + BOOL importLegacyStream(std::istream& input_stream); + BOOL exportLegacyStream(std::ostream& output_stream) const; + + LLXMLNode *exportFileXML() const; + bool importXML(LLXMLNode* node); + + bool operator==(const LLPermissions &rhs) const; + bool operator!=(const LLPermissions &rhs) const; + + friend std::ostream& operator<<(std::ostream &s, const LLPermissions &perm); + + // Reflection. + friend class LLMetaClassT<LLPermissions>; + virtual const LLMetaClass& getMetaClass() const; +}; + +// Inlines +BOOL LLPermissions::allowModifyBy(const LLUUID& agent, const LLUUID& group) const +{ + return allowOperationBy(PERM_MODIFY, agent, group); +} + +BOOL LLPermissions::allowCopyBy(const LLUUID& agent, const LLUUID& group) const +{ + return allowOperationBy(PERM_COPY, agent, group); +} + + +BOOL LLPermissions::allowMoveBy(const LLUUID& agent, const LLUUID& group) const +{ + return allowOperationBy(PERM_MOVE, agent, group); +} + +BOOL LLPermissions::allowModifyBy(const LLUUID& agent) const +{ + return allowOperationBy(PERM_MODIFY, agent, LLUUID::null); +} + +BOOL LLPermissions::allowCopyBy(const LLUUID& agent) const +{ + return allowOperationBy(PERM_COPY, agent, LLUUID::null); +} + +BOOL LLPermissions::allowMoveBy(const LLUUID& agent) const +{ + return allowOperationBy(PERM_MOVE, agent, LLUUID::null); +} + +BOOL LLPermissions::allowTransferTo(const LLUUID &agent_id) const +{ + if (mIsGroupOwned) + { + return allowOperationBy(PERM_TRANSFER, mGroup, mGroup); + } + else + { + return ((mOwner == agent_id) ? TRUE : allowOperationBy(PERM_TRANSFER, mOwner)); + } +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLAggregatePermissions +// +// Class which encapsulates object and inventory permissions, +// ownership, etc. Currently, it only aggregates PERM_COPY, +// PERM_MODIFY, and PERM_TRANSFER. +// +// Usually you will construct an instance and hand the object several +// permissions masks to aggregate the copy, modify, and +// transferability into a nice trinary value. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLAggregatePermissions +{ +public: + enum EValue + { + AP_EMPTY = 0x00, + AP_NONE = 0x01, + AP_SOME = 0x02, + AP_ALL = 0x03 + }; + + // construct an empty aggregate permissions + LLAggregatePermissions(); + + // pass in a PERM_COPY, PERM_TRANSFER, etc, and get out a EValue + // enumeration describing the current aggregate permissions. + EValue getValue(PermissionBit bit) const; + + // returns the permissions packed into the 6 LSB of a U8: + // 00TTMMCC + // where TT = transfer, MM = modify, and CC = copy + // LSB is to the right + U8 getU8() const; + + // return TRUE is the aggregate permissions are empty, otherwise FALSE. + BOOL isEmpty() const ; + + // pass in a PERM_COPY, PERM_TRANSFER, etc, and an EValue + // enumeration to specifically set that value. Not implemented + // because I'm not sure it's a useful api. + //void setValue(PermissionBit bit, EValue); + + // Given a mask, aggregate the useful permissions. + void aggregate(PermissionMask mask); + + // Aggregate aggregates + void aggregate(const LLAggregatePermissions& ag); + + // message handling + void packMessage(LLMessageSystem* msg, const char* field) const; + void unpackMessage(LLMessageSystem* msg, const char* block, const char *field, S32 block_num = 0); + + static const LLAggregatePermissions empty; + + friend std::ostream& operator<<(std::ostream &s, const LLAggregatePermissions &perm); + +protected: + enum EPermIndex + { + PI_COPY = 0, + PI_MODIFY = 1, + PI_TRANSFER = 2, + PI_END = 3, + PI_COUNT = 3 + }; + void aggregateBit(EPermIndex idx, BOOL allowed); + void aggregateIndex(EPermIndex idx, U8 bits); + static EPermIndex perm2PermIndex(PermissionBit bit); + + // structure used to store the aggregate so far. + U8 mBits[PI_COUNT]; +}; + +// These functions convert between structured data and permissions as +// appropriate for serialization. The permissions are a map of things +// like 'creator_id', 'owner_id', etc, with the value copied from the +// permission object. +LLSD ll_create_sd_from_permissions(const LLPermissions& perm); +LLPermissions ll_permissions_from_sd(const LLSD& sd_perm); + +#endif diff --git a/indra/llinventory/llpermissionsflags.h b/indra/llinventory/llpermissionsflags.h new file mode 100644 index 0000000000..f45758c501 --- /dev/null +++ b/indra/llinventory/llpermissionsflags.h @@ -0,0 +1,78 @@ +/** + * @file llpermissionsflags.h + * + * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LL_LLPERMISSIONSFLAGS_H +#define LL_LLPERMISSIONSFLAGS_H + +// llpermissionsflags.h +// Copyright 2002, Linden Research, Inc. +// +// Flags for various permissions bits. +// Shared between viewer and simulator. + +// permission bits +typedef U32 PermissionMask; +typedef U32 PermissionBit; + + +// Do you have permission to transfer ownership of the object or +// item. Fair use rules dictate that if you cannot copy, you can +// always transfer. +const PermissionBit PERM_TRANSFER = (1 << 13); // 0x00002000 + +// objects, scale or change textures +// parcels, allow building on it +const PermissionBit PERM_MODIFY = (1 << 14); // 0x00004000 + +// objects, allow copy +const PermissionBit PERM_COPY = (1 << 15); // 0x00008000 + +// parcels, allow entry, deprecated +//const PermissionBit PERM_ENTER = (1 << 16); // 0x00010000 + +// parcels, allow terraform, deprecated +//const PermissionBit PERM_TERRAFORM = (1 << 17); // 0x00020000 + +// NOTA BENE: This flag is NO LONGER USED!!! However, it is possible that some +// objects in the universe have it set so DON"T USE IT going forward. +//const PermissionBit PERM_OWNER_DEBIT = (1 << 18); // 0x00040000 + +// objects, can grab/translate/rotate +const PermissionBit PERM_MOVE = (1 << 19); // 0x00080000 + +// parcels, avatars take damage, deprecated +//const PermissionBit PERM_DAMAGE = (1 << 20); // 0x00100000 + +// don't use bit 31 -- printf/scanf with "%x" assume signed numbers +const PermissionBit PERM_RESERVED = ((U32)1) << 31; + +const PermissionMask PERM_NONE = 0x00000000; +const PermissionMask PERM_ALL = 0x7FFFFFFF; +//const PermissionMask PERM_ALL_PARCEL = PERM_MODIFY | PERM_ENTER | PERM_TERRAFORM | PERM_DAMAGE; +const PermissionMask PERM_ITEM_UNRESTRICTED = PERM_MODIFY | PERM_COPY | PERM_TRANSFER; + + +// Useful stuff for transmission. +// Which permissions field are we trying to change? +const U8 PERM_BASE = 0x01; +// TODO: Add another PERM_OWNER operation type for allowOperationBy DK 04/03/06 +const U8 PERM_OWNER = 0x02; +const U8 PERM_GROUP = 0x04; +const U8 PERM_EVERYONE = 0x08; +const U8 PERM_NEXT_OWNER = 0x10; + +// This is just a quickie debugging key +// no modify: PERM_ALL & ~PERM_MODIFY = 0x7fffbfff +// no copy: PERM_ALL & ~PERM_COPY = 0x7fff7fff +// no modify or copy: = 0x7fff3fff +// no transfer: PERM_ALL & ~PERM_TRANSFER = 0x7fffdfff +// no modify, no transfer = 0x7fff9fff +// no copy, no transfer (INVALID!) = 0x7fff5fff +// no modify, no copy, no transfer (INVALID!) = 0x7fff1fff + + +#endif diff --git a/indra/llinventory/llsaleinfo.cpp b/indra/llinventory/llsaleinfo.cpp new file mode 100644 index 0000000000..7e2b293d42 --- /dev/null +++ b/indra/llinventory/llsaleinfo.cpp @@ -0,0 +1,356 @@ +/** + * @file llsaleinfo.cpp + * @brief + * + * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include <iostream> +#include "linden_common.h" + +#include "llsaleinfo.h" + +#include "llerror.h" +#include "message.h" +#include "llsdutil.h" + +// use this to avoid temporary object creation +const LLSaleInfo LLSaleInfo::DEFAULT; + +///---------------------------------------------------------------------------- +/// Local function declarations, constants, enums, and typedefs +///---------------------------------------------------------------------------- + +const char* FOR_SALE_NAMES[] = +{ + "not", + "orig", + "copy", + "cntn" +}; + +///---------------------------------------------------------------------------- +/// Class llsaleinfo +///---------------------------------------------------------------------------- + +// Default constructor +LLSaleInfo::LLSaleInfo() : + mSaleType(LLSaleInfo::FS_NOT), + mSalePrice(DEFAULT_PRICE) +{ +} + +LLSaleInfo::LLSaleInfo(EForSale sale_type, S32 sale_price) : + mSaleType(sale_type), + mSalePrice(sale_price) +{ + mSalePrice = llclamp(mSalePrice, 0, S32_MAX); +} + +BOOL LLSaleInfo::isForSale() const +{ + return (FS_NOT != mSaleType); +} + +U32 LLSaleInfo::getCRC32() const +{ + U32 rv = (U32)mSalePrice; + rv += (mSaleType * 0x07073096); + return rv; +} + + +BOOL LLSaleInfo::exportFile(FILE* fp) const +{ + fprintf(fp, "\tsale_info\t0\n\t{\n"); + fprintf(fp, "\t\tsale_type\t%s\n", lookup(mSaleType)); + fprintf(fp, "\t\tsale_price\t%d\n", mSalePrice); + fprintf(fp,"\t}\n"); + return TRUE; +} + +BOOL LLSaleInfo::exportLegacyStream(std::ostream& output_stream) const +{ + output_stream << "\tsale_info\t0\n\t{\n"; + output_stream << "\t\tsale_type\t" << lookup(mSaleType) << "\n"; + output_stream << "\t\tsale_price\t" << mSalePrice << "\n"; + output_stream <<"\t}\n"; + return TRUE; +} + +LLSD LLSaleInfo::asLLSD() const +{ + LLSD sd = LLSD(); + sd["sale_type"] = lookup(mSaleType); + sd["sale_price"] = mSalePrice; + return sd; +} + +bool LLSaleInfo::fromLLSD(LLSD& sd, BOOL& has_perm_mask, U32& perm_mask) +{ + const char *w; + + mSaleType = lookup(sd["sale_type"].asString().c_str()); + mSalePrice = llclamp(sd["sale_price"].asInteger(), 0, S32_MAX); + w = "perm_mask"; + if (sd.has(w)) + { + has_perm_mask = TRUE; + perm_mask = ll_U32_from_sd(sd[w]); + } + return true; +} + +LLXMLNode *LLSaleInfo::exportFileXML() const +{ + LLXMLNode *ret = new LLXMLNode("sale_info", FALSE); + LLString type_str = lookup(mSaleType); + ret->createChild("type", TRUE)->setStringValue(1, &type_str); + ret->createChild("price", TRUE)->setIntValue(1, &mSalePrice); + return ret; +} + +BOOL LLSaleInfo::importXML(LLXMLNode* node) +{ + BOOL success = FALSE; + if (node) + { + success = TRUE; + LLXMLNodePtr sub_node; + if (node->getChild("type", sub_node)) + { + mSaleType = lookup(sub_node->getValue().c_str()); + } + if (node->getChild("price", sub_node)) + { + success &= (1 == sub_node->getIntValue(1, &mSalePrice)); + } + if (!success) + { + lldebugs << "LLSaleInfo::importXML() failed for node named '" + << node->getName() << "'" << llendl; + } + } + return success; +} + +BOOL LLSaleInfo::importFile(FILE* fp, BOOL& has_perm_mask, U32& perm_mask) +{ + has_perm_mask = FALSE; + + char buffer[MAX_STRING]; + char keyword[MAX_STRING]; + char valuestr[MAX_STRING]; + BOOL success = TRUE; + + keyword[0] = '\0'; + valuestr[0] = '\0'; + while(success && (!feof(fp))) + { + fgets(buffer, MAX_STRING, fp); + sscanf(buffer, " %s %s", keyword, valuestr); + if(!keyword) + { + continue; + } + if(0 == strcmp("{",keyword)) + { + continue; + } + if(0 == strcmp("}", keyword)) + { + break; + } + else if(0 == strcmp("sale_type", keyword)) + { + mSaleType = lookup(valuestr); + } + else if(0 == strcmp("sale_price", keyword)) + { + sscanf(valuestr, "%d", &mSalePrice); + mSalePrice = llclamp(mSalePrice, 0, S32_MAX); + } + else if (!strcmp("perm_mask", keyword)) + { + //llinfos << "found deprecated keyword perm_mask" << llendl; + has_perm_mask = TRUE; + sscanf(valuestr, "%x", &perm_mask); + } + else + { + llwarns << "unknown keyword '" << keyword + << "' in sale info import" << llendl; + } + } + return success; +} + +BOOL LLSaleInfo::importLegacyStream(std::istream& input_stream, BOOL& has_perm_mask, U32& perm_mask) +{ + has_perm_mask = FALSE; + + char buffer[MAX_STRING]; + char keyword[MAX_STRING]; + char valuestr[MAX_STRING]; + BOOL success = TRUE; + + keyword[0] = '\0'; + valuestr[0] = '\0'; + while(success && input_stream.good()) + { + input_stream.getline(buffer, MAX_STRING); + sscanf(buffer, " %s %s", keyword, valuestr); + if(!keyword) + { + continue; + } + if(0 == strcmp("{",keyword)) + { + continue; + } + if(0 == strcmp("}", keyword)) + { + break; + } + else if(0 == strcmp("sale_type", keyword)) + { + mSaleType = lookup(valuestr); + } + else if(0 == strcmp("sale_price", keyword)) + { + sscanf(valuestr, "%d", &mSalePrice); + mSalePrice = llclamp(mSalePrice, 0, S32_MAX); + } + else if (!strcmp("perm_mask", keyword)) + { + //llinfos << "found deprecated keyword perm_mask" << llendl; + has_perm_mask = TRUE; + sscanf(valuestr, "%x", &perm_mask); + } + else + { + llwarns << "unknown keyword '" << keyword + << "' in sale info import" << llendl; + } + } + return success; +} + +void LLSaleInfo::setSalePrice(S32 price) +{ + mSalePrice = price; + mSalePrice = llclamp(mSalePrice, 0, S32_MAX); +} + +void LLSaleInfo::packMessage(LLMessageSystem* msg) const +{ + U8 sale_type = static_cast<U8>(mSaleType); + msg->addU8Fast(_PREHASH_SaleType, sale_type); + msg->addS32Fast(_PREHASH_SalePrice, mSalePrice); + //msg->addU32Fast(_PREHASH_NextOwnerMask, mNextOwnerPermMask); +} + +void LLSaleInfo::unpackMessage(LLMessageSystem* msg, const char* block) +{ + U8 sale_type; + msg->getU8Fast(block, _PREHASH_SaleType, sale_type); + mSaleType = static_cast<EForSale>(sale_type); + msg->getS32Fast(block, _PREHASH_SalePrice, mSalePrice); + mSalePrice = llclamp(mSalePrice, 0, S32_MAX); + //msg->getU32Fast(block, _PREHASH_NextOwnerMask, mNextOwnerPermMask); +} + +void LLSaleInfo::unpackMultiMessage(LLMessageSystem* msg, const char* block, + S32 block_num) +{ + U8 sale_type; + msg->getU8Fast(block, _PREHASH_SaleType, sale_type, block_num); + mSaleType = static_cast<EForSale>(sale_type); + msg->getS32Fast(block, _PREHASH_SalePrice, mSalePrice, block_num); + mSalePrice = llclamp(mSalePrice, 0, S32_MAX); + //msg->getU32Fast(block, _PREHASH_NextOwnerMask, mNextOwnerPermMask, block_num); +} + +LLSaleInfo::EForSale LLSaleInfo::lookup(const char* name) +{ + for(S32 i = 0; i < FS_COUNT; i++) + { + if(0 == strcmp(name, FOR_SALE_NAMES[i])) + { + // match + return (EForSale)i; + } + } + return FS_NOT; +} + +const char* LLSaleInfo::lookup(EForSale type) +{ + if((type >= 0) && (type < FS_COUNT)) + { + return FOR_SALE_NAMES[S32(type)]; + } + else + { + return NULL; + } +} + +// Allow accumulation of sale info. The price of each is added, +// conflict in sale type results in FS_NOT, and the permissions are +// tightened. +void LLSaleInfo::accumulate(const LLSaleInfo& sale_info) +{ + if(mSaleType != sale_info.mSaleType) + { + mSaleType = FS_NOT; + } + mSalePrice += sale_info.mSalePrice; + //mNextOwnerPermMask &= sale_info.mNextOwnerPermMask; +} + +bool LLSaleInfo::operator==(const LLSaleInfo &rhs) const +{ + return ( + (mSaleType == rhs.mSaleType) && + (mSalePrice == rhs.mSalePrice) + ); +} + +bool LLSaleInfo::operator!=(const LLSaleInfo &rhs) const +{ + return ( + (mSaleType != rhs.mSaleType) || + (mSalePrice != rhs.mSalePrice) + ); +} + + +///---------------------------------------------------------------------------- +/// Local function definitions +///---------------------------------------------------------------------------- + +///---------------------------------------------------------------------------- +/// exported functions +///---------------------------------------------------------------------------- +static const std::string ST_TYPE_LABEL("sale_type"); +static const std::string ST_PRICE_LABEL("sale_price"); + +LLSD ll_create_sd_from_sale_info(const LLSaleInfo& sale) +{ + LLSD rv; + const char* type = LLSaleInfo::lookup(sale.getSaleType()); + if(!type) type = LLSaleInfo::lookup(LLSaleInfo::FS_NOT); + rv[ST_TYPE_LABEL] = type; + rv[ST_PRICE_LABEL] = sale.getSalePrice(); + return rv; +} + +LLSaleInfo ll_sale_info_from_sd(const LLSD& sd) +{ + LLSaleInfo rv; + rv.setSaleType(LLSaleInfo::lookup(sd[ST_TYPE_LABEL].asString().c_str())); + rv.setSalePrice(llclamp((S32)sd[ST_PRICE_LABEL], 0, S32_MAX)); + return rv; +} diff --git a/indra/llinventory/llsaleinfo.h b/indra/llinventory/llsaleinfo.h new file mode 100644 index 0000000000..2eceea87ef --- /dev/null +++ b/indra/llinventory/llsaleinfo.h @@ -0,0 +1,110 @@ +/** + * @file llsaleinfo.h + * @brief LLSaleInfo class header file. + * + * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LL_LLSALEINFO_H +#define LL_LLSALEINFO_H + +#include <stdio.h> +#include <iostream> + +#include "llpermissionsflags.h" +#include "llsd.h" +#include "llxmlnode.h" + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLSaleInfo +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +// L$ default price for objects +const S32 DEFAULT_PRICE = 10; + +class LLMessageSystem; + +class LLSaleInfo +{ +public: + // use this to avoid temporary object creation + static const LLSaleInfo DEFAULT; + + enum EForSale + { + // item is not to be considered for transactions + FS_NOT = 0, + + // the origional is on sale + FS_ORIGINAL = 1, + + // A copy is for sale + FS_COPY = 2, + + // Valid only for tasks, the inventory is for sale + // at the price in this structure. + FS_CONTENTS = 3, + + FS_COUNT + }; + +protected: + EForSale mSaleType; + S32 mSalePrice; + +public: + // default constructor is fine usually + LLSaleInfo(); + LLSaleInfo(EForSale sale_type, S32 sale_price); + + // accessors + BOOL isForSale() const; + EForSale getSaleType() const { return mSaleType; } + S32 getSalePrice() const { return mSalePrice; } + U32 getCRC32() const; + + // mutators + void setSaleType(EForSale type) { mSaleType = type; } + void setSalePrice(S32 price); + //void setNextOwnerPermMask(U32 mask) { mNextOwnerPermMask = mask; } + + + // file serialization + BOOL exportFile(FILE* fp) const; + BOOL importFile(FILE* fp, BOOL& has_perm_mask, U32& perm_mask); + + BOOL exportLegacyStream(std::ostream& output_stream) const; + LLSD asLLSD() const; + operator LLSD() const { return asLLSD(); } + bool fromLLSD(LLSD& sd, BOOL& has_perm_mask, U32& perm_mask); + BOOL importLegacyStream(std::istream& input_stream, BOOL& has_perm_mask, U32& perm_mask); + + LLXMLNode *exportFileXML() const; + BOOL importXML(LLXMLNode* node); + + // message serialization + void packMessage(LLMessageSystem* msg) const; + void unpackMessage(LLMessageSystem* msg, const char* block); + void unpackMultiMessage(LLMessageSystem* msg, const char* block, + S32 block_num); + + // static functionality for determine for sale status. + static EForSale lookup(const char* name); + static const char* lookup(EForSale type); + + // Allow accumulation of sale info. The price of each is added, + // conflict in sale type results in FS_NOT, and the permissions + // are tightened. + void accumulate(const LLSaleInfo& sale_info); + + bool operator==(const LLSaleInfo &rhs) const; + bool operator!=(const LLSaleInfo &rhs) const; +}; + +// These functions convert between structured data and sale info as +// appropriate for serialization. +LLSD ll_create_sd_from_sale_info(const LLSaleInfo& sale); +LLSaleInfo ll_sale_info_from_sd(const LLSD& sd); + +#endif // LL_LLSALEINFO_H diff --git a/indra/llinventory/lltransactionflags.cpp b/indra/llinventory/lltransactionflags.cpp new file mode 100644 index 0000000000..3f1aa14959 --- /dev/null +++ b/indra/llinventory/lltransactionflags.cpp @@ -0,0 +1,43 @@ +/** + * @file lltransactionflags.cpp + * @brief Some exported symbols and functions for dealing with + * transaction flags. + * + * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "linden_common.h" + +#include "lltransactionflags.h" + +const U8 TRANSACTION_FLAGS_NONE = 0; +const U8 TRANSACTION_FLAG_SOURCE_GROUP = 1; +const U8 TRANSACTION_FLAG_DEST_GROUP = 2; +const U8 TRANSACTION_FLAG_OWNER_GROUP = 4; +const U8 TRANSACTION_FLAG_SIMULTANEOUS_CONTRIBUTION = 8; +const U8 TRANSACTION_FLAG_SIMULTANEOUS_CONTRIBUTION_REMOVAL = 16; + +U8 pack_transaction_flags(BOOL is_source_group, BOOL is_dest_group) +{ + U8 rv = 0; + if(is_source_group) rv |= TRANSACTION_FLAG_SOURCE_GROUP; + if(is_dest_group) rv |= TRANSACTION_FLAG_DEST_GROUP; + return rv; +} + +BOOL is_tf_source_group(TransactionFlags flags) +{ + return ((flags & TRANSACTION_FLAG_SOURCE_GROUP) == TRANSACTION_FLAG_SOURCE_GROUP); +} + +BOOL is_tf_dest_group(TransactionFlags flags) +{ + return ((flags & TRANSACTION_FLAG_DEST_GROUP) == TRANSACTION_FLAG_DEST_GROUP); +} + +BOOL is_tf_owner_group(TransactionFlags flags) +{ + return ((flags & TRANSACTION_FLAG_OWNER_GROUP) == TRANSACTION_FLAG_OWNER_GROUP); +} + diff --git a/indra/llinventory/lltransactionflags.h b/indra/llinventory/lltransactionflags.h new file mode 100644 index 0000000000..eaa138fef7 --- /dev/null +++ b/indra/llinventory/lltransactionflags.h @@ -0,0 +1,27 @@ +/** + * @file lltransactionflags.h + * + * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LL_LLTRANSACTIONFLAGS_H +#define LL_LLTRANSACTIONFLAGS_H + +typedef U8 TransactionFlags; + +// defined in common/llinventory/lltransactionflags.cpp +extern const TransactionFlags TRANSACTION_FLAGS_NONE; +extern const TransactionFlags TRANSACTION_FLAG_SOURCE_GROUP; +extern const TransactionFlags TRANSACTION_FLAG_DEST_GROUP; +extern const TransactionFlags TRANSACTION_FLAG_OWNER_GROUP; +extern const TransactionFlags TRANSACTION_FLAG_SIMULTANEOUS_CONTRIBUTION; +extern const TransactionFlags TRANSACTION_FLAG_SIMULTANEOUS_CONTRIBUTION_REMOVAL; + +// very simple helper functions +TransactionFlags pack_transaction_flags(BOOL is_source_group, BOOL is_dest_group); +BOOL is_tf_source_group(TransactionFlags flags); +BOOL is_tf_dest_group(TransactionFlags flags); +BOOL is_tf_owner_group(TransactionFlags flags); + +#endif // LL_LLTRANSACTIONFLAGS_H diff --git a/indra/llinventory/lltransactiontypes.h b/indra/llinventory/lltransactiontypes.h new file mode 100644 index 0000000000..d7894b2fdb --- /dev/null +++ b/indra/llinventory/lltransactiontypes.h @@ -0,0 +1,93 @@ +/** + * @file lltransactiontypes.h + * + * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LL_LLTRANSACTIONTYPES_H +#define LL_LLTRANSACTIONTYPES_H + +// *NOTE: The constants in this file are also in the +// transaction_description table in the database. If you add a +// constant here, please add it to the database. eg: +// +// insert into transaction_description +// set type = 1000, description = 'Object Claim'; +// +// Also add it to the various money string lookups on the dataserver +// in lldatamoney + +// Money transaction failure codes +const U8 TRANS_FAIL_SIMULATOR_TIMEOUT = 1; +const U8 TRANS_FAIL_DATASERVER_TIMEOUT = 2; + +// Codes up to 999 for error conditions +const S32 TRANS_NULL = 0; + +// Codes 1000-1999 reserved for one-time charges +const S32 TRANS_OBJECT_CLAIM = 1000; +const S32 TRANS_LAND_CLAIM = 1001; +const S32 TRANS_GROUP_CREATE = 1002; +const S32 TRANS_OBJECT_PUBLIC_CLAIM = 1003; +const S32 TRANS_GROUP_JOIN = 1004; // May be moved to group transactions eventually +const S32 TRANS_TELEPORT_CHARGE = 1100; // FF not sure why this jumps to 1100... +const S32 TRANS_UPLOAD_CHARGE = 1101; +const S32 TRANS_LAND_AUCTION = 1102; +const S32 TRANS_CLASSIFIED_CHARGE = 1103; + +// Codes 2000-2999 reserved for recurrent charges +const S32 TRANS_OBJECT_TAX = 2000; +const S32 TRANS_LAND_TAX = 2001; +const S32 TRANS_LIGHT_TAX = 2002; +const S32 TRANS_PARCEL_DIR_FEE = 2003; +const S32 TRANS_GROUP_TAX = 2004; // Taxes incurred as part of group membership +const S32 TRANS_CLASSIFIED_RENEW = 2005; + +// Codes 3000-3999 reserved for inventory transactions +const S32 TRANS_GIVE_INVENTORY = 3000; + +// Codes 5000-5999 reserved for transfers between users +const S32 TRANS_OBJECT_SALE = 5000; +const S32 TRANS_GIFT = 5001; +const S32 TRANS_LAND_SALE = 5002; +const S32 TRANS_REFER_BONUS = 5003; +const S32 TRANS_INVENTORY_SALE = 5004; +const S32 TRANS_REFUND_PURCHASE = 5005; +const S32 TRANS_LAND_PASS_SALE = 5006; +const S32 TRANS_DWELL_BONUS = 5007; +const S32 TRANS_PAY_OBJECT = 5008; +const S32 TRANS_OBJECT_PAYS = 5009; + +// Codes 6000-6999 reserved for group transactions +//const S32 TRANS_GROUP_JOIN = 6000; //reserved for future use +const S32 TRANS_GROUP_LAND_DEED = 6001; +const S32 TRANS_GROUP_OBJECT_DEED = 6002; +const S32 TRANS_GROUP_LIABILITY = 6003; +const S32 TRANS_GROUP_DIVIDEND = 6004; +const S32 TRANS_MEMBERSHIP_DUES = 6005; + +// Codes 8000-8999 reserved for one-type credits +const S32 TRANS_OBJECT_RELEASE = 8000; +const S32 TRANS_LAND_RELEASE = 8001; +const S32 TRANS_OBJECT_DELETE = 8002; +const S32 TRANS_OBJECT_PUBLIC_DECAY = 8003; +const S32 TRANS_OBJECT_PUBLIC_DELETE= 8004; + +// Code 9000-9099 reserved for usertool transactions +const S32 TRANS_LINDEN_ADJUSTMENT = 9000; +const S32 TRANS_LINDEN_GRANT = 9001; +const S32 TRANS_LINDEN_PENALTY = 9002; +const S32 TRANS_EVENT_FEE = 9003; +const S32 TRANS_EVENT_PRIZE = 9004; + +// These must match entries in money_stipend table in MySQL +// Codes 10000-10999 reserved for stipend credits +const S32 TRANS_STIPEND_BASIC = 10000; +const S32 TRANS_STIPEND_DEVELOPER = 10001; +const S32 TRANS_STIPEND_ALWAYS = 10002; +const S32 TRANS_STIPEND_DAILY = 10003; +const S32 TRANS_STIPEND_RATING = 10004; +const S32 TRANS_STIPEND_DELTA = 10005; + +#endif diff --git a/indra/llinventory/lluserrelations.cpp b/indra/llinventory/lluserrelations.cpp new file mode 100644 index 0000000000..c10f6f610a --- /dev/null +++ b/indra/llinventory/lluserrelations.cpp @@ -0,0 +1,89 @@ +/** + * @file lluserrealations.cpp + * @author Phoenix + * @date 2006-10-12 + * @brief Implementation of a simple cache of user relations. + * + * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "linden_common.h" +#include "lluserrelations.h" + +// static +const U8 LLRelationship::GRANTED_VISIBLE_MASK = LLRelationship::GRANT_MODIFY_OBJECTS | LLRelationship::GRANT_MAP_LOCATION; +const LLRelationship LLRelationship::DEFAULT_RELATIONSHIP = LLRelationship(GRANT_ONLINE_STATUS, GRANT_ONLINE_STATUS, false); + +LLRelationship::LLRelationship() : + mGrantToAgent(0), + mGrantFromAgent(0), + mIsOnline(false) +{ +} + +LLRelationship::LLRelationship(S32 grant_to, S32 grant_from, bool is_online) : + mGrantToAgent(grant_to), + mGrantFromAgent(grant_from), + mIsOnline(is_online) +{ +} + +bool LLRelationship::isOnline() const +{ + return mIsOnline; +} + +void LLRelationship::online(bool is_online) +{ + mIsOnline = is_online; +} + +bool LLRelationship::isRightGrantedTo(S32 rights) const +{ + return ((mGrantToAgent & rights) == rights); +} + +bool LLRelationship::isRightGrantedFrom(S32 rights) const +{ + return ((mGrantFromAgent & rights) == rights); +} + +S32 LLRelationship::getRightsGrantedTo() const +{ + return mGrantToAgent; +} + +S32 LLRelationship::getRightsGrantedFrom() const +{ + return mGrantFromAgent; +} + +void LLRelationship::grantRights(S32 to_agent, S32 from_agent) +{ + mGrantToAgent |= to_agent; + mGrantFromAgent |= from_agent; +} + +void LLRelationship::revokeRights(S32 to_agent, S32 from_agent) +{ + mGrantToAgent &= ~to_agent; + mGrantFromAgent &= ~from_agent; +} + + + +/* +bool LLGrantedRights::getNextRights( + LLUUID& agent_id, + S32& to_agent, + S32& from_agent) const +{ + rights_map_t::const_iterator iter = mRights.upper_bound(agent_id); + if(iter == mRights.end()) return false; + agent_id = (*iter).first; + to_agent = (*iter).second.mToAgent; + from_agent = (*iter).second.mFromAgent; + return true; +} +*/ diff --git a/indra/llinventory/lluserrelations.h b/indra/llinventory/lluserrelations.h new file mode 100644 index 0000000000..7c24254339 --- /dev/null +++ b/indra/llinventory/lluserrelations.h @@ -0,0 +1,154 @@ +/** + * @file llluserrelations.h + * @author Phoenix + * @date 2006-10-12 + * @brief Declaration of a class for handling granted rights. + * + * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LL_LLUSERRELAIONS_H +#define LL_LLUSERRELAIONS_H + +#include <map> +#include "lluuid.h" + +/** + * @class LLRelationship + * + * This class represents a relationship between two agents, where the + * related agent is stored and the other agent is the relationship is + * implicit by container ownership. + * This is merely a cache of this information used by the sim + * and viewer. + * + * You are expected to use this in a map or similar structure, eg: + * typedef std::map<LLUUID, LLRelationship> agent_relationship_map; + */ +class LLRelationship +{ +public: + /** + * @brief Constructors. + */ + LLRelationship(); + LLRelationship(S32 grant_to, S32 grant_from, bool is_online); + + static const LLRelationship DEFAULT_RELATIONSHIP; + + /** + * @name Status functionality + * + * I thought it would be keen to have a generic status interface, + * but the only thing we currently cache is online status. As this + * assumption changes, this API may evolve. + */ + //@{ + /** + * @brief Does this instance believe the related agent is currently + * online or available. + * + * NOTE: This API may be deprecated if there is any transient status + * other than online status, for example, away/busy/etc. + * + * This call does not check any kind of central store or make any + * deep information calls - it simply checks a cache of online + * status. + * @return Returns true if this relationship believes the agent is + * online. + */ + bool isOnline() const; + + /** + * @brief Set the online status. + * + * NOTE: This API may be deprecated if there is any transient status + * other than online status. + * @param is_online Se the online status + */ + void online(bool is_online); + //@} + + /* @name Granted rights + */ + //@{ + /** + * @brief Anonymous enumeration for specifying rights. + */ + enum + { + GRANT_NONE = 0x0, + GRANT_ONLINE_STATUS = 0x1, + GRANT_MAP_LOCATION = 0x2, + GRANT_MODIFY_OBJECTS = 0x4, + }; + + /** + * ??? + */ + static const U8 GRANTED_VISIBLE_MASK; + + /** + * @brief Check for a set of rights granted to agent. + * + * @param rights A bitfield to check for rights. + * @return Returns true if all rights have been granted. + */ + bool isRightGrantedTo(S32 rights) const; + + /** + * @brief Check for a set of rights granted from an agent. + * + * @param rights A bitfield to check for rights. + * @return Returns true if all rights have been granted. + */ + bool isRightGrantedFrom(S32 rights) const; + + /** + * @brief Get the rights granted to the other agent. + * + * @return Returns the bitmask of granted rights. + */ + S32 getRightsGrantedTo() const; + + /** + * @brief Get the rights granted from the other agent. + * + * @return Returns the bitmask of granted rights. + */ + S32 getRightsGrantedFrom() const; + + void setRightsTo(S32 to_agent) { mGrantToAgent = to_agent; } + void setRightsFrom(S32 from_agent) { mGrantFromAgent = from_agent; } + + /** + * @brief Grant a set of rights. + * + * Any bit which is set will grant that right if it is set in the + * instance. You can pass in LLGrantedRights::NONE to not change + * that field. + * @param to_agent The rights to grant to agent_id. + * @param from_agent The rights granted from agent_id. + */ + void grantRights(S32 to_agent, S32 from_agent); + + /** + * @brief Revoke a set of rights. + * + * Any bit which is set will revoke that right if it is set in the + * instance. You can pass in LLGrantedRights::NONE to not change + * that field. + * @param to_agent The rights to grant to agent_id. + * @param from_agent The rights granted from agent_id. + */ + void revokeRights(S32 to_agent, S32 from_agent); + //@} + +protected: + S32 mGrantToAgent; + S32 mGrantFromAgent; + bool mIsOnline; +}; + +#endif // LL_LLUSERRELAIONS_H |