/indra/llprimitive/llmediaentry.cpp
C++ | 596 lines | 467 code | 47 blank | 82 comment | 81 complexity | bd7cbfdb06edb77da17383ac0513fa20 MD5 | raw file
Possible License(s): LGPL-2.1
1/** 2 * @file llmediaentry.cpp 3 * @brief This is a single instance of media data related to the face of a prim 4 * 5 * $LicenseInfo:firstyear=2001&license=viewerlgpl$ 6 * Second Life Viewer Source Code 7 * Copyright (C) 2010, Linden Research, Inc. 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Lesser General Public 11 * License as published by the Free Software Foundation; 12 * version 2.1 of the License only. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with this library; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 22 * 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA 24 * $/LicenseInfo$ 25 */ 26 27#include "linden_common.h" 28#include "llmediaentry.h" 29#include "lllslconstants.h" 30 31#include <boost/regex.hpp> 32 33// LLSD key defines 34// DO NOT REORDER OR REMOVE THESE! 35 36// Some LLSD keys. Do not change! 37#define MEDIA_ALT_IMAGE_ENABLE_KEY_STR "alt_image_enable" 38#define MEDIA_CONTROLS_KEY_STR "controls" 39#define MEDIA_CURRENT_URL_KEY_STR "current_url" 40#define MEDIA_HOME_URL_KEY_STR "home_url" 41#define MEDIA_AUTO_LOOP_KEY_STR "auto_loop" 42#define MEDIA_AUTO_PLAY_KEY_STR "auto_play" 43#define MEDIA_AUTO_SCALE_KEY_STR "auto_scale" 44#define MEDIA_AUTO_ZOOM_KEY_STR "auto_zoom" 45#define MEDIA_FIRST_CLICK_INTERACT_KEY_STR "first_click_interact" 46#define MEDIA_WIDTH_PIXELS_KEY_STR "width_pixels" 47#define MEDIA_HEIGHT_PIXELS_KEY_STR "height_pixels" 48 49// "security" fields 50#define MEDIA_WHITELIST_ENABLE_KEY_STR "whitelist_enable" 51#define MEDIA_WHITELIST_KEY_STR "whitelist" 52 53// "permissions" fields 54#define MEDIA_PERMS_INTERACT_KEY_STR "perms_interact" 55#define MEDIA_PERMS_CONTROL_KEY_STR "perms_control" 56 57// "general" fields 58const char* LLMediaEntry::ALT_IMAGE_ENABLE_KEY = MEDIA_ALT_IMAGE_ENABLE_KEY_STR; 59const char* LLMediaEntry::CONTROLS_KEY = MEDIA_CONTROLS_KEY_STR; 60const char* LLMediaEntry::CURRENT_URL_KEY = MEDIA_CURRENT_URL_KEY_STR; 61const char* LLMediaEntry::HOME_URL_KEY = MEDIA_HOME_URL_KEY_STR; 62const char* LLMediaEntry::AUTO_LOOP_KEY = MEDIA_AUTO_LOOP_KEY_STR; 63const char* LLMediaEntry::AUTO_PLAY_KEY = MEDIA_AUTO_PLAY_KEY_STR; 64const char* LLMediaEntry::AUTO_SCALE_KEY = MEDIA_AUTO_SCALE_KEY_STR; 65const char* LLMediaEntry::AUTO_ZOOM_KEY = MEDIA_AUTO_ZOOM_KEY_STR; 66const char* LLMediaEntry::FIRST_CLICK_INTERACT_KEY = MEDIA_FIRST_CLICK_INTERACT_KEY_STR; 67const char* LLMediaEntry::WIDTH_PIXELS_KEY = MEDIA_WIDTH_PIXELS_KEY_STR; 68const char* LLMediaEntry::HEIGHT_PIXELS_KEY = MEDIA_HEIGHT_PIXELS_KEY_STR; 69 70// "security" fields 71const char* LLMediaEntry::WHITELIST_ENABLE_KEY = MEDIA_WHITELIST_ENABLE_KEY_STR; 72const char* LLMediaEntry::WHITELIST_KEY = MEDIA_WHITELIST_KEY_STR; 73 74// "permissions" fields 75const char* LLMediaEntry::PERMS_INTERACT_KEY = MEDIA_PERMS_INTERACT_KEY_STR; 76const char* LLMediaEntry::PERMS_CONTROL_KEY = MEDIA_PERMS_CONTROL_KEY_STR; 77 78#define DEFAULT_URL_PREFIX "http://" 79 80// Constructor(s) 81LLMediaEntry::LLMediaEntry() : 82 mAltImageEnable(false), 83 mControls(STANDARD), 84 mCurrentURL(""), 85 mHomeURL(""), 86 mAutoLoop(false), 87 mAutoPlay(false), 88 mAutoScale(false), 89 mAutoZoom(false), 90 mFirstClickInteract(false), 91 mWidthPixels(0), 92 mHeightPixels(0), 93 mWhiteListEnable(false), 94 // mWhiteList 95 mPermsInteract(PERM_ALL), 96 mPermsControl(PERM_ALL), 97 mMediaIDp(NULL) 98{ 99} 100 101LLMediaEntry::LLMediaEntry(const LLMediaEntry &rhs) : 102 mMediaIDp(NULL) 103{ 104 // "general" fields 105 mAltImageEnable = rhs.mAltImageEnable; 106 mControls = rhs.mControls; 107 mCurrentURL = rhs.mCurrentURL; 108 mHomeURL = rhs.mHomeURL; 109 mAutoLoop = rhs.mAutoLoop; 110 mAutoPlay = rhs.mAutoPlay; 111 mAutoScale = rhs.mAutoScale; 112 mAutoZoom = rhs.mAutoZoom; 113 mFirstClickInteract = rhs.mFirstClickInteract; 114 mWidthPixels = rhs.mWidthPixels; 115 mHeightPixels = rhs.mHeightPixels; 116 117 // "security" fields 118 mWhiteListEnable = rhs.mWhiteListEnable; 119 mWhiteList = rhs.mWhiteList; 120 121 // "permissions" fields 122 mPermsInteract = rhs.mPermsInteract; 123 mPermsControl = rhs.mPermsControl; 124} 125 126LLMediaEntry::~LLMediaEntry() 127{ 128 if (NULL != mMediaIDp) 129 { 130 delete mMediaIDp; 131 } 132} 133 134LLSD LLMediaEntry::asLLSD() const 135{ 136 LLSD sd; 137 asLLSD(sd); 138 return sd; 139} 140 141// 142// LLSD functions 143// 144void LLMediaEntry::asLLSD(LLSD& sd) const 145{ 146 // "general" fields 147 sd[ALT_IMAGE_ENABLE_KEY] = mAltImageEnable; 148 sd[CONTROLS_KEY] = (LLSD::Integer)mControls; 149 sd[CURRENT_URL_KEY] = mCurrentURL; 150 sd[HOME_URL_KEY] = mHomeURL; 151 sd[AUTO_LOOP_KEY] = mAutoLoop; 152 sd[AUTO_PLAY_KEY] = mAutoPlay; 153 sd[AUTO_SCALE_KEY] = mAutoScale; 154 sd[AUTO_ZOOM_KEY] = mAutoZoom; 155 sd[FIRST_CLICK_INTERACT_KEY] = mFirstClickInteract; 156 sd[WIDTH_PIXELS_KEY] = mWidthPixels; 157 sd[HEIGHT_PIXELS_KEY] = mHeightPixels; 158 159 // "security" fields 160 sd[WHITELIST_ENABLE_KEY] = mWhiteListEnable; 161 sd.erase(WHITELIST_KEY); 162 for (U32 i=0; i<mWhiteList.size(); i++) 163 { 164 sd[WHITELIST_KEY].append(mWhiteList[i]); 165 } 166 167 // "permissions" fields 168 sd[PERMS_INTERACT_KEY] = mPermsInteract; 169 sd[PERMS_CONTROL_KEY] = mPermsControl; 170} 171 172// static 173bool LLMediaEntry::checkLLSD(const LLSD& sd) 174{ 175 if (sd.isUndefined()) return true; 176 LLMediaEntry temp; 177 return temp.fromLLSDInternal(sd, true); 178} 179 180void LLMediaEntry::fromLLSD(const LLSD& sd) 181{ 182 (void)fromLLSDInternal(sd, true); 183} 184 185void LLMediaEntry::mergeFromLLSD(const LLSD& sd) 186{ 187 (void)fromLLSDInternal(sd, false); 188} 189 190// *NOTE: returns true if NO failures to set occurred, false otherwise. 191// However, be aware that if a failure to set does occur, it does 192// not stop setting fields from the LLSD! 193bool LLMediaEntry::fromLLSDInternal(const LLSD& sd, bool overwrite) 194{ 195 // *HACK: we sort of cheat here and assume that status is a 196 // bit field. We "or" into status and instead of returning 197 // it, we return whether it finishes off as LSL_STATUS_OK or not. 198 U32 status = LSL_STATUS_OK; 199 200 // "general" fields 201 if ( overwrite || sd.has(ALT_IMAGE_ENABLE_KEY) ) 202 { 203 status |= setAltImageEnable( sd[ALT_IMAGE_ENABLE_KEY] ); 204 } 205 if ( overwrite || sd.has(CONTROLS_KEY) ) 206 { 207 status |= setControls( (MediaControls)(LLSD::Integer)sd[CONTROLS_KEY] ); 208 } 209 if ( overwrite || sd.has(CURRENT_URL_KEY) ) 210 { 211 // Don't check whitelist 212 status |= setCurrentURLInternal( sd[CURRENT_URL_KEY], false ); 213 } 214 if ( overwrite || sd.has(HOME_URL_KEY) ) 215 { 216 status |= setHomeURL( sd[HOME_URL_KEY] ); 217 } 218 if ( overwrite || sd.has(AUTO_LOOP_KEY) ) 219 { 220 status |= setAutoLoop( sd[AUTO_LOOP_KEY] ); 221 } 222 if ( overwrite || sd.has(AUTO_PLAY_KEY) ) 223 { 224 status |= setAutoPlay( sd[AUTO_PLAY_KEY] ); 225 } 226 if ( overwrite || sd.has(AUTO_SCALE_KEY) ) 227 { 228 status |= setAutoScale( sd[AUTO_SCALE_KEY] ); 229 } 230 if ( overwrite || sd.has(AUTO_ZOOM_KEY) ) 231 { 232 status |= setAutoZoom( sd[AUTO_ZOOM_KEY] ); 233 } 234 if ( overwrite || sd.has(FIRST_CLICK_INTERACT_KEY) ) 235 { 236 status |= setFirstClickInteract( sd[FIRST_CLICK_INTERACT_KEY] ); 237 } 238 if ( overwrite || sd.has(WIDTH_PIXELS_KEY) ) 239 { 240 status |= setWidthPixels( (LLSD::Integer)sd[WIDTH_PIXELS_KEY] ); 241 } 242 if ( overwrite || sd.has(HEIGHT_PIXELS_KEY) ) 243 { 244 status |= setHeightPixels( (LLSD::Integer)sd[HEIGHT_PIXELS_KEY] ); 245 } 246 247 // "security" fields 248 if ( overwrite || sd.has(WHITELIST_ENABLE_KEY) ) 249 { 250 status |= setWhiteListEnable( sd[WHITELIST_ENABLE_KEY] ); 251 } 252 if ( overwrite || sd.has(WHITELIST_KEY) ) 253 { 254 status |= setWhiteList( sd[WHITELIST_KEY] ); 255 } 256 257 // "permissions" fields 258 if ( overwrite || sd.has(PERMS_INTERACT_KEY) ) 259 { 260 status |= setPermsInteract( 0xff & (LLSD::Integer)sd[PERMS_INTERACT_KEY] ); 261 } 262 if ( overwrite || sd.has(PERMS_CONTROL_KEY) ) 263 { 264 status |= setPermsControl( 0xff & (LLSD::Integer)sd[PERMS_CONTROL_KEY] ); 265 } 266 267 return LSL_STATUS_OK == status; 268} 269 270LLMediaEntry& LLMediaEntry::operator=(const LLMediaEntry &rhs) 271{ 272 if (this != &rhs) 273 { 274 // "general" fields 275 mAltImageEnable = rhs.mAltImageEnable; 276 mControls = rhs.mControls; 277 mCurrentURL = rhs.mCurrentURL; 278 mHomeURL = rhs.mHomeURL; 279 mAutoLoop = rhs.mAutoLoop; 280 mAutoPlay = rhs.mAutoPlay; 281 mAutoScale = rhs.mAutoScale; 282 mAutoZoom = rhs.mAutoZoom; 283 mFirstClickInteract = rhs.mFirstClickInteract; 284 mWidthPixels = rhs.mWidthPixels; 285 mHeightPixels = rhs.mHeightPixels; 286 287 // "security" fields 288 mWhiteListEnable = rhs.mWhiteListEnable; 289 mWhiteList = rhs.mWhiteList; 290 291 // "permissions" fields 292 mPermsInteract = rhs.mPermsInteract; 293 mPermsControl = rhs.mPermsControl; 294 } 295 296 return *this; 297} 298 299bool LLMediaEntry::operator==(const LLMediaEntry &rhs) const 300{ 301 return ( 302 // "general" fields 303 mAltImageEnable == rhs.mAltImageEnable && 304 mControls == rhs.mControls && 305 mCurrentURL == rhs.mCurrentURL && 306 mHomeURL == rhs.mHomeURL && 307 mAutoLoop == rhs.mAutoLoop && 308 mAutoPlay == rhs.mAutoPlay && 309 mAutoScale == rhs.mAutoScale && 310 mAutoZoom == rhs.mAutoZoom && 311 mFirstClickInteract == rhs.mFirstClickInteract && 312 mWidthPixels == rhs.mWidthPixels && 313 mHeightPixels == rhs.mHeightPixels && 314 315 // "security" fields 316 mWhiteListEnable == rhs.mWhiteListEnable && 317 mWhiteList == rhs.mWhiteList && 318 319 // "permissions" fields 320 mPermsInteract == rhs.mPermsInteract && 321 mPermsControl == rhs.mPermsControl 322 323 ); 324} 325 326bool LLMediaEntry::operator!=(const LLMediaEntry &rhs) const 327{ 328 return ( 329 // "general" fields 330 mAltImageEnable != rhs.mAltImageEnable || 331 mControls != rhs.mControls || 332 mCurrentURL != rhs.mCurrentURL || 333 mHomeURL != rhs.mHomeURL || 334 mAutoLoop != rhs.mAutoLoop || 335 mAutoPlay != rhs.mAutoPlay || 336 mAutoScale != rhs.mAutoScale || 337 mAutoZoom != rhs.mAutoZoom || 338 mFirstClickInteract != rhs.mFirstClickInteract || 339 mWidthPixels != rhs.mWidthPixels || 340 mHeightPixels != rhs.mHeightPixels || 341 342 // "security" fields 343 mWhiteListEnable != rhs.mWhiteListEnable || 344 mWhiteList != rhs.mWhiteList || 345 346 // "permissions" fields 347 mPermsInteract != rhs.mPermsInteract || 348 mPermsControl != rhs.mPermsControl 349 350 ); 351} 352 353U32 LLMediaEntry::setWhiteList( const std::vector<std::string> &whitelist ) 354{ 355 // *NOTE: This code is VERY similar to the setWhitelist below. 356 // IF YOU CHANGE THIS IMPLEMENTATION, BE SURE TO CHANGE THE OTHER! 357 U32 size = 0; 358 U32 count = 0; 359 // First count to make sure the size constraint is not violated 360 std::vector<std::string>::const_iterator iter = whitelist.begin(); 361 std::vector<std::string>::const_iterator end = whitelist.end(); 362 for ( ; iter < end; ++iter) 363 { 364 const std::string &entry = (*iter); 365 size += entry.length() + 1; // Include one for \0 366 count ++; 367 if (size > MAX_WHITELIST_SIZE || count > MAX_WHITELIST_COUNT) 368 { 369 return LSL_STATUS_BOUNDS_ERROR; 370 } 371 } 372 // Next clear the vector 373 mWhiteList.clear(); 374 // Then re-iterate and copy entries 375 iter = whitelist.begin(); 376 for ( ; iter < end; ++iter) 377 { 378 const std::string &entry = (*iter); 379 mWhiteList.push_back(entry); 380 } 381 return LSL_STATUS_OK; 382} 383 384U32 LLMediaEntry::setWhiteList( const LLSD &whitelist ) 385{ 386 // If whitelist is undef, the whitelist is cleared 387 if (whitelist.isUndefined()) 388 { 389 mWhiteList.clear(); 390 return LSL_STATUS_OK; 391 } 392 393 // However, if the whitelist is an empty array, erase it. 394 if (whitelist.isArray()) 395 { 396 // *NOTE: This code is VERY similar to the setWhitelist above. 397 // IF YOU CHANGE THIS IMPLEMENTATION, BE SURE TO CHANGE THE OTHER! 398 U32 size = 0; 399 U32 count = 0; 400 // First check to make sure the size and count constraints are not violated 401 LLSD::array_const_iterator iter = whitelist.beginArray(); 402 LLSD::array_const_iterator end = whitelist.endArray(); 403 for ( ; iter < end; ++iter) 404 { 405 const std::string &entry = (*iter).asString(); 406 size += entry.length() + 1; // Include one for \0 407 count ++; 408 if (size > MAX_WHITELIST_SIZE || count > MAX_WHITELIST_COUNT) 409 { 410 return LSL_STATUS_BOUNDS_ERROR; 411 } 412 } 413 // Next clear the vector 414 mWhiteList.clear(); 415 // Then re-iterate and copy entries 416 iter = whitelist.beginArray(); 417 for ( ; iter < end; ++iter) 418 { 419 const std::string &entry = (*iter).asString(); 420 mWhiteList.push_back(entry); 421 } 422 return LSL_STATUS_OK; 423 } 424 else 425 { 426 return LSL_STATUS_MALFORMED_PARAMS; 427 } 428} 429 430 431static void prefix_with(std::string &str, const char *chars, const char *prefix) 432{ 433 // Given string 'str', prefix all instances of any character in 'chars' 434 // with 'prefix' 435 size_t found = str.find_first_of(chars); 436 size_t prefix_len = strlen(prefix); 437 while (found != std::string::npos) 438 { 439 str.insert(found, prefix, prefix_len); 440 found = str.find_first_of(chars, found+prefix_len+1); 441 } 442} 443 444static bool pattern_match(const std::string &candidate_str, const std::string &pattern) 445{ 446 // If the pattern is empty, it matches 447 if (pattern.empty()) return true; 448 449 // 'pattern' is a glob pattern, we only accept '*' chars 450 // copy it 451 std::string expression = pattern; 452 453 // Escape perl's regexp chars with a backslash, except all "*" chars 454 prefix_with(expression, ".[{()\\+?|^$", "\\"); 455 prefix_with(expression, "*", "."); 456 457 // case-insensitive matching: 458 boost::regex regexp(expression, boost::regex::perl|boost::regex::icase); 459 return boost::regex_match(candidate_str, regexp); 460} 461 462bool LLMediaEntry::checkCandidateUrl(const std::string& url) const 463{ 464 if (getWhiteListEnable()) 465 { 466 return checkUrlAgainstWhitelist(url, getWhiteList()); 467 } 468 else 469 { 470 return true; 471 } 472} 473 474// static 475bool LLMediaEntry::checkUrlAgainstWhitelist(const std::string& url, 476 const std::vector<std::string> &whitelist) 477{ 478 bool passes = true; 479 // *NOTE: no entries? Don't check 480 if (whitelist.size() > 0) 481 { 482 passes = false; 483 484 // Case insensitive: the reason why we toUpper both this and the 485 // filter 486 std::string candidate_url = url; 487 // Use lluri to see if there is a path part in the candidate URL. No path? Assume "/" 488 LLURI candidate_uri(candidate_url); 489 std::vector<std::string>::const_iterator iter = whitelist.begin(); 490 std::vector<std::string>::const_iterator end = whitelist.end(); 491 for ( ; iter < end; ++iter ) 492 { 493 std::string filter = *iter; 494 495 LLURI filter_uri(filter); 496 bool scheme_passes = pattern_match( candidate_uri.scheme(), filter_uri.scheme() ); 497 if (filter_uri.scheme().empty()) 498 { 499 filter_uri = LLURI(DEFAULT_URL_PREFIX + filter); 500 } 501 bool authority_passes = pattern_match( candidate_uri.authority(), filter_uri.authority() ); 502 bool path_passes = pattern_match( candidate_uri.escapedPath(), filter_uri.escapedPath() ); 503 504 if (scheme_passes && authority_passes && path_passes) 505 { 506 passes = true; 507 break; 508 } 509 } 510 } 511 return passes; 512} 513 514U32 LLMediaEntry::setStringFieldWithLimit( std::string &field, const std::string &value, U32 limit ) 515{ 516 if ( value.length() > limit ) 517 { 518 return LSL_STATUS_BOUNDS_ERROR; 519 } 520 else 521 { 522 field = value; 523 return LSL_STATUS_OK; 524 } 525} 526 527U32 LLMediaEntry::setControls(LLMediaEntry::MediaControls controls) 528{ 529 if (controls == STANDARD || 530 controls == MINI) 531 { 532 mControls = controls; 533 return LSL_STATUS_OK; 534 } 535 return LSL_STATUS_BOUNDS_ERROR; 536} 537 538U32 LLMediaEntry::setPermsInteract( U8 val ) 539{ 540 mPermsInteract = val & PERM_MASK; 541 return LSL_STATUS_OK; 542} 543 544U32 LLMediaEntry::setPermsControl( U8 val ) 545{ 546 mPermsControl = val & PERM_MASK; 547 return LSL_STATUS_OK; 548} 549 550U32 LLMediaEntry::setCurrentURL(const std::string& current_url) 551{ 552 return setCurrentURLInternal( current_url, true ); 553} 554 555U32 LLMediaEntry::setCurrentURLInternal(const std::string& current_url, bool check_whitelist) 556{ 557 if ( ! check_whitelist || checkCandidateUrl(current_url)) 558 { 559 return setStringFieldWithLimit( mCurrentURL, current_url, MAX_URL_LENGTH ); 560 } 561 else 562 { 563 return LSL_STATUS_WHITELIST_FAILED; 564 } 565} 566 567U32 LLMediaEntry::setHomeURL(const std::string& home_url) 568{ 569 return setStringFieldWithLimit( mHomeURL, home_url, MAX_URL_LENGTH ); 570} 571 572U32 LLMediaEntry::setWidthPixels(U16 width) 573{ 574 if (width > MAX_WIDTH_PIXELS) return LSL_STATUS_BOUNDS_ERROR; 575 mWidthPixels = width; 576 return LSL_STATUS_OK; 577} 578 579U32 LLMediaEntry::setHeightPixels(U16 height) 580{ 581 if (height > MAX_HEIGHT_PIXELS) return LSL_STATUS_BOUNDS_ERROR; 582 mHeightPixels = height; 583 return LSL_STATUS_OK; 584} 585 586const LLUUID &LLMediaEntry::getMediaID() const 587{ 588 // Lazily generate media ID 589 if (NULL == mMediaIDp) 590 { 591 mMediaIDp = new LLUUID(); 592 mMediaIDp->generate(); 593 } 594 return *mMediaIDp; 595} 596