diff options
author | Monroe Williams <monroe@lindenlab.com> | 2009-10-01 02:35:53 +0000 |
---|---|---|
committer | Monroe Williams <monroe@lindenlab.com> | 2009-10-01 02:35:53 +0000 |
commit | cf9239cabcf7999a2d2393bd4bdb6fc08e27c09c (patch) | |
tree | b366355f955b7bf55197ef6caa105881da88df32 /indra/llprimitive | |
parent | 8135ddac021d3ea1aba2100f862bdb58eff33d07 (diff) |
svn merge -r 134922:134973 svn+ssh://svn.lindenlab.com/svn/linden/branches/media-on-a-prim/moap-7
Merging branches/media-on-a-prim/moap-7 down to viewer-2.0.
Diffstat (limited to 'indra/llprimitive')
-rw-r--r-- | indra/llprimitive/CMakeLists.txt | 9 | ||||
-rw-r--r-- | indra/llprimitive/llmediaentry.cpp | 597 | ||||
-rw-r--r-- | indra/llprimitive/llmediaentry.h | 228 | ||||
-rw-r--r-- | indra/llprimitive/llprimitive.cpp | 1 | ||||
-rw-r--r-- | indra/llprimitive/lltextureentry.cpp | 196 | ||||
-rw-r--r-- | indra/llprimitive/lltextureentry.h | 50 | ||||
-rw-r--r-- | indra/llprimitive/tests/llmediaentry_test.cpp | 484 |
7 files changed, 1558 insertions, 7 deletions
diff --git a/indra/llprimitive/CMakeLists.txt b/indra/llprimitive/CMakeLists.txt index 478dd398ff..d130513637 100644 --- a/indra/llprimitive/CMakeLists.txt +++ b/indra/llprimitive/CMakeLists.txt @@ -17,6 +17,7 @@ include_directories( set(llprimitive_SOURCE_FILES llmaterialtable.cpp + llmediaentry.cpp llprimitive.cpp llprimtexturelist.cpp lltextureanim.cpp @@ -31,6 +32,7 @@ set(llprimitive_HEADER_FILES legacy_object_types.h llmaterialtable.h + llmediaentry.h llprimitive.h llprimtexturelist.h lltextureanim.h @@ -49,3 +51,10 @@ set_source_files_properties(${llprimitive_HEADER_FILES} list(APPEND llprimitive_SOURCE_FILES ${llprimitive_HEADER_FILES}) add_library (llprimitive ${llprimitive_SOURCE_FILES}) + +#add unit tests +INCLUDE(LLAddBuildTest) +SET(llprimitive_TEST_SOURCE_FILES + llmediaentry.cpp + ) +LL_ADD_PROJECT_UNIT_TESTS(llprimitive "${llprimitive_TEST_SOURCE_FILES}") diff --git a/indra/llprimitive/llmediaentry.cpp b/indra/llprimitive/llmediaentry.cpp new file mode 100644 index 0000000000..fa04bf80e7 --- /dev/null +++ b/indra/llprimitive/llmediaentry.cpp @@ -0,0 +1,597 @@ +/** + * @file llmediaentry.cpp + * @brief This is a single instance of media data related to the face of a prim + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "llmediaentry.h" +#include "lllslconstants.h" + +#include <boost/regex.hpp> + +// LLSD key defines +// DO NOT REORDER OR REMOVE THESE! + +// Some LLSD keys. Do not change! +#define MEDIA_ALT_IMAGE_ENABLE_KEY_STR "alt_image_enable" +#define MEDIA_CONTROLS_KEY_STR "controls" +#define MEDIA_CURRENT_URL_KEY_STR "current_url" +#define MEDIA_HOME_URL_KEY_STR "home_url" +#define MEDIA_AUTO_LOOP_KEY_STR "auto_loop" +#define MEDIA_AUTO_PLAY_KEY_STR "auto_play" +#define MEDIA_AUTO_SCALE_KEY_STR "auto_scale" +#define MEDIA_AUTO_ZOOM_KEY_STR "auto_zoom" +#define MEDIA_FIRST_CLICK_INTERACT_KEY_STR "first_click_interact" +#define MEDIA_WIDTH_PIXELS_KEY_STR "width_pixels" +#define MEDIA_HEIGHT_PIXELS_KEY_STR "height_pixels" + +// "security" fields +#define MEDIA_WHITELIST_ENABLE_KEY_STR "whitelist_enable" +#define MEDIA_WHITELIST_KEY_STR "whitelist" + +// "permissions" fields +#define MEDIA_PERMS_INTERACT_KEY_STR "perms_interact" +#define MEDIA_PERMS_CONTROL_KEY_STR "perms_control" + +// "general" fields +const char* LLMediaEntry::ALT_IMAGE_ENABLE_KEY = MEDIA_ALT_IMAGE_ENABLE_KEY_STR; +const char* LLMediaEntry::CONTROLS_KEY = MEDIA_CONTROLS_KEY_STR; +const char* LLMediaEntry::CURRENT_URL_KEY = MEDIA_CURRENT_URL_KEY_STR; +const char* LLMediaEntry::HOME_URL_KEY = MEDIA_HOME_URL_KEY_STR; +const char* LLMediaEntry::AUTO_LOOP_KEY = MEDIA_AUTO_LOOP_KEY_STR; +const char* LLMediaEntry::AUTO_PLAY_KEY = MEDIA_AUTO_PLAY_KEY_STR; +const char* LLMediaEntry::AUTO_SCALE_KEY = MEDIA_AUTO_SCALE_KEY_STR; +const char* LLMediaEntry::AUTO_ZOOM_KEY = MEDIA_AUTO_ZOOM_KEY_STR; +const char* LLMediaEntry::FIRST_CLICK_INTERACT_KEY = MEDIA_FIRST_CLICK_INTERACT_KEY_STR; +const char* LLMediaEntry::WIDTH_PIXELS_KEY = MEDIA_WIDTH_PIXELS_KEY_STR; +const char* LLMediaEntry::HEIGHT_PIXELS_KEY = MEDIA_HEIGHT_PIXELS_KEY_STR; + +// "security" fields +const char* LLMediaEntry::WHITELIST_ENABLE_KEY = MEDIA_WHITELIST_ENABLE_KEY_STR; +const char* LLMediaEntry::WHITELIST_KEY = MEDIA_WHITELIST_KEY_STR; + +// "permissions" fields +const char* LLMediaEntry::PERMS_INTERACT_KEY = MEDIA_PERMS_INTERACT_KEY_STR; +const char* LLMediaEntry::PERMS_CONTROL_KEY = MEDIA_PERMS_CONTROL_KEY_STR; + +#define DEFAULT_URL_PREFIX "http://" + +// Constructor(s) +LLMediaEntry::LLMediaEntry() : + mAltImageEnable(false), + mControls(STANDARD), + mCurrentURL(""), + mHomeURL(""), + mAutoLoop(false), + mAutoPlay(false), + mAutoScale(false), + mAutoZoom(false), + mFirstClickInteract(false), + mWidthPixels(0), + mHeightPixels(0), + mWhiteListEnable(false), + // mWhiteList + mPermsInteract(PERM_ALL), + mPermsControl(PERM_ALL), + mMediaIDp(NULL) +{ +} + +LLMediaEntry::LLMediaEntry(const LLMediaEntry &rhs) : + mMediaIDp(NULL) +{ + // "general" fields + mAltImageEnable = rhs.mAltImageEnable; + mControls = rhs.mControls; + mCurrentURL = rhs.mCurrentURL; + mHomeURL = rhs.mHomeURL; + mAutoLoop = rhs.mAutoLoop; + mAutoPlay = rhs.mAutoPlay; + mAutoScale = rhs.mAutoScale; + mAutoZoom = rhs.mAutoZoom; + mFirstClickInteract = rhs.mFirstClickInteract; + mWidthPixels = rhs.mWidthPixels; + mHeightPixels = rhs.mHeightPixels; + + // "security" fields + mWhiteListEnable = rhs.mWhiteListEnable; + mWhiteList = rhs.mWhiteList; + + // "permissions" fields + mPermsInteract = rhs.mPermsInteract; + mPermsControl = rhs.mPermsControl; +} + +LLMediaEntry::~LLMediaEntry() +{ + if (NULL != mMediaIDp) + { + delete mMediaIDp; + } +} + +LLSD LLMediaEntry::asLLSD() const +{ + LLSD sd; + asLLSD(sd); + return sd; +} + +// +// LLSD functions +// +void LLMediaEntry::asLLSD(LLSD& sd) const +{ + // "general" fields + sd[ALT_IMAGE_ENABLE_KEY] = mAltImageEnable; + sd[CONTROLS_KEY] = (LLSD::Integer)mControls; + sd[CURRENT_URL_KEY] = mCurrentURL; + sd[HOME_URL_KEY] = mHomeURL; + sd[AUTO_LOOP_KEY] = mAutoLoop; + sd[AUTO_PLAY_KEY] = mAutoPlay; + sd[AUTO_SCALE_KEY] = mAutoScale; + sd[AUTO_ZOOM_KEY] = mAutoZoom; + sd[FIRST_CLICK_INTERACT_KEY] = mFirstClickInteract; + sd[WIDTH_PIXELS_KEY] = mWidthPixels; + sd[HEIGHT_PIXELS_KEY] = mHeightPixels; + + // "security" fields + sd[WHITELIST_ENABLE_KEY] = mWhiteListEnable; + for (U32 i=0; i<mWhiteList.size(); i++) + { + sd[WHITELIST_KEY].append(mWhiteList[i]); + } + + // "permissions" fields + sd[PERMS_INTERACT_KEY] = mPermsInteract; + sd[PERMS_CONTROL_KEY] = mPermsControl; +} + +// static +bool LLMediaEntry::checkLLSD(const LLSD& sd) +{ + if (sd.isUndefined()) return true; + LLMediaEntry temp; + return temp.fromLLSDInternal(sd, true); +} + +void LLMediaEntry::fromLLSD(const LLSD& sd) +{ + (void)fromLLSDInternal(sd, true); +} + +void LLMediaEntry::mergeFromLLSD(const LLSD& sd) +{ + (void)fromLLSDInternal(sd, false); +} + +// *NOTE: returns true if NO failures to set occurred, false otherwise. +// However, be aware that if a failure to set does occur, it does +// not stop setting fields from the LLSD! +bool LLMediaEntry::fromLLSDInternal(const LLSD& sd, bool overwrite) +{ + // *HACK: we sort of cheat here and assume that status is a + // bit field. We "or" into status and instead of returning + // it, we return whether it finishes off as LSL_STATUS_OK or not. + U32 status = LSL_STATUS_OK; + + // "general" fields + if ( overwrite || sd.has(ALT_IMAGE_ENABLE_KEY) ) + { + status |= setAltImageEnable( sd[ALT_IMAGE_ENABLE_KEY] ); + } + if ( overwrite || sd.has(CONTROLS_KEY) ) + { + status |= setControls( (MediaControls)(LLSD::Integer)sd[CONTROLS_KEY] ); + } + if ( overwrite || sd.has(CURRENT_URL_KEY) ) + { + // Don't check whitelist + status |= setCurrentURLInternal( sd[CURRENT_URL_KEY], false ); + } + if ( overwrite || sd.has(HOME_URL_KEY) ) + { + status |= setHomeURL( sd[HOME_URL_KEY] ); + } + if ( overwrite || sd.has(AUTO_LOOP_KEY) ) + { + status |= setAutoLoop( sd[AUTO_LOOP_KEY] ); + } + if ( overwrite || sd.has(AUTO_PLAY_KEY) ) + { + status |= setAutoPlay( sd[AUTO_PLAY_KEY] ); + } + if ( overwrite || sd.has(AUTO_SCALE_KEY) ) + { + status |= setAutoScale( sd[AUTO_SCALE_KEY] ); + } + if ( overwrite || sd.has(AUTO_ZOOM_KEY) ) + { + status |= setAutoZoom( sd[AUTO_ZOOM_KEY] ); + } + if ( overwrite || sd.has(FIRST_CLICK_INTERACT_KEY) ) + { + status |= setFirstClickInteract( sd[FIRST_CLICK_INTERACT_KEY] ); + } + if ( overwrite || sd.has(WIDTH_PIXELS_KEY) ) + { + status |= setWidthPixels( (LLSD::Integer)sd[WIDTH_PIXELS_KEY] ); + } + if ( overwrite || sd.has(HEIGHT_PIXELS_KEY) ) + { + status |= setHeightPixels( (LLSD::Integer)sd[HEIGHT_PIXELS_KEY] ); + } + + // "security" fields + if ( overwrite || sd.has(WHITELIST_ENABLE_KEY) ) + { + status |= setWhiteListEnable( sd[WHITELIST_ENABLE_KEY] ); + } + if ( overwrite || sd.has(WHITELIST_KEY) ) + { + status |= setWhiteList( sd[WHITELIST_KEY] ); + } + + // "permissions" fields + if ( overwrite || sd.has(PERMS_INTERACT_KEY) ) + { + status |= setPermsInteract( 0xff & (LLSD::Integer)sd[PERMS_INTERACT_KEY] ); + } + if ( overwrite || sd.has(PERMS_CONTROL_KEY) ) + { + status |= setPermsControl( 0xff & (LLSD::Integer)sd[PERMS_CONTROL_KEY] ); + } + + return LSL_STATUS_OK == status; +} + +LLMediaEntry& LLMediaEntry::operator=(const LLMediaEntry &rhs) +{ + if (this != &rhs) + { + // "general" fields + mAltImageEnable = rhs.mAltImageEnable; + mControls = rhs.mControls; + mCurrentURL = rhs.mCurrentURL; + mHomeURL = rhs.mHomeURL; + mAutoLoop = rhs.mAutoLoop; + mAutoPlay = rhs.mAutoPlay; + mAutoScale = rhs.mAutoScale; + mAutoZoom = rhs.mAutoZoom; + mFirstClickInteract = rhs.mFirstClickInteract; + mWidthPixels = rhs.mWidthPixels; + mHeightPixels = rhs.mHeightPixels; + + // "security" fields + mWhiteListEnable = rhs.mWhiteListEnable; + mWhiteList = rhs.mWhiteList; + + // "permissions" fields + mPermsInteract = rhs.mPermsInteract; + mPermsControl = rhs.mPermsControl; + } + + return *this; +} + +bool LLMediaEntry::operator==(const LLMediaEntry &rhs) const +{ + return ( + // "general" fields + mAltImageEnable == rhs.mAltImageEnable && + mControls == rhs.mControls && + mCurrentURL == rhs.mCurrentURL && + mHomeURL == rhs.mHomeURL && + mAutoLoop == rhs.mAutoLoop && + mAutoPlay == rhs.mAutoPlay && + mAutoScale == rhs.mAutoScale && + mAutoZoom == rhs.mAutoZoom && + mFirstClickInteract == rhs.mFirstClickInteract && + mWidthPixels == rhs.mWidthPixels && + mHeightPixels == rhs.mHeightPixels && + + // "security" fields + mWhiteListEnable == rhs.mWhiteListEnable && + mWhiteList == rhs.mWhiteList && + + // "permissions" fields + mPermsInteract == rhs.mPermsInteract && + mPermsControl == rhs.mPermsControl + + ); +} + +bool LLMediaEntry::operator!=(const LLMediaEntry &rhs) const +{ + return ( + // "general" fields + mAltImageEnable != rhs.mAltImageEnable || + mControls != rhs.mControls || + mCurrentURL != rhs.mCurrentURL || + mHomeURL != rhs.mHomeURL || + mAutoLoop != rhs.mAutoLoop || + mAutoPlay != rhs.mAutoPlay || + mAutoScale != rhs.mAutoScale || + mAutoZoom != rhs.mAutoZoom || + mFirstClickInteract != rhs.mFirstClickInteract || + mWidthPixels != rhs.mWidthPixels || + mHeightPixels != rhs.mHeightPixels || + + // "security" fields + mWhiteListEnable != rhs.mWhiteListEnable || + mWhiteList != rhs.mWhiteList || + + // "permissions" fields + mPermsInteract != rhs.mPermsInteract || + mPermsControl != rhs.mPermsControl + + ); +} + +U32 LLMediaEntry::setWhiteList( const std::vector<std::string> &whitelist ) +{ + // *NOTE: This code is VERY similar to the setWhitelist below. + // IF YOU CHANGE THIS IMPLEMENTATION, BE SURE TO CHANGE THE OTHER! + U32 size = 0; + U32 count = 0; + // First count to make sure the size constraint is not violated + std::vector<std::string>::const_iterator iter = whitelist.begin(); + std::vector<std::string>::const_iterator end = whitelist.end(); + for ( ; iter < end; ++iter) + { + const std::string &entry = (*iter); + size += entry.length() + 1; // Include one for \0 + count ++; + if (size > MAX_WHITELIST_SIZE || count > MAX_WHITELIST_COUNT) + { + return LSL_STATUS_BOUNDS_ERROR; + } + } + // Next clear the vector + mWhiteList.clear(); + // Then re-iterate and copy entries + iter = whitelist.begin(); + for ( ; iter < end; ++iter) + { + const std::string &entry = (*iter); + mWhiteList.push_back(entry); + } + return LSL_STATUS_OK; +} + +U32 LLMediaEntry::setWhiteList( const LLSD &whitelist ) +{ + // If whitelist is undef, this is a no-op. + if (whitelist.isUndefined()) return LSL_STATUS_OK; + + // However, if the whitelist is an empty array, erase it. + if (whitelist.isArray()) + { + // *NOTE: This code is VERY similar to the setWhitelist above. + // IF YOU CHANGE THIS IMPLEMENTATION, BE SURE TO CHANGE THE OTHER! + U32 size = 0; + U32 count = 0; + // First check to make sure the size and count constraints are not violated + LLSD::array_const_iterator iter = whitelist.beginArray(); + LLSD::array_const_iterator end = whitelist.endArray(); + for ( ; iter < end; ++iter) + { + const std::string &entry = (*iter).asString(); + size += entry.length() + 1; // Include one for \0 + count ++; + if (size > MAX_WHITELIST_SIZE || count > MAX_WHITELIST_COUNT) + { + return LSL_STATUS_BOUNDS_ERROR; + } + } + // Next clear the vector + mWhiteList.clear(); + // Then re-iterate and copy entries + iter = whitelist.beginArray(); + for ( ; iter < end; ++iter) + { + const std::string &entry = (*iter).asString(); + mWhiteList.push_back(entry); + } + return LSL_STATUS_OK; + } + else + { + return LSL_STATUS_MALFORMED_PARAMS; + } +} + + +static void prefix_with(std::string &str, const char *chars, const char *prefix) +{ + // Given string 'str', prefix all instances of any character in 'chars' + // with 'prefix' + size_t found = str.find_first_of(chars); + size_t prefix_len = strlen(prefix); + while (found != std::string::npos) + { + str.insert(found, prefix, prefix_len); + found = str.find_first_of(chars, found+prefix_len+1); + } +} + +static bool pattern_match(const std::string &candidate_str, const std::string &pattern) +{ + // If the pattern is empty, it matches + if (pattern.empty()) return true; + + // 'pattern' is a glob pattern, we only accept '*' chars + // copy it + std::string expression = pattern; + + // Escape perl's regexp chars with a backslash, except all "*" chars + prefix_with(expression, ".[{()\\+?|^$", "\\"); + prefix_with(expression, "*", "."); + + // case-insensitive matching: + boost::regex regexp(expression, boost::regex::perl|boost::regex::icase); + return boost::regex_match(candidate_str, regexp); +} + +bool LLMediaEntry::checkCandidateUrl(const std::string& url) const +{ + if (getWhiteListEnable()) + { + return checkUrlAgainstWhitelist(url, getWhiteList()); + } + else + { + return true; + } +} + +// static +bool LLMediaEntry::checkUrlAgainstWhitelist(const std::string& url, + const std::vector<std::string> &whitelist) +{ + bool passes = true; + // *NOTE: no entries? Don't check + if (whitelist.size() > 0) + { + passes = false; + + // Case insensitive: the reason why we toUpper both this and the + // filter + std::string candidate_url = url; + // Use lluri to see if there is a path part in the candidate URL. No path? Assume "/" + LLURI candidate_uri(candidate_url); + std::vector<std::string>::const_iterator iter = whitelist.begin(); + std::vector<std::string>::const_iterator end = whitelist.end(); + for ( ; iter < end; ++iter ) + { + std::string filter = *iter; + + LLURI filter_uri(filter); + bool scheme_passes = pattern_match( candidate_uri.scheme(), filter_uri.scheme() ); + if (filter_uri.scheme().empty()) + { + filter_uri = LLURI(DEFAULT_URL_PREFIX + filter); + } + bool authority_passes = pattern_match( candidate_uri.authority(), filter_uri.authority() ); + bool path_passes = pattern_match( candidate_uri.escapedPath(), filter_uri.escapedPath() ); + + if (scheme_passes && authority_passes && path_passes) + { + passes = true; + break; + } + } + } + return passes; +} + +U32 LLMediaEntry::setStringFieldWithLimit( std::string &field, const std::string &value, U32 limit ) +{ + if ( value.length() > limit ) + { + return LSL_STATUS_BOUNDS_ERROR; + } + else + { + field = value; + return LSL_STATUS_OK; + } +} + +U32 LLMediaEntry::setControls(LLMediaEntry::MediaControls controls) +{ + if (controls == STANDARD || + controls == MINI) + { + mControls = controls; + return LSL_STATUS_OK; + } + return LSL_STATUS_BOUNDS_ERROR; +} + +U32 LLMediaEntry::setPermsInteract( U8 val ) +{ + mPermsInteract = val & PERM_MASK; + return LSL_STATUS_OK; +} + +U32 LLMediaEntry::setPermsControl( U8 val ) +{ + mPermsControl = val & PERM_MASK; + return LSL_STATUS_OK; +} + +U32 LLMediaEntry::setCurrentURL(const std::string& current_url) +{ + return setCurrentURLInternal( current_url, true ); +} + +U32 LLMediaEntry::setCurrentURLInternal(const std::string& current_url, bool check_whitelist) +{ + if ( ! check_whitelist || checkCandidateUrl(current_url)) + { + return setStringFieldWithLimit( mCurrentURL, current_url, MAX_URL_LENGTH ); + } + else + { + return LSL_STATUS_WHITELIST_FAILED; + } +} + +U32 LLMediaEntry::setHomeURL(const std::string& home_url) +{ + return setStringFieldWithLimit( mHomeURL, home_url, MAX_URL_LENGTH ); +} + +U32 LLMediaEntry::setWidthPixels(U16 width) +{ + if (width > MAX_WIDTH_PIXELS) return LSL_STATUS_BOUNDS_ERROR; + mWidthPixels = width; + return LSL_STATUS_OK; +} + +U32 LLMediaEntry::setHeightPixels(U16 height) +{ + if (height > MAX_HEIGHT_PIXELS) return LSL_STATUS_BOUNDS_ERROR; + mHeightPixels = height; + return LSL_STATUS_OK; +} + +const LLUUID &LLMediaEntry::getMediaID() const +{ + // Lazily generate media ID + if (NULL == mMediaIDp) + { + mMediaIDp = new LLUUID(); + mMediaIDp->generate(); + } + return *mMediaIDp; +} + diff --git a/indra/llprimitive/llmediaentry.h b/indra/llprimitive/llmediaentry.h new file mode 100644 index 0000000000..2a5486666a --- /dev/null +++ b/indra/llprimitive/llmediaentry.h @@ -0,0 +1,228 @@ +/** + * @file llmediaentry.h + * @brief This is a single instance of media data related to the face of a prim + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLMEDIAENTRY_H +#define LL_LLMEDIAENTRY_H + +#include "llsd.h" +#include "llstring.h" + +// For return values of set* +#include "lllslconstants.h" + +class LLMediaEntry +{ +public: + enum MediaControls { + STANDARD = 0, + MINI + }; + + // Constructors + LLMediaEntry(); + LLMediaEntry(const LLMediaEntry &rhs); + + LLMediaEntry &operator=(const LLMediaEntry &rhs); + virtual ~LLMediaEntry(); + + bool operator==(const LLMediaEntry &rhs) const; + bool operator!=(const LLMediaEntry &rhs) const; + + // Render as LLSD + LLSD asLLSD() const; + void asLLSD(LLSD& sd) const; + operator LLSD() const { return asLLSD(); } + // Returns false iff the given LLSD contains fields that violate any bounds + // limits. + static bool checkLLSD(const LLSD& sd); + // This doesn't merge, it overwrites the data, so will use + // LLSD defaults if need be. Note: does not check limits! + // Use checkLLSD() above first to ensure the LLSD is valid. + void fromLLSD(const LLSD& sd); + // This merges data from the incoming LLSD into our fields. + // Note that it also does NOT check limits! Use checkLLSD() above first. + void mergeFromLLSD(const LLSD& sd); + + // "general" fields + bool getAltImageEnable() const { return mAltImageEnable; } + MediaControls getControls() const { return mControls; } + std::string getCurrentURL() const { return mCurrentURL; } + std::string getHomeURL() const { return mHomeURL; } + bool getAutoLoop() const { return mAutoLoop; } + bool getAutoPlay() const { return mAutoPlay; } + bool getAutoScale() const { return mAutoScale; } + bool getAutoZoom() const { return mAutoZoom; } + bool getFirstClickInteract() const { return mFirstClickInteract; } + U16 getWidthPixels() const { return mWidthPixels; } + U16 getHeightPixels() const { return mHeightPixels; } + + // "security" fields + bool getWhiteListEnable() const { return mWhiteListEnable; } + const std::vector<std::string> &getWhiteList() const { return mWhiteList; } + + // "permissions" fields + U8 getPermsInteract() const { return mPermsInteract; } + U8 getPermsControl() const { return mPermsControl; } + + // Setters. Those that return a U32 return a status error code + // See lllslconstants.h + + // "general" fields + U32 setAltImageEnable(bool alt_image_enable) { mAltImageEnable = alt_image_enable; return LSL_STATUS_OK; } + U32 setControls(MediaControls controls); + U32 setCurrentURL(const std::string& current_url); + U32 setHomeURL(const std::string& home_url); + U32 setAutoLoop(bool auto_loop) { mAutoLoop = auto_loop; return LSL_STATUS_OK; } + U32 setAutoPlay(bool auto_play) { mAutoPlay = auto_play; return LSL_STATUS_OK; } + U32 setAutoScale(bool auto_scale) { mAutoScale = auto_scale; return LSL_STATUS_OK; } + U32 setAutoZoom(bool auto_zoom) { mAutoZoom = auto_zoom; return LSL_STATUS_OK; } + U32 setFirstClickInteract(bool first_click) { mFirstClickInteract = first_click; return LSL_STATUS_OK; } + U32 setWidthPixels(U16 width); + U32 setHeightPixels(U16 height); + + // "security" fields + U32 setWhiteListEnable( bool whitelist_enable ) { mWhiteListEnable = whitelist_enable; return LSL_STATUS_OK; } + U32 setWhiteList( const std::vector<std::string> &whitelist ); + U32 setWhiteList( const LLSD &whitelist ); // takes an LLSD array + + // "permissions" fields + U32 setPermsInteract( U8 val ); + U32 setPermsControl( U8 val ); + + const LLUUID& getMediaID() const; + + // Helper function to check a candidate URL against the whitelist + // Returns true iff candidate URL passes (or if there is no whitelist), false otherwise + bool checkCandidateUrl(const std::string& url) const; + +public: + // Static function to check a URL against a whitelist + // Returns true iff url passes the given whitelist + static bool checkUrlAgainstWhitelist(const std::string &url, + const std::vector<std::string> &whitelist); + +public: + // LLSD key defines + // "general" fields + static const char* ALT_IMAGE_ENABLE_KEY; + static const char* CONTROLS_KEY; + static const char* CURRENT_URL_KEY; + static const char* HOME_URL_KEY; + static const char* AUTO_LOOP_KEY; + static const char* AUTO_PLAY_KEY; + static const char* AUTO_SCALE_KEY; + static const char* AUTO_ZOOM_KEY; + static const char* FIRST_CLICK_INTERACT_KEY; + static const char* WIDTH_PIXELS_KEY; + static const char* HEIGHT_PIXELS_KEY; + + // "security" fields + static const char* WHITELIST_ENABLE_KEY; + static const char* WHITELIST_KEY; + + // "permissions" fields + static const char* PERMS_INTERACT_KEY; + static const char* PERMS_CONTROL_KEY; + + // Field enumerations & constants + + // *NOTE: DO NOT change the order of these, and do not insert values + // in the middle! + // Add values to the end, and make sure to change PARAM_MAX_ID! + enum Fields { + ALT_IMAGE_ENABLE_ID = 0, + CONTROLS_ID = 1, + CURRENT_URL_ID = 2, + HOME_URL_ID = 3, + AUTO_LOOP_ID = 4, + AUTO_PLAY_ID = 5, + AUTO_SCALE_ID = 6, + AUTO_ZOOM_ID = 7, + FIRST_CLICK_INTERACT_ID = 8, + WIDTH_PIXELS_ID = 9, + HEIGHT_PIXELS_ID = 10, + WHITELIST_ENABLE_ID = 11, + WHITELIST_ID = 12, + PERMS_INTERACT_ID = 13, + PERMS_CONTROL_ID = 14, + PARAM_MAX_ID = PERMS_CONTROL_ID + }; + + // "permissions" values + // (e.g. (PERM_OWNER | PERM_GROUP) sets permissions on for OWNER and GROUP + static const U8 PERM_NONE = 0x0; + static const U8 PERM_OWNER = 0x1; + static const U8 PERM_GROUP = 0x2; + static const U8 PERM_ANYONE = 0x4; + static const U8 PERM_ALL = PERM_OWNER|PERM_GROUP|PERM_ANYONE; + static const U8 PERM_MASK = PERM_OWNER|PERM_GROUP|PERM_ANYONE; + + // Limits (in bytes) + static const U32 MAX_URL_LENGTH = 1024; + static const U32 MAX_WHITELIST_SIZE = 1024; + static const U32 MAX_WHITELIST_COUNT = 64; + static const U16 MAX_WIDTH_PIXELS = 2048; + static const U16 MAX_HEIGHT_PIXELS = 2048; + +private: + + U32 setStringFieldWithLimit( std::string &field, const std::string &value, U32 limit ); + U32 setCurrentURLInternal( const std::string &url, bool check_whitelist); + bool fromLLSDInternal(const LLSD &sd, bool overwrite); + +private: + // "general" fields + bool mAltImageEnable; + MediaControls mControls; + std::string mCurrentURL; + std::string mHomeURL; + bool mAutoLoop; + bool mAutoPlay; + bool mAutoScale; + bool mAutoZoom; + bool mFirstClickInteract; + U16 mWidthPixels; + U16 mHeightPixels; + + // "security" fields + bool mWhiteListEnable; + std::vector<std::string> mWhiteList; + + // "permissions" fields + U8 mPermsInteract; + U8 mPermsControl; + + mutable LLUUID *mMediaIDp; // temporary id assigned to media on the viewer +}; + +#endif + diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp index b925d97b5c..b102254b62 100644 --- a/indra/llprimitive/llprimitive.cpp +++ b/indra/llprimitive/llprimitive.cpp @@ -1134,6 +1134,7 @@ S32 LLPrimitive::unpackTEMessage(LLMessageSystem *mesgsys, char *block_name, con color.mV[VALPHA] = F32(255 - coloru.mV[VALPHA]) / 255.f; retval |= setTEColor(i, color); + } return retval; diff --git a/indra/llprimitive/lltextureentry.cpp b/indra/llprimitive/lltextureentry.cpp index 3bcd831142..b534939dfc 100644 --- a/indra/llprimitive/lltextureentry.cpp +++ b/indra/llprimitive/lltextureentry.cpp @@ -32,13 +32,31 @@ #include "linden_common.h" +#include "lluuid.h" +#include "llmediaentry.h" #include "lltextureentry.h" #include "llsdutil.h" +#include "v4color.h" const U8 DEFAULT_BUMP_CODE = 0; // no bump or shininess const LLTextureEntry LLTextureEntry::null; +// Some LLSD keys. Do not change these! +#define OBJECT_ID_KEY_STR "object_id" +#define TEXTURE_INDEX_KEY_STR "texture_index" +#define OBJECT_MEDIA_VERSION_KEY_STR "object_media_version" +#define OBJECT_MEDIA_DATA_KEY_STR "object_media_data" +#define TEXTURE_MEDIA_DATA_KEY_STR "media_data" + +/*static*/ const char* LLTextureEntry::OBJECT_ID_KEY = OBJECT_ID_KEY_STR; +/*static*/ const char* LLTextureEntry::OBJECT_MEDIA_DATA_KEY = OBJECT_MEDIA_DATA_KEY_STR; +/*static*/ const char* LLTextureEntry::MEDIA_VERSION_KEY = OBJECT_MEDIA_VERSION_KEY_STR; +/*static*/ const char* LLTextureEntry::TEXTURE_INDEX_KEY = TEXTURE_INDEX_KEY_STR; +/*static*/ const char* LLTextureEntry::TEXTURE_MEDIA_DATA_KEY = TEXTURE_MEDIA_DATA_KEY_STR; + +static const std::string MEDIA_VERSION_STRING_PREFIX = "x-mv:"; + // static LLTextureEntry* LLTextureEntry::newTextureEntry() { @@ -47,16 +65,19 @@ LLTextureEntry* LLTextureEntry::newTextureEntry() //=============================================================== LLTextureEntry::LLTextureEntry() + : mMediaEntry(NULL) { init(LLUUID::null,1.f,1.f,0.f,0.f,0.f,DEFAULT_BUMP_CODE); } LLTextureEntry::LLTextureEntry(const LLUUID& tex_id) + : mMediaEntry(NULL) { init(tex_id,1.f,1.f,0.f,0.f,0.f,DEFAULT_BUMP_CODE); } LLTextureEntry::LLTextureEntry(const LLTextureEntry &rhs) + : mMediaEntry(NULL) { mID = rhs.mID; mScaleS = rhs.mScaleS; @@ -68,6 +89,10 @@ LLTextureEntry::LLTextureEntry(const LLTextureEntry &rhs) mBump = rhs.mBump; mMediaFlags = rhs.mMediaFlags; mGlow = rhs.mGlow; + if (rhs.mMediaEntry != NULL) { + // Make a copy + mMediaEntry = new LLMediaEntry(*rhs.mMediaEntry); + } } LLTextureEntry &LLTextureEntry::operator=(const LLTextureEntry &rhs) @@ -84,6 +109,16 @@ LLTextureEntry &LLTextureEntry::operator=(const LLTextureEntry &rhs) mBump = rhs.mBump; mMediaFlags = rhs.mMediaFlags; mGlow = rhs.mGlow; + if (mMediaEntry != NULL) { + delete mMediaEntry; + } + if (rhs.mMediaEntry != NULL) { + // Make a copy + mMediaEntry = new LLMediaEntry(*rhs.mMediaEntry); + } + else { + mMediaEntry = NULL; + } } return *this; @@ -103,10 +138,19 @@ void LLTextureEntry::init(const LLUUID& tex_id, F32 scale_s, F32 scale_t, F32 of mGlow = 0; setColor(LLColor4(1.f, 1.f, 1.f, 1.f)); + if (mMediaEntry != NULL) { + delete mMediaEntry; + } + mMediaEntry = NULL; } LLTextureEntry::~LLTextureEntry() { + if(mMediaEntry) + { + delete mMediaEntry; + mMediaEntry = NULL; + } } bool LLTextureEntry::operator!=(const LLTextureEntry &rhs) const @@ -158,10 +202,17 @@ void LLTextureEntry::asLLSD(LLSD& sd) const sd["bump"] = getBumpShiny(); sd["fullbright"] = getFullbright(); sd["media_flags"] = mMediaFlags; + if (hasMedia()) { + LLSD mediaData; + if (NULL != getMediaData()) { + getMediaData()->asLLSD(mediaData); + } + sd[TEXTURE_MEDIA_DATA_KEY] = mediaData; + } sd["glow"] = mGlow; } -bool LLTextureEntry::fromLLSD(LLSD& sd) +bool LLTextureEntry::fromLLSD(const LLSD& sd) { const char *w, *x; w = "imageid"; @@ -206,6 +257,17 @@ bool LLTextureEntry::fromLLSD(LLSD& sd) { setMediaTexGen( sd[w].asInteger() ); } else goto fail; + // If the "has media" flag doesn't match the fact that + // media data exists, updateMediaData will "fix" it + // by either clearing or setting the flag + w = TEXTURE_MEDIA_DATA_KEY; + if (hasMedia() != sd.has(w)) + { + llwarns << "LLTextureEntry::fromLLSD: media_flags (" << hasMedia() << + ") does not match presence of media_data (" << sd.has(w) << "). Fixing." << llendl; + } + updateMediaData(sd[w]); + w = "glow"; if (sd.has(w)) { @@ -370,7 +432,19 @@ S32 LLTextureEntry::setMediaTexGen(U8 media) if (mMediaFlags != media) { mMediaFlags = media; - return TEM_CHANGE_TEXTURE; + + // Special code for media handling + if( hasMedia() && mMediaEntry == NULL) + { + mMediaEntry = new LLMediaEntry; + } + else if ( ! hasMedia() && mMediaEntry != NULL) + { + delete mMediaEntry; + mMediaEntry = NULL; + } + + return TEM_CHANGE_MEDIA; } return TEM_CHANGE_NONE; } @@ -430,7 +504,19 @@ S32 LLTextureEntry::setMediaFlags(U8 media_flags) { mMediaFlags &= ~TEM_MEDIA_MASK; mMediaFlags |= media_flags; - return TEM_CHANGE_TEXTURE; + + // Special code for media handling + if( hasMedia() && mMediaEntry == NULL) + { + mMediaEntry = new LLMediaEntry; + } + else if ( ! hasMedia() && mMediaEntry != NULL) + { + delete mMediaEntry; + mMediaEntry = NULL; + } + + return TEM_CHANGE_MEDIA; } return TEM_CHANGE_NONE; } @@ -456,3 +542,107 @@ S32 LLTextureEntry::setGlow(F32 glow) } return TEM_CHANGE_NONE; } + +void LLTextureEntry::setMediaData(const LLMediaEntry &media_entry) +{ + mMediaFlags |= MF_HAS_MEDIA; + if (NULL != mMediaEntry) + { + delete mMediaEntry; + } + mMediaEntry = new LLMediaEntry(media_entry); +} + +bool LLTextureEntry::updateMediaData(const LLSD& media_data) +{ + if (media_data.isUndefined()) + { + // clear the media data + clearMediaData(); + return false; + } + else { + mMediaFlags |= MF_HAS_MEDIA; + if (mMediaEntry == NULL) + { + mMediaEntry = new LLMediaEntry; + } + // *NOTE: this will *clobber* all of the fields in mMediaEntry + // with whatever fields are present (or not present) in media_data! + mMediaEntry->fromLLSD(media_data); + return true; + } +} + +void LLTextureEntry::clearMediaData() +{ + mMediaFlags &= ~MF_HAS_MEDIA; + if (mMediaEntry != NULL) { + delete mMediaEntry; + } + mMediaEntry = NULL; +} + +void LLTextureEntry::mergeIntoMediaData(const LLSD& media_fields) +{ + mMediaFlags |= MF_HAS_MEDIA; + if (mMediaEntry == NULL) + { + mMediaEntry = new LLMediaEntry; + } + // *NOTE: this will *merge* the data in media_fields + // with the data in our media entry + mMediaEntry->mergeFromLLSD(media_fields); +} + +//static +std::string LLTextureEntry::touchMediaVersionString(const std::string &in_version, const LLUUID &agent_id) +{ + // XXX TODO: make media version string binary (base64-encoded?) + // Media "URL" is a representation of a version and the last-touched agent + // x-mv:nnnnn/agent-id + // where "nnnnn" is version number + // *NOTE: not the most efficient code in the world... + U32 current_version = getVersionFromMediaVersionString(in_version) + 1; + const size_t MAX_VERSION_LEN = 10; // 2^32 fits in 10 decimal digits + char buf[MAX_VERSION_LEN+1]; + snprintf(buf, (int)MAX_VERSION_LEN+1, "%0*u", (int)MAX_VERSION_LEN, current_version); // added int cast to fix warning/breakage on mac. + return MEDIA_VERSION_STRING_PREFIX + buf + "/" + agent_id.asString(); +} + +//static +U32 LLTextureEntry::getVersionFromMediaVersionString(const std::string &version_string) +{ + U32 version = 0; + if (!version_string.empty()) + { + size_t found = version_string.find(MEDIA_VERSION_STRING_PREFIX); + if (found != std::string::npos) + { + found = version_string.find_first_of("/", found); + std::string v = version_string.substr(MEDIA_VERSION_STRING_PREFIX.length(), found); + version = strtoul(v.c_str(),NULL,10); + } + } + return version; +} + +//static +LLUUID LLTextureEntry::getAgentIDFromMediaVersionString(const std::string &version_string) +{ + LLUUID id; + if (!version_string.empty()) + { + size_t found = version_string.find(MEDIA_VERSION_STRING_PREFIX); + if (found != std::string::npos) + { + found = version_string.find_first_of("/", found); + if (found != std::string::npos) + { + std::string v = version_string.substr(found + 1); + id.set(v); + } + } + } + return id; +} diff --git a/indra/llprimitive/lltextureentry.h b/indra/llprimitive/lltextureentry.h index 84870e93e6..8d2834f78c 100644 --- a/indra/llprimitive/lltextureentry.h +++ b/indra/llprimitive/lltextureentry.h @@ -37,10 +37,13 @@ #include "v4color.h" #include "llsd.h" +// These bits are used while unpacking TEM messages to tell which aspects of +// the texture entry changed. const S32 TEM_CHANGE_NONE = 0x0; const S32 TEM_CHANGE_COLOR = 0x1; const S32 TEM_CHANGE_TEXTURE = 0x2; -const S32 TEM_INVALID = 0x4; +const S32 TEM_CHANGE_MEDIA = 0x4; +const S32 TEM_INVALID = 0x8; const S32 TEM_BUMPMAP_COUNT = 32; @@ -65,6 +68,8 @@ const S32 TEM_MEDIA_MASK = 0x01; const S32 TEM_TEX_GEN_MASK = 0x06; const S32 TEM_TEX_GEN_SHIFT = 1; +// forward declarations +class LLMediaEntry; class LLTextureEntry { @@ -92,7 +97,7 @@ public: LLSD asLLSD() const; void asLLSD(LLSD& sd) const; operator LLSD() const { return asLLSD(); } - bool fromLLSD(LLSD& sd); + bool fromLLSD(const LLSD& sd); virtual LLTextureEntry* newBlank() const; virtual LLTextureEntry* newCopy() const; @@ -140,9 +145,35 @@ public: U8 getTexGen() const { return mMediaFlags & TEM_TEX_GEN_MASK; } U8 getMediaTexGen() const { return mMediaFlags; } F32 getGlow() const { return mGlow; } - + + // *NOTE: it is possible for hasMedia() to return true, but getMediaData() to return NULL. + // CONVERSELY, it is also possible for hasMedia() to return false, but getMediaData() + // to NOT return NULL. + bool hasMedia() const { return (bool)(mMediaFlags & MF_HAS_MEDIA); } + LLMediaEntry* getMediaData() const { return mMediaEntry; } + + // Completely change the media data on this texture entry. + void setMediaData(const LLMediaEntry &media_entry); + // Returns true if media data was updated, false if it was cleared + bool updateMediaData(const LLSD& media_data); + // Clears media data, and sets the media flags bit to 0 + void clearMediaData(); + // Merges the given LLSD of media fields with this media entry. + // Only those fields that are set that match the keys in + // LLMediaEntry will be affected. If no fields are set or if + // the LLSD is undefined, this is a no-op. + void mergeIntoMediaData(const LLSD& media_fields); + + // Takes a media version string (an empty string or a previously-returned string) + // and returns a "touched" string, touched by agent_id + static std::string touchMediaVersionString(const std::string &in_version, const LLUUID &agent_id); + // Given a media version string, return the version + static U32 getVersionFromMediaVersionString(const std::string &version_string); + // Given a media version string, return the UUID of the agent + static LLUUID getAgentIDFromMediaVersionString(const std::string &version_string); + // Media flags - enum { MF_NONE = 0x0, MF_WEB_PAGE = 0x1 }; + enum { MF_NONE = 0x0, MF_HAS_MEDIA = 0x1 }; public: F32 mScaleS; // S, T offset @@ -152,6 +183,14 @@ public: F32 mRotation; // anti-clockwise rotation in rad about the bottom left corner static const LLTextureEntry null; + + // LLSD key defines + static const char* OBJECT_ID_KEY; + static const char* OBJECT_MEDIA_DATA_KEY; + static const char* MEDIA_VERSION_KEY; + static const char* TEXTURE_INDEX_KEY; + static const char* TEXTURE_MEDIA_DATA_KEY; + protected: LLUUID mID; // Texture GUID LLColor4 mColor; @@ -159,6 +198,9 @@ protected: U8 mMediaFlags; // replace with web page, movie, etc. F32 mGlow; + // Note the media data is not sent via the same message structure as the rest of the TE + LLMediaEntry* mMediaEntry; // The media data for the face + // NOTE: when adding new data to this class, in addition to adding it to the serializers asLLSD/fromLLSD and the // message packers (e.g. LLPrimitive::packTEMessage) you must also implement its copy in LLPrimitive::copyTEs() diff --git a/indra/llprimitive/tests/llmediaentry_test.cpp b/indra/llprimitive/tests/llmediaentry_test.cpp new file mode 100644 index 0000000000..72478d0459 --- /dev/null +++ b/indra/llprimitive/tests/llmediaentry_test.cpp @@ -0,0 +1,484 @@ +/** + * @file llmediaentry_test.cpp + * @brief llmediaentry unit tests + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * Copyright (c) 2001-2009, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "lltut.h" +#include "boost/lexical_cast.hpp" +#include "llstring.h" +#include "llsdutil.h" +#include "llsdserialize.h" + +#include "../llmediaentry.h" +#include "lllslconstants.h" + +#define DEFAULT_MEDIA_ENTRY "<llsd>\n\ + <map>\n\ + <key>alt_image_enable</key>\n\ + <boolean>0</boolean>\n\ + <key>auto_loop</key>\n\ + <boolean>0</boolean>\n\ + <key>auto_play</key>\n\ + <boolean>0</boolean>\n\ + <key>auto_scale</key>\n\ + <boolean>0</boolean>\n\ + <key>auto_zoom</key>\n\ + <boolean>0</boolean>\n\ + <key>controls</key>\n\ + <integer>0</integer>\n\ + <key>current_url</key>\n\ + <string />\n\ + <key>first_click_interact</key>\n\ + <boolean>0</boolean>\n\ + <key>height_pixels</key>\n\ + <integer>0</integer>\n\ + <key>home_url</key>\n\ + <string />\n\ + <key>perms_control</key>\n\ + <integer>7</integer>\n\ + <key>perms_interact</key>\n\ + <integer>7</integer>\n\ + <key>whitelist_enable</key>\n\ + <boolean>0</boolean>\n\ + <key>width_pixels</key>\n\ + <integer>0</integer>\n\ + </map>\n\ + </llsd>" + +#define EMPTY_MEDIA_ENTRY "<llsd>\n\ + <map>\n\ + <key>alt_image_enable</key>\n\ + <boolean>0</boolean>\n\ + <key>auto_loop</key>\n\ + <boolean>0</boolean>\n\ + <key>auto_play</key>\n\ + <boolean>0</boolean>\n\ + <key>auto_scale</key>\n\ + <boolean>0</boolean>\n\ + <key>auto_zoom</key>\n\ + <boolean>0</boolean>\n\ + <key>controls</key>\n\ + <integer>0</integer>\n\ + <key>current_url</key>\n\ + <string />\n\ + <key>first_click_interact</key>\n\ + <boolean>0</boolean>\n\ + <key>height_pixels</key>\n\ + <integer>0</integer>\n\ + <key>home_url</key>\n\ + <string />\n\ + <key>perms_control</key>\n\ + <integer>0</integer>\n\ + <key>perms_interact</key>\n\ + <integer>0</integer>\n\ + <key>whitelist_enable</key>\n\ + <boolean>0</boolean>\n\ + <key>width_pixels</key>\n\ + <integer>0</integer>\n\ + </map>\n\ + </llsd>" + +#define PARTIAL_MEDIA_ENTRY(CURRENT_URL) "<llsd>\n\ + <map>\n\ + <key>alt_image_enable</key>\n\ + <boolean>0</boolean>\n\ + <key>auto_loop</key>\n\ + <boolean>0</boolean>\n\ + <key>auto_play</key>\n\ + <boolean>0</boolean>\n\ + <key>auto_scale</key>\n\ + <boolean>0</boolean>\n\ + <key>auto_zoom</key>\n\ + <boolean>0</boolean>\n\ + <key>controls</key>\n\ + <integer>0</integer>\n\ + <key>current_url</key>\n\ + <string>" CURRENT_URL "</string>\n\ + <key>first_click_interact</key>\n\ + <boolean>0</boolean>\n\ + <key>height_pixels</key>\n\ + <integer>0</integer>\n\ + <key>home_url</key>\n\ + <string />\n\ + <key>perms_control</key>\n\ + <integer>0</integer>\n\ + <key>perms_interact</key>\n\ + <integer>0</integer>\n\ + <key>whitelist_enable</key>\n\ + <boolean>0</boolean>\n\ + <key>width_pixels</key>\n\ + <integer>0</integer>\n\ + </map>\n\ + </llsd>" + +namespace tut +{ + // this is fixture data that gets created before each test and destroyed + // after each test. this is where we put all of the setup/takedown code + // and data needed for each test. + struct MediaEntry_test + { + MediaEntry_test() { + emptyMediaEntryStr = EMPTY_MEDIA_ENTRY; + std::istringstream e(EMPTY_MEDIA_ENTRY); + LLSDSerialize::fromXML(emptyMediaEntryLLSD, e); + defaultMediaEntryStr = DEFAULT_MEDIA_ENTRY; + std::istringstream d(DEFAULT_MEDIA_ENTRY); + LLSDSerialize::fromXML(defaultMediaEntryLLSD, d); + } + std::string emptyMediaEntryStr; + LLSD emptyMediaEntryLLSD; + std::string defaultMediaEntryStr; + LLSD defaultMediaEntryLLSD; + }; + + typedef test_group<MediaEntry_test, 55> factory; + typedef factory::object object; +} + + +namespace +{ + // this is for naming our tests to make pretty output + tut::factory tf("MediaEntry Test"); +} + +namespace tut +{ + bool llsd_equals(const LLSD& a, const LLSD& b) { + // cheesy, brute force, but it works + return std::string(ll_pretty_print_sd(a)) == std::string(ll_pretty_print_sd(b)); + } + + void ensure_llsd_equals(const std::string& msg, const LLSD& expected, const LLSD& actual) + { + if (! llsd_equals(expected, actual)) + { + std::string message = msg; + message += ": actual: "; + message += ll_pretty_print_sd(actual); + message += "\n expected: "; + message += ll_pretty_print_sd(expected); + message += "\n"; + ensure(message, false); + } + } + + void ensure_string_equals(const std::string& msg, const std::string& expected, const std::string& actual) + { + if ( expected != actual ) + { + std::string message = msg; + message += ": actual: "; + message += actual; + message += "\n expected: "; + message += expected; + message += "\n"; + ensure(message, false); + } + } + + void set_whitelist(LLMediaEntry &entry, const char *str) + { + std::vector<std::string> tokens; + LLStringUtil::getTokens(std::string(str), tokens, ","); + entry.setWhiteList(tokens); + } + + void whitelist_test(bool enable, const char *whitelist, const char *candidate_url, bool expected_pass) + { + std::string message = "Whitelist test"; + LLMediaEntry entry; + entry.setWhiteListEnable(enable); + set_whitelist(entry, whitelist); + bool passed_whitelist = entry.checkCandidateUrl(candidate_url); + if (passed_whitelist != expected_pass) + { + message += " failed: expected "; + message += (expected_pass) ? "" : "NOT "; + message += "to match\nwhitelist = "; + message += whitelist; + message += "\ncandidate_url = "; + message += candidate_url; + } + ensure(message, expected_pass == passed_whitelist); + } + + void whitelist_test(const char *whitelist, const char *candidate_url, bool expected_pass) + { + whitelist_test(true, whitelist, candidate_url, expected_pass); + } + void whitelist_test(const char *whitelist, const char *candidate_url) + { + whitelist_test(true, whitelist, candidate_url, true); + } + + template<> template<> + void object::test<1>() + { + set_test_name("Test LLMediaEntry Instantiation"); + LLMediaEntry entry; + ensure_llsd_equals(get_test_name(), defaultMediaEntryLLSD, entry.asLLSD()); + + } + + template<> template<> + void object::test<2>() + { + set_test_name("Test LLMediaEntry Instantiation from LLSD"); + LLMediaEntry entry; + LLSD sd; + entry.fromLLSD(sd); + ensure_llsd_equals(get_test_name() + " failed", emptyMediaEntryLLSD, entry.asLLSD()); + } + + template<> template<> + void object::test<3>() + { + set_test_name("Test LLMediaEntry Partial Instantiation from LLSD"); + LLMediaEntry entry; + LLSD sd; + sd[LLMediaEntry::CURRENT_URL_KEY] = "http://www.example.com"; + entry.fromLLSD(sd); + LLSD golden; + std::istringstream p(PARTIAL_MEDIA_ENTRY("http://www.example.com")); + LLSDSerialize::fromXML(golden,p); + ensure_llsd_equals(get_test_name() + " failed", golden, entry.asLLSD()); + } + + // limit tests + const char *URL_OK = "http://www.example.com"; + const char *URL_TOO_BIG = "http://www.example.com.qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"; + + template<> template<> + void object::test<4>() + { + set_test_name("Test Limits on setting current URL"); + LLMediaEntry entry; + U32 status = entry.setCurrentURL(URL_OK); + ensure(get_test_name() + " ok failed", status == LSL_STATUS_OK); + status = entry.setCurrentURL(URL_TOO_BIG); + ensure(get_test_name() + " ok failed", status == LSL_STATUS_BOUNDS_ERROR); + } + + template<> template<> + void object::test<5>() + { + set_test_name("Test Limits on setting home URL"); + LLMediaEntry entry; + U32 status = entry.setHomeURL(URL_OK); + ensure(get_test_name() + " ok failed", status == LSL_STATUS_OK); + status = entry.setHomeURL(URL_TOO_BIG); + ensure(get_test_name() + " ok failed", status == LSL_STATUS_BOUNDS_ERROR); + } + + template<> template<> + void object::test<6>() + { + set_test_name("Test Limits on setting whitelist"); + + // Test a valid list + LLMediaEntry entry; + std::vector<std::string> whitelist; + whitelist.push_back(std::string(URL_OK)); + S32 status = entry.setWhiteList(whitelist); + ensure(get_test_name() + " invalid result", status == LSL_STATUS_OK); + ensure(get_test_name() + " failed", whitelist == entry.getWhiteList()); + } + + template<> template<> + void object::test<7>() + { + set_test_name("Test Limits on setting whitelist too big"); + + // Test an invalid list + LLMediaEntry entry; + std::vector<std::string> whitelist, empty; + whitelist.push_back(std::string(URL_OK)); + whitelist.push_back(std::string(URL_TOO_BIG)); + S32 status = entry.setWhiteList(whitelist); + ensure(get_test_name() + " invalid result", status == LSL_STATUS_BOUNDS_ERROR); + ensure(get_test_name() + " failed", empty == entry.getWhiteList()); + } + + template<> template<> + void object::test<8>() + { + set_test_name("Test Limits on setting whitelist too many"); + + // Test an invalid list + LLMediaEntry entry; + std::vector<std::string> whitelist, empty; + for (int i=0; i < LLMediaEntry::MAX_WHITELIST_SIZE+1; i++) { + whitelist.push_back("Q"); + } + S32 status = entry.setWhiteList(whitelist); + ensure(get_test_name() + " invalid result", status == LSL_STATUS_BOUNDS_ERROR); + ensure(get_test_name() + " failed", empty == entry.getWhiteList()); + } + + template<> template<> + void object::test<9>() + { + set_test_name("Test to make sure both setWhiteList() functions behave the same"); + + // Test a valid list + std::vector<std::string> whitelist, empty; + LLSD whitelist_llsd; + whitelist.push_back(std::string(URL_OK)); + whitelist_llsd.append(std::string(URL_OK)); + LLMediaEntry entry1, entry2; + ensure(get_test_name() + " setWhiteList(s) don't match", + entry1.setWhiteList(whitelist) == LSL_STATUS_OK && + entry2.setWhiteList(whitelist_llsd)== LSL_STATUS_OK ); + ensure(get_test_name() + " failed", + entry1.getWhiteList() == entry2.getWhiteList()); + } + + template<> template<> + void object::test<10>() + { + set_test_name("Test to make sure both setWhiteList() functions behave the same"); + + // Test an invalid list + std::vector<std::string> whitelist, empty; + LLSD whitelist_llsd; + whitelist.push_back(std::string(URL_OK)); + whitelist.push_back(std::string(URL_TOO_BIG)); + whitelist_llsd.append(std::string(URL_OK)); + whitelist_llsd.append(std::string(URL_TOO_BIG)); + LLMediaEntry entry1, entry2; + ensure(get_test_name() + " setWhiteList(s) don't match", + entry1.setWhiteList(whitelist) == LSL_STATUS_BOUNDS_ERROR && + entry2.setWhiteList(whitelist_llsd) == LSL_STATUS_BOUNDS_ERROR); + ensure(get_test_name() + " failed", + empty == entry1.getWhiteList() && + empty == entry2.getWhiteList()); + } + + template<> template<> + void object::test<11>() + { + set_test_name("Test to make sure both setWhiteList() functions behave the same"); + + // Test an invalid list, too many + std::vector<std::string> whitelist, empty; + LLSD whitelist_llsd; + for (int i=0; i < LLMediaEntry::MAX_WHITELIST_SIZE+1; i++) { + whitelist.push_back("Q"); + whitelist_llsd.append("Q"); + } + LLMediaEntry entry1, entry2; + ensure(get_test_name() + " invalid result", + entry1.setWhiteList(whitelist) == LSL_STATUS_BOUNDS_ERROR && + entry2.setWhiteList(whitelist_llsd) == LSL_STATUS_BOUNDS_ERROR); + ensure(get_test_name() + " failed", + empty == entry1.getWhiteList() && + empty == entry2.getWhiteList()); + } + + // Whitelist check tests + + // Check the "empty whitelist" case + template<> template<> + void object::test<12>() { whitelist_test("", "http://www.example.com", true); } + + // Check the "missing scheme" case + template<> template<> + void object::test<13>() { whitelist_test("www.example.com", "http://www.example.com", true); } + + // Check the "exactly the same" case + template<> template<> + void object::test<14>() { whitelist_test("http://example.com", "http://example.com", true); } + + // Check the enable flag + template<> template<> + void object::test<15>() { whitelist_test(false, "www.example.com", "http://www.secondlife.com", true); } + template<> template<> + void object::test<16>() { whitelist_test(true, "www.example.com", "http://www.secondlife.com", false); } + + // Check permutations of trailing slash: + template<> template<> + void object::test<17>() { whitelist_test("http://www.example.com", "http://www.example.com/", true); } + template<> template<> + void object::test<18>() { whitelist_test("http://www.example.com/", "http://www.example.com/", true); } + template<> template<> + void object::test<19>() { whitelist_test("http://www.example.com/", "http://www.example.com", false); } + template<> template<> + void object::test<20>() { whitelist_test("http://www.example.com", "http://www.example.com/foobar", true); } + template<> template<> + void object::test<21>() { whitelist_test("http://www.example.com/", "http://www.example.com/foobar", false); } + + + // More cases... + template<> template<> + void object::test<22>() { whitelist_test("http://example.com", "http://example.com/wiki", true); } + template<> template<> + void object::test<23>() { whitelist_test("www.example.com", "http://www.example.com/help", true); } + template<> template<> + void object::test<24>() { whitelist_test("http://www.example.com", "http://wwwexample.com", false); } + template<> template<> + void object::test<25>() { whitelist_test("http://www.example.com", "http://www.example.com/wiki", true); } + template<> template<> + void object::test<26>() { whitelist_test("example.com", "http://wwwexample.com", false); } + template<> template<> + void object::test<27>() { whitelist_test("http://www.example.com/", "http://www.amazon.com/wiki", false); } + template<> template<> + void object::test<28>() { whitelist_test("www.example.com", "http://www.amazon.com", false); } + + // regexp cases + template<> template<> + void object::test<29>() { whitelist_test("*.example.com", "http://www.example.com", true); } + template<> template<> + void object::test<30>() { whitelist_test("*.example.com", "http://www.amazon.com", false); } + template<> template<> + void object::test<31>() { whitelist_test("*.example.com", "http://www.example.com/foo/bar", true); } + template<> template<> + void object::test<32>() { whitelist_test("*.example.com", "http:/example.com/foo/bar", false); } + template<> template<> + void object::test<33>() { whitelist_test("*example.com", "http://example.com/foo/bar", true); } + template<> template<> + void object::test<34>() { whitelist_test("*example.com", "http://my.virus.com/foo/bar?example.com", false); } + template<> template<> + void object::test<35>() { whitelist_test("example.com", "http://my.virus.com/foo/bar?example.com", false); } + template<> template<> + void object::test<36>() { whitelist_test("*example.com", "http://my.virus.com/foo/bar?*example.com", false); } + template<> template<> + void object::test<37>() { whitelist_test("http://*example.com", "http://www.example.com", true); } + template<> template<> + void object::test<38>() { whitelist_test("http://*.example.com", "http://www.example.com", true); } + template<> template<> + void object::test<39>() { whitelist_test("http://*.e$?^.com", "http://www.e$?^.com", true); } + template<> template<> + void object::test<40>() { whitelist_test("*.example.com/foo/bar", "http://www.example.com/", false); } + template<> template<> + void object::test<41>() { whitelist_test("*.example.com/foo/bar", "http://example.com/foo/bar", false); } + template<> template<> + void object::test<42>() { whitelist_test("http://*.example.com/foo/bar", "http://www.example.com", false); } + template<> template<> + void object::test<43>() { whitelist_test("http://*.example.com", "https://www.example.com", false); } + template<> template<> + void object::test<44>() { whitelist_test("http*://*.example.com", "rtsp://www.example.com", false); } + template<> template<> + void object::test<45>() { whitelist_test("http*://*.example.com", "https://www.example.com", true); } + template<> template<> + void object::test<46>() { whitelist_test("example.com", "http://www.example.com", false); } + template<> template<> + void object::test<47>() { whitelist_test("www.example.com", "http://www.example.com:80", false); } + template<> template<> + void object::test<48>() { whitelist_test("www.example.com", "http://www.example.com", true); } + template<> template<> + void object::test<49>() { whitelist_test("www.example.com/", "http://www.example.com", false); } + template<> template<> + void object::test<50>() { whitelist_test("www.example.com/foo/bar/*", "http://www.example.com/foo/bar/baz", true); } + // Path only + template<> template<> + void object::test<51>() { whitelist_test("/foo/*/baz", "http://www.example.com/foo/bar/baz", true); } + template<> template<> + void object::test<52>() { whitelist_test("/foo/*/baz", "http://www.example.com/foo/bar/", false); } +} |