summaryrefslogtreecommitdiff
path: root/indra/llinventory/llinventory.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llinventory/llinventory.cpp')
-rw-r--r--indra/llinventory/llinventory.cpp478
1 files changed, 318 insertions, 160 deletions
diff --git a/indra/llinventory/llinventory.cpp b/indra/llinventory/llinventory.cpp
index f126accfb8..19ad49c6cc 100644
--- a/indra/llinventory/llinventory.cpp
+++ b/indra/llinventory/llinventory.cpp
@@ -33,6 +33,7 @@
#include "llxorcipher.h"
#include "llsd.h"
#include "llsdserialize.h"
+#include "llstreamtools.h"
#include "message.h"
#include <boost/tokenizer.hpp>
@@ -61,6 +62,9 @@ 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");
static const std::string INV_TOGGLED_LABEL("toggled");
+static const std::string INV_SCRIPT_LABEL("script");
+static const std::string INV_RUNTIME_LABEL("runtime");
+static const std::string INV_METADATA_LABEL("metadata");
// key used by agent-inventory-service
static const std::string INV_ASSET_TYPE_LABEL_WS("type_default");
@@ -109,6 +113,7 @@ void LLInventoryObject::copyObject(const LLInventoryObject* other)
mName = other->mName;
mThumbnailUUID = other->mThumbnailUUID;
mFavorite = other->mFavorite;
+ mRuntime = other->mRuntime;
}
const LLUUID& LLInventoryObject::getUUID() const
@@ -131,6 +136,11 @@ bool LLInventoryObject::getIsFavorite() const
return mFavorite;
}
+const std::string& LLInventoryObject::getRuntime() const
+{
+ return mRuntime;
+}
+
const std::string& LLInventoryObject::getName() const
{
return mName;
@@ -190,9 +200,23 @@ void LLInventoryObject::setFavorite(bool favorite)
mFavorite = favorite;
}
+void LLInventoryObject::setRuntime(std::string_view runtime)
+{
+ // Store the runtime unconditionally; it will be validated/cleared
+ // when the final asset type is known (see setType()).
+ mRuntime = runtime;
+}
+
void LLInventoryObject::setType(LLAssetType::EType type)
{
mType = type;
+
+ // Only LSL text assets are expected to have a runtime; clear any
+ // previously stored runtime for other asset types.
+ if (mType != LLAssetType::AT_LSL_TEXT)
+ {
+ mRuntime.clear();
+ }
}
@@ -279,6 +303,15 @@ bool LLInventoryObject::importLegacyStream(std::istream& input_stream)
{
setFavorite(false);
}
+
+ if (metadata.has("script") && metadata["script"].has("runtime"))
+ {
+ setRuntime(metadata["script"]["runtime"].asString());
+ }
+ else
+ {
+ setRuntime(std::string());
+ }
}
else if(0 == strcmp("name", keyword))
{
@@ -421,6 +454,7 @@ void LLInventoryItem::copyItem(const LLInventoryItem* other)
mInventoryType = other->mInventoryType;
mFlags = other->mFlags;
mCreationDate = other->mCreationDate;
+ mRuntime = other->mRuntime;
}
// If this is a linked item, then the UUID of the base object is
@@ -784,6 +818,16 @@ bool LLInventoryItem::importLegacyStream(std::istream& input_stream)
{
setFavorite(false);
}
+
+ if (metadata.has("script") && metadata["script"].has("runtime"))
+ {
+ setRuntime(metadata["script"]["runtime"].asString());
+ }
+ else
+ {
+ setRuntime(std::string());
+ }
+
}
else if(0 == strcmp("inv_type", keyword))
{
@@ -827,6 +871,42 @@ bool LLInventoryItem::importLegacyStream(std::istream& input_stream)
}
mDescription.assign(valuestr);
+
+ // Currently server side doesn't handle escaped newline right
+ // and they end up unescaped.
+ // And even if we do copy the data correctly, moving the item
+ // from notecard using CopyInventoryFromNotecard drops the
+ // content after first the new line.
+ //
+ // Check if the description ends with | on this line
+ if (strchr(buffer, '|') == nullptr)
+ {
+ // No | found, continue reading lines until we find one
+ char next_buffer[MAX_STRING];
+ while (input_stream.good())
+ {
+ input_stream.getline(next_buffer, MAX_STRING);
+
+ // Look for | delimiter
+ char* pipe_pos = strchr(next_buffer, '|');
+ if (pipe_pos != nullptr)
+ {
+ // Found the delimiter, add text up to it
+ *pipe_pos = '\0'; // Terminate at the pipe
+ mDescription += '\n';
+ mDescription += next_buffer;
+ break;
+ }
+ else
+ {
+ // No delimiter yet, add the whole line
+ mDescription += '\n';
+ mDescription += next_buffer;
+ }
+ }
+ }
+
+ unescape_string(mDescription);
LLStringUtil::replaceNonstandardASCII(mDescription, ' ');
/* TODO -- ask Ian about this code
const char *donkey = mDescription.c_str();
@@ -920,7 +1000,12 @@ bool LLInventoryItem::exportLegacyStream(std::ostream& output_stream, bool inclu
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";
+ // Note: Currently server side doesn't handle newline right for notecards.
+ // Even if we escape or replace all newlines with spaces, notecard's
+ // import buffer still ends up with newlines.
+ std::string desc = mDescription;
+ escape_string(desc);
+ output_stream << "\t\tdesc\t" << desc.c_str() << "|\n";
output_stream << "\t\tcreation_date\t" << mCreationDate << "\n";
output_stream << "\t}\n";
return true;
@@ -941,12 +1026,19 @@ void LLInventoryItem::asLLSD( LLSD& sd ) const
if (mThumbnailUUID.notNull())
{
- sd[INV_THUMBNAIL_LABEL] = LLSD().with(INV_ASSET_ID_LABEL, mThumbnailUUID);
+ LLSD& thumbnail = sd[INV_THUMBNAIL_LABEL];
+ thumbnail[INV_ASSET_ID_LABEL] = mThumbnailUUID;
}
if (mFavorite)
{
- sd[INV_FAVORITE_LABEL] = LLSD().with(INV_TOGGLED_LABEL, mFavorite);
+ LLSD& favorite = sd[INV_FAVORITE_LABEL];
+ favorite[INV_TOGGLED_LABEL] = mFavorite;
+ }
+
+ if (!mRuntime.empty())
+ {
+ sd[INV_SCRIPT_LABEL] = LLSD().with(INV_RUNTIME_LABEL, mRuntime);
}
U32 mask = mPermissions.getMaskBase();
@@ -963,7 +1055,7 @@ void LLInventoryItem::asLLSD( LLSD& sd ) const
cipher.encrypt(shadow_id.mData, UUID_BYTES);
sd[INV_SHADOW_ID_LABEL] = shadow_id;
}
- sd[INV_ASSET_TYPE_LABEL] = std::string(LLAssetType::lookup(mType));
+ sd[INV_ASSET_TYPE_LABEL] = LLAssetType::lookup(mType);
const std::string inv_type_str = LLInventoryType::lookup(mInventoryType);
if(!inv_type_str.empty())
{
@@ -1002,171 +1094,231 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new)
end = sd.endMap();
for (i = sd.beginMap(); i != end; ++i)
{
- if (i->first == INV_ITEM_ID_LABEL)
- {
- mUUID = i->second;
- continue;
- }
-
- if (i->first == INV_PARENT_ID_LABEL)
- {
- mParentUUID = i->second;
- continue;
- }
-
- if (i->first == INV_THUMBNAIL_LABEL)
- {
- const LLSD &thumbnail_map = i->second;
- if (thumbnail_map.has(INV_ASSET_ID_LABEL))
- {
- mThumbnailUUID = thumbnail_map[INV_ASSET_ID_LABEL];
- }
- /* Example:
- <key> asset_id </key>
- <uuid> acc0ec86 - 17f2 - 4b92 - ab41 - 6718b1f755f7 </uuid>
- <key> perms </key>
- <integer> 8 </integer>
- <key>service</key>
- <integer> 3 </integer>
- <key>version</key>
- <integer> 1 </key>
- */
- continue;
- }
-
- if (i->first == INV_THUMBNAIL_ID_LABEL)
- {
- mThumbnailUUID = i->second.asUUID();
- continue;
- }
-
- if (i->first == INV_FAVORITE_LABEL)
- {
- const LLSD& favorite_map = i->second;
- if (favorite_map.has(INV_TOGGLED_LABEL))
- {
- mFavorite = favorite_map[INV_TOGGLED_LABEL].asBoolean();
- }
- continue;
- }
+ // Use string length as a fast pre-filter before string comparison
+ const std::string& key = i->first;
+ const LLSD& value = i->second;
+ const size_t key_len = key.length();
- if (i->first == INV_PERMISSIONS_LABEL)
+ switch (key_len)
{
- mPermissions.importLLSD(i->second);
- continue;
- }
-
- if (i->first == INV_SALE_INFO_LABEL)
- {
- // 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(i->second, has_perm_mask, perm_mask))
- {
- return false;
- }
- if (has_perm_mask)
- {
- if (perm_mask == PERM_NONE)
+ case 4: // "name", "desc", "type"
+ if (key == INV_NAME_LABEL) // "name"
{
- perm_mask = mPermissions.getMaskOwner();
+ mName = value.asString();
+ LLStringUtil::replaceNonstandardASCII(mName, ' ');
+ LLStringUtil::replaceChar(mName, '|', ' ');
+ continue;
}
- // fair use fix.
- if (!(perm_mask & PERM_COPY))
+ if (key == INV_DESC_LABEL) // "desc"
{
- perm_mask |= PERM_TRANSFER;
+ mDescription = value.asString();
+ LLStringUtil::replaceNonstandardASCII(mDescription, ' ');
+ continue;
}
- mPermissions.setMaskNext(perm_mask);
- }
- continue;
- }
-
- if (i->first == INV_SHADOW_ID_LABEL)
- {
- mAssetUUID = i->second;
- LLXORCipher cipher(MAGIC_ID.mData, UUID_BYTES);
- cipher.decrypt(mAssetUUID.mData, UUID_BYTES);
- continue;
- }
+ if (key == INV_ASSET_TYPE_LABEL) // "type"
+ {
+ if (value.isString())
+ {
+ mType = LLAssetType::lookup(value.asStringRef().c_str());
+ }
+ else if (value.isInteger())
+ {
+ S8 type = (U8)value.asInteger();
+ mType = static_cast<LLAssetType::EType>(type);
+ }
+ continue;
+ }
+ break;
- if (i->first == INV_ASSET_ID_LABEL)
- {
- mAssetUUID = i->second;
- continue;
- }
+ case 5: // "flags"
+ if (key == INV_FLAGS_LABEL)
+ {
+ if (value.isBinary())
+ {
+ mFlags = ll_U32_from_sd(value);
+ }
+ else if (value.isInteger())
+ {
+ mFlags = value.asInteger();
+ }
+ continue;
+ }
+ break;
- if (i->first == INV_LINKED_ID_LABEL)
- {
- mAssetUUID = i->second;
- continue;
- }
+ case 6: // "script"
+ if (key == INV_SCRIPT_LABEL)
+ {
+ const LLSD& script_map = value;
+ if (script_map.has(INV_RUNTIME_LABEL))
+ {
+ mRuntime = script_map[INV_RUNTIME_LABEL].asString();
+ }
+ else
+ {
+ // Clear stale runtime data when a script block is
+ // present without an explicit runtime value.
+ mRuntime.clear();
+ }
+ continue;
+ }
+ break;
- if (i->first == INV_ASSET_TYPE_LABEL)
- {
- LLSD const &label = i->second;
- if (label.isString())
- {
- mType = LLAssetType::lookup(label.asStringRef().c_str());
- }
- else if (label.isInteger())
- {
- S8 type = (U8) label.asInteger();
- mType = static_cast<LLAssetType::EType>(type);
- }
- continue;
- }
+ case 7: // "item_id"
+ if (key == INV_ITEM_ID_LABEL)
+ {
+ mUUID = value;
+ continue;
+ }
+ break;
- if (i->first == INV_INVENTORY_TYPE_LABEL)
- {
- LLSD const &label = i->second;
- if (label.isString())
- {
- mInventoryType = LLInventoryType::lookup(label.asStringRef().c_str());
- }
- else if (label.isInteger())
- {
- S8 type = (U8) label.asInteger();
- mInventoryType = static_cast<LLInventoryType::EType>(type);
- }
- continue;
- }
+ case 8: // "asset_id", "inv_type"
+ if (key == INV_ASSET_ID_LABEL)
+ {
+ mAssetUUID = value;
+ continue;
+ }
+ if (key == INV_INVENTORY_TYPE_LABEL) // "inv_type"
+ {
+ if (value.isString())
+ {
+ mInventoryType = LLInventoryType::lookup(value.asStringRef().c_str());
+ }
+ else if (value.isInteger())
+ {
+ S8 type = (U8)value.asInteger();
+ mInventoryType = static_cast<LLInventoryType::EType>(type);
+ }
+ continue;
+ }
+ if (key == INV_FAVORITE_LABEL) // "favorite"
+ {
+ if (value.has(INV_TOGGLED_LABEL))
+ {
+ mFavorite = value[INV_TOGGLED_LABEL].asBoolean();
+ }
+ continue;
+ }
+ if (key == INV_METADATA_LABEL)
+ {
+ // Server (non-AIS) exports thumbnail, favorite, and
+ // script metadata under a "metadata" wrapper.
+ const LLSD& metadata = value;
+ if (metadata.has(INV_THUMBNAIL_LABEL))
+ {
+ const LLSD& thumbnail = metadata[INV_THUMBNAIL_LABEL];
+ if (thumbnail.has(INV_ASSET_ID_LABEL))
+ {
+ mThumbnailUUID = thumbnail[INV_ASSET_ID_LABEL].asUUID();
+ }
+ }
+ if (metadata.has(INV_FAVORITE_LABEL))
+ {
+ const LLSD& favorite = metadata[INV_FAVORITE_LABEL];
+ if (favorite.has(INV_TOGGLED_LABEL))
+ {
+ mFavorite = favorite[INV_TOGGLED_LABEL].asBoolean();
+ }
+ }
+ if (metadata.has(INV_SCRIPT_LABEL)
+ && metadata[INV_SCRIPT_LABEL].has(INV_RUNTIME_LABEL))
+ {
+ mRuntime = metadata[INV_SCRIPT_LABEL][INV_RUNTIME_LABEL].asString();
+ }
+ continue;
+ }
+ break;
- if (i->first == INV_FLAGS_LABEL)
- {
- LLSD const &label = i->second;
- if (label.isBinary())
- {
- mFlags = ll_U32_from_sd(label);
- }
- else if (label.isInteger())
- {
- mFlags = label.asInteger();
- }
- continue;
- }
+ case 9: // "parent_id", "shadow_id", "linked_id", "sale_info", "thumbnail"
+ if (key == INV_PARENT_ID_LABEL)
+ {
+ mParentUUID = value;
+ continue;
+ }
+ if (key == INV_SHADOW_ID_LABEL)
+ {
+ mAssetUUID = value;
+ LLXORCipher cipher(MAGIC_ID.mData, UUID_BYTES);
+ cipher.decrypt(mAssetUUID.mData, UUID_BYTES);
+ continue;
+ }
+ if (key == INV_LINKED_ID_LABEL)
+ {
+ mAssetUUID = value;
+ continue;
+ }
+ if (key == INV_SALE_INFO_LABEL)
+ {
+ // 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(value, has_perm_mask, perm_mask))
+ {
+ return false;
+ }
+ 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);
+ }
+ continue;
+ }
+ if (key == INV_THUMBNAIL_LABEL)
+ {
+ if (value.has(INV_ASSET_ID_LABEL))
+ {
+ mThumbnailUUID = value[INV_ASSET_ID_LABEL];
+ }
+ /* Example:
+ <key> asset_id </key>
+ <uuid> acc0ec86 - 17f2 - 4b92 - ab41 - 6718b1f755f7 </uuid>
+ <key> perms </key>
+ <integer> 8 </integer>
+ <key>service</key>
+ <integer> 3 </integer>
+ <key>version</key>
+ <integer> 1 </key>
+ */
+ continue;
+ }
+ break;
+ case 10: // "created_at"
+ if (key == INV_CREATION_DATE_LABEL)
+ {
+ mCreationDate = value.asInteger();
+ continue;
+ }
+ break;
- if (i->first == INV_NAME_LABEL)
- {
- mName = i->second.asString();
- LLStringUtil::replaceNonstandardASCII(mName, ' ');
- LLStringUtil::replaceChar(mName, '|', ' ');
- continue;
- }
+ case 11: // "permissions"
+ if (key == INV_PERMISSIONS_LABEL)
+ {
+ mPermissions.importLLSD(value);
+ continue;
+ }
+ break;
- if (i->first == INV_DESC_LABEL)
- {
- mDescription = i->second.asString();
- LLStringUtil::replaceNonstandardASCII(mDescription, ' ');
- continue;
- }
+ case 12: // "thumbnail_id"
+ if (key == INV_THUMBNAIL_ID_LABEL)
+ {
+ mThumbnailUUID = value.asUUID();
+ continue;
+ }
+ break;
- if (i->first == INV_CREATION_DATE_LABEL)
- {
- mCreationDate = i->second.asInteger();
- continue;
+ default:
+ // Unknown field - skip
+ break;
}
}
@@ -1242,12 +1394,14 @@ LLSD LLInventoryCategory::asLLSD() const
if (mThumbnailUUID.notNull())
{
- sd[INV_THUMBNAIL_LABEL] = LLSD().with(INV_ASSET_ID_LABEL, mThumbnailUUID);
+ LLSD& thumbnail = sd[INV_THUMBNAIL_LABEL];
+ thumbnail[INV_ASSET_ID_LABEL] = mThumbnailUUID;
}
if (mFavorite)
{
- sd[INV_FAVORITE_LABEL] = LLSD().with(INV_TOGGLED_LABEL, mFavorite);
+ LLSD& favorite = sd[INV_FAVORITE_LABEL];
+ favorite[INV_TOGGLED_LABEL] = mFavorite;
}
return sd;
@@ -1469,6 +1623,8 @@ bool LLInventoryCategory::importLegacyStream(std::istream& input_stream)
{
setFavorite(false);
}
+ setRuntime(std::string());
+
}
else
{
@@ -1507,17 +1663,19 @@ void LLInventoryCategory::exportLLSD(LLSD& cat_data) const
{
cat_data[INV_FOLDER_ID_LABEL] = mUUID;
cat_data[INV_PARENT_ID_LABEL] = mParentUUID;
- cat_data[INV_ASSET_TYPE_LABEL] = std::string(LLAssetType::lookup(mType));
+ cat_data[INV_ASSET_TYPE_LABEL] = LLAssetType::lookup(mType);
cat_data[INV_PREFERRED_TYPE_LABEL] = LLFolderType::lookup(mPreferredType);
cat_data[INV_NAME_LABEL] = mName;
if (mThumbnailUUID.notNull())
{
- cat_data[INV_THUMBNAIL_LABEL] = LLSD().with(INV_ASSET_ID_LABEL, mThumbnailUUID);
+ LLSD& thumbnail = cat_data[INV_THUMBNAIL_LABEL];
+ thumbnail[INV_ASSET_ID_LABEL] = mThumbnailUUID;
}
if (mFavorite)
{
- cat_data[INV_FAVORITE_LABEL] = LLSD().with(INV_TOGGLED_LABEL, mFavorite);
+ LLSD& favorite = cat_data[INV_FAVORITE_LABEL];
+ favorite[INV_TOGGLED_LABEL] = mFavorite;
}
}