diff options
Diffstat (limited to 'indra/llprimitive/llmediaentry.cpp')
-rw-r--r-- | indra/llprimitive/llmediaentry.cpp | 597 |
1 files changed, 597 insertions, 0 deletions
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; +} + |