summaryrefslogtreecommitdiff
path: root/indra/llprimitive
diff options
context:
space:
mode:
authorMark Palange (Mani) <palange@lindenlab.com>2009-10-02 10:35:42 -0700
committerMark Palange (Mani) <palange@lindenlab.com>2009-10-02 10:35:42 -0700
commit4d53e235c2445b820bb0ae7303b269648dd86037 (patch)
treec1ab3643cf257099822e7a0e1e832d76813555f6 /indra/llprimitive
parent80f27013b2d2a7ca6509db033c8f0c31562e04c7 (diff)
parentdde2153014cd7d7b8fa704f7067a41344bfbb1c2 (diff)
merge of latest viewer/viewer-20
Diffstat (limited to 'indra/llprimitive')
-rw-r--r--indra/llprimitive/CMakeLists.txt9
-rw-r--r--indra/llprimitive/llmediaentry.cpp597
-rw-r--r--indra/llprimitive/llmediaentry.h228
-rw-r--r--indra/llprimitive/llprimitive.cpp1
-rw-r--r--indra/llprimitive/lltextureentry.cpp196
-rw-r--r--indra/llprimitive/lltextureentry.h50
-rw-r--r--indra/llprimitive/tests/llmediaentry_test.cpp484
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 2a8b557f43..5ad758072c 100644
--- a/indra/llprimitive/llprimitive.cpp
+++ b/indra/llprimitive/llprimitive.cpp
@@ -1319,6 +1319,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 2736d54cc8..2c587554a2 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_math.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); }
+}