From cd17687f01420952712a500107e0f93e7ab8d5f8 Mon Sep 17 00:00:00 2001 From: Jacek Antonelli Date: Fri, 15 Aug 2008 23:45:34 -0500 Subject: Second Life viewer sources 1.19.1.0 --- linden/indra/llcommon/indra_constants.h | 4 + linden/indra/llcommon/linden_common.h | 7 + linden/indra/llcommon/llapp.cpp | 11 +- linden/indra/llcommon/llapr.cpp | 8 +- linden/indra/llcommon/llclickaction.h | 2 + linden/indra/llcommon/llcommon_vc8.vcproj | 1618 ++++++++++++++------------- linden/indra/llcommon/llcommon_vc9.vcproj | 1616 +++++++++++++------------- linden/indra/llcommon/llfasttimer.h | 7 +- linden/indra/llcommon/llfile.cpp | 28 + linden/indra/llcommon/llfile.h | 9 + linden/indra/llcommon/llkeythrottle.h | 5 +- linden/indra/llcommon/lllog.cpp | 3 +- linden/indra/llcommon/llmemory.h | 50 +- linden/indra/llcommon/llpreprocessor.h | 27 - linden/indra/llcommon/llsdserialize.cpp | 763 +++++++++---- linden/indra/llcommon/llsdserialize.h | 232 +++- linden/indra/llcommon/llsdserialize_xml.cpp | 23 +- linden/indra/llcommon/llstreamtools.cpp | 27 +- linden/indra/llcommon/llstreamtools.h | 7 +- linden/indra/llcommon/llsys.cpp | 3 +- linden/indra/llcommon/llthread.cpp | 7 + linden/indra/llcommon/llthread.h | 10 +- linden/indra/llcommon/llversionserver.h | 6 +- linden/indra/llcommon/llversionviewer.h | 4 +- 24 files changed, 2509 insertions(+), 1968 deletions(-) (limited to 'linden/indra/llcommon') diff --git a/linden/indra/llcommon/indra_constants.h b/linden/indra/llcommon/indra_constants.h index 16e471d..eceb576 100644 --- a/linden/indra/llcommon/indra_constants.h +++ b/linden/indra/llcommon/indra_constants.h @@ -323,6 +323,10 @@ const U32 PARCEL_MEDIA_COMMAND_TIME = 6; const U32 PARCEL_MEDIA_COMMAND_AGENT = 7; const U32 PARCEL_MEDIA_COMMAND_UNLOAD = 8; const U32 PARCEL_MEDIA_COMMAND_AUTO_ALIGN = 9; +const U32 PARCEL_MEDIA_COMMAND_TYPE = 10; +const U32 PARCEL_MEDIA_COMMAND_SIZE = 11; +const U32 PARCEL_MEDIA_COMMAND_DESC = 12; +const U32 PARCEL_MEDIA_COMMAND_LOOP_SET = 13; // map item types const U32 MAP_ITEM_TELEHUB = 0x01; diff --git a/linden/indra/llcommon/linden_common.h b/linden/indra/llcommon/linden_common.h index 01540c1..547cceb 100644 --- a/linden/indra/llcommon/linden_common.h +++ b/linden/indra/llcommon/linden_common.h @@ -58,6 +58,13 @@ #ifdef LL_WINDOWS #pragma warning (3 : 4702) // we like level 3, not 4 +// level 4 warnings that we need to disable: +#pragma warning (disable : 4100) // unreferenced formal parameter +#pragma warning (disable : 4127) // conditional expression is constant (e.g. while(1) ) +#pragma warning (disable : 4244) // possible loss of data on conversions +#pragma warning (disable : 4396) // the inline specifier cannot be used when a friend declaration refers to a specialization of a function template +#pragma warning (disable : 4512) // assignment operator could not be generated +#pragma warning (disable : 4706) // assignment within conditional (even if((x = y)) ) #endif // LL_WINDOWS // Linden only libs in alpha-order other than stdtypes.h diff --git a/linden/indra/llcommon/llapp.cpp b/linden/indra/llcommon/llapp.cpp index 5ae0727..a10436a 100644 --- a/linden/indra/llcommon/llapp.cpp +++ b/linden/indra/llcommon/llapp.cpp @@ -166,7 +166,16 @@ bool LLApp::parseCommandOptions(int argc, char** argv) // we found another option after this one or we have // reached the end. simply record that this option was // found and continue. - commands[name] = true; + int flag = name.compare("logfile"); + if (0 == flag) + { + commands[name] = "log"; + } + else + { + commands[name] = true; + } + continue; } ++ii; diff --git a/linden/indra/llcommon/llapr.cpp b/linden/indra/llcommon/llapr.cpp index 2a81e5e..770cd3f 100644 --- a/linden/indra/llcommon/llapr.cpp +++ b/linden/indra/llcommon/llapr.cpp @@ -292,7 +292,7 @@ bool ll_apr_file_remove(const LLString& filename, apr_pool_t* pool) s = apr_file_remove(filename.c_str(), pool); if (s != APR_SUCCESS) { - llwarns << "ll_apr_file_remove failed on file: " << filename << llendl; + lldebugs << "ll_apr_file_remove failed on file: " << filename << llendl; ll_apr_warn_status(s); return false; } @@ -306,7 +306,7 @@ bool ll_apr_file_rename(const LLString& filename, const LLString& newname, apr_p s = apr_file_rename(filename.c_str(), newname.c_str(), pool); if (s != APR_SUCCESS) { - llwarns << "ll_apr_file_rename failed on file: " << filename << llendl; + lldebugs << "ll_apr_file_rename failed on file: " << filename << llendl; ll_apr_warn_status(s); return false; } @@ -363,7 +363,7 @@ bool ll_apr_dir_make(const LLString& dirname, apr_pool_t* pool) s = apr_dir_make(dirname.c_str(), APR_FPROT_OS_DEFAULT, pool); if (s != APR_SUCCESS) { - llwarns << "ll_apr_file_remove failed on file: " << dirname << llendl; + lldebugs << "ll_apr_dir_make failed on file: " << dirname << llendl; ll_apr_warn_status(s); return false; } @@ -377,7 +377,7 @@ bool ll_apr_dir_remove(const LLString& dirname, apr_pool_t* pool) s = apr_file_remove(dirname.c_str(), pool); if (s != APR_SUCCESS) { - llwarns << "ll_apr_file_remove failed on file: " << dirname << llendl; + lldebugs << "ll_apr_dir_remove failed on file: " << dirname << llendl; ll_apr_warn_status(s); return false; } diff --git a/linden/indra/llcommon/llclickaction.h b/linden/indra/llcommon/llclickaction.h index 20bfbfd..b69ca15 100644 --- a/linden/indra/llcommon/llclickaction.h +++ b/linden/indra/llcommon/llclickaction.h @@ -39,5 +39,7 @@ const U8 CLICK_ACTION_SIT = 1; const U8 CLICK_ACTION_BUY = 2; const U8 CLICK_ACTION_PAY = 3; const U8 CLICK_ACTION_OPEN = 4; +const U8 CLICK_ACTION_PLAY = 5; +const U8 CLICK_ACTION_OPEN_MEDIA = 6; #endif diff --git a/linden/indra/llcommon/llcommon_vc8.vcproj b/linden/indra/llcommon/llcommon_vc8.vcproj index fc3060a..5578075 100644 --- a/linden/indra/llcommon/llcommon_vc8.vcproj +++ b/linden/indra/llcommon/llcommon_vc8.vcproj @@ -1,807 +1,811 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/linden/indra/llcommon/llcommon_vc9.vcproj b/linden/indra/llcommon/llcommon_vc9.vcproj index 752d13f..0bb2d11 100644 --- a/linden/indra/llcommon/llcommon_vc9.vcproj +++ b/linden/indra/llcommon/llcommon_vc9.vcproj @@ -1,808 +1,808 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/linden/indra/llcommon/llfasttimer.h b/linden/indra/llcommon/llfasttimer.h index 3ecf176..8ad2667 100644 --- a/linden/indra/llcommon/llfasttimer.h +++ b/linden/indra/llcommon/llfasttimer.h @@ -62,6 +62,7 @@ public: FTM_SIMULATE_PARTICLES, FTM_UPDATE_SKY, FTM_UPDATE_TEXTURES, + FTM_UPDATE_WLPARAM, FTM_UPDATE_WATER, FTM_UPDATE_CLOUDS, FTM_UPDATE_GRASS, @@ -86,8 +87,12 @@ public: FTM_RENDER_HUD, FTM_RENDER_PARTICLES, FTM_RENDER_WATER, + FTM_RENDER_WL_SKY, + FTM_RENDER_FAKE_VBO_UPDATE, FTM_RENDER_TIMER, FTM_RENDER_UI, + FTM_RENDER_BLOOM, + FTM_RENDER_BLOOM_FBO, FTM_RENDER_FONTS, // newview specific @@ -124,6 +129,7 @@ public: FTM_GEO_RESERVE, FTM_GEO_LIGHT, FTM_GEO_SHADOW, + FTM_GEO_SKY, FTM_GEN_VOLUME, FTM_GEN_TRIANGLES, FTM_GEN_FLEX, @@ -150,7 +156,6 @@ public: FTM_PIPELINE, FTM_VFILE_WAIT, FTM_FLEXIBLE_UPDATE, - FTM_OCCLUSION, FTM_OCCLUSION_READBACK, FTM_HUD_EFFECTS, FTM_HUD_UPDATE, diff --git a/linden/indra/llcommon/llfile.cpp b/linden/indra/llcommon/llfile.cpp index 38157df..6ccf6ac 100644 --- a/linden/indra/llcommon/llfile.cpp +++ b/linden/indra/llcommon/llfile.cpp @@ -286,8 +286,36 @@ llofstream::llofstream(const char *_Filename, llofstream::~llofstream() { // destroy the object + close(); delete _Filebuffer; } #endif // #if USE_LLFILESTREAMS +/************** helper functions ********************************/ + +std::streamsize llifstream_size(llifstream& ifstr) +{ + if(!ifstr.is_open()) return 0; + std::streampos pos_old = ifstr.tellg(); + ifstr.seekg(0, ios_base::beg); + std::streampos pos_beg = ifstr.tellg(); + ifstr.seekg(0, ios_base::end); + std::streampos pos_end = ifstr.tellg(); + ifstr.seekg(pos_old, ios_base::beg); + return pos_end - pos_beg; +} + +std::streamsize llofstream_size(llofstream& ofstr) +{ + if(!ofstr.is_open()) return 0; + std::streampos pos_old = ofstr.tellp(); + ofstr.seekp(0, ios_base::beg); + std::streampos pos_beg = ofstr.tellp(); + ofstr.seekp(0, ios_base::end); + std::streampos pos_end = ofstr.tellp(); + ofstr.seekp(pos_old, ios_base::beg); + return pos_end - pos_beg; +} + + diff --git a/linden/indra/llcommon/llfile.h b/linden/indra/llcommon/llfile.h index 062b183..2650775 100644 --- a/linden/indra/llcommon/llfile.h +++ b/linden/indra/llcommon/llfile.h @@ -168,5 +168,14 @@ private: #endif +/** + * @breif filesize helpers. + * + * The file size helpers are not considered particularly efficient, + * and should only be used for config files and the like -- not in a + * loop. + */ +std::streamsize llifstream_size(llifstream& fstr); +std::streamsize llofstream_size(llofstream& fstr); #endif // not LL_LLFILE_H diff --git a/linden/indra/llcommon/llkeythrottle.h b/linden/indra/llcommon/llkeythrottle.h index c2dc9d2..8314269 100644 --- a/linden/indra/llcommon/llkeythrottle.h +++ b/linden/indra/llcommon/llkeythrottle.h @@ -192,10 +192,7 @@ public: { noteAction(id); typename LLKeyThrottleImpl::Entry& curr = (*m.currMap)[id]; - if (curr.count < m.countLimit) - { - curr.count = m.countLimit; - } + curr.count = llmax(m.countLimit, curr.count); curr.blocked = TRUE; } diff --git a/linden/indra/llcommon/lllog.cpp b/linden/indra/llcommon/lllog.cpp index 147b2d3..10f7c00 100644 --- a/linden/indra/llcommon/lllog.cpp +++ b/linden/indra/llcommon/lllog.cpp @@ -69,7 +69,8 @@ void LLLogImpl::log(const std::string message, LLSD& info) } } } - llinfos << "LLLOGMESSAGE (" << (sequence++) << ") " << message << " " << LLSDNotationStreamer(info) << llendl; + llinfos << "LLLOGMESSAGE (" << (sequence++) << ") " << message + << " " << LLSDNotationStreamer(info) << llendl; } //@brief Function to check if specified legacy log message should be sent. diff --git a/linden/indra/llcommon/llmemory.h b/linden/indra/llcommon/llmemory.h index 8a3ca0b..e1af7ba 100644 --- a/linden/indra/llcommon/llmemory.h +++ b/linden/indra/llcommon/llmemory.h @@ -243,25 +243,25 @@ protected: // Expands LLPointer to return a pointer to a special instance of class Type instead of NULL. // This is useful in instances where operations on NULL pointers are semantically safe and/or // when error checking occurs at a different granularity or in a different part of the code -// than when referencing an object via a LLHandle. +// than when referencing an object via a LLSafeHandle. // template -class LLHandle +class LLSafeHandle { public: - LLHandle() : + LLSafeHandle() : mPointer(NULL) { } - LLHandle(Type* ptr) : + LLSafeHandle(Type* ptr) : mPointer(NULL) { assign(ptr); } - LLHandle(const LLHandle& ptr) : + LLSafeHandle(const LLSafeHandle& ptr) : mPointer(NULL) { assign(ptr.mPointer); @@ -269,13 +269,13 @@ public: // support conversion up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. template - LLHandle(const LLHandle& ptr) : + LLSafeHandle(const LLSafeHandle& ptr) : mPointer(NULL) { assign(ptr.get()); } - ~LLHandle() + ~LLSafeHandle() { unref(); } @@ -300,17 +300,17 @@ public: operator const Type*() const { return mPointer; } bool operator !=(Type* ptr) const { return (mPointer != ptr); } bool operator ==(Type* ptr) const { return (mPointer == ptr); } - bool operator ==(const LLHandle& ptr) const { return (mPointer == ptr.mPointer); } - bool operator < (const LLHandle& ptr) const { return (mPointer < ptr.mPointer); } - bool operator > (const LLHandle& ptr) const { return (mPointer > ptr.mPointer); } + bool operator ==(const LLSafeHandle& ptr) const { return (mPointer == ptr.mPointer); } + bool operator < (const LLSafeHandle& ptr) const { return (mPointer < ptr.mPointer); } + bool operator > (const LLSafeHandle& ptr) const { return (mPointer > ptr.mPointer); } - LLHandle& operator =(Type* ptr) + LLSafeHandle& operator =(Type* ptr) { assign(ptr); return *this; } - LLHandle& operator =(const LLHandle& ptr) + LLSafeHandle& operator =(const LLSafeHandle& ptr) { assign(ptr.mPointer); return *this; @@ -318,7 +318,7 @@ public: // support assignment up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. template - LLHandle& operator =(const LLHandle& ptr) + LLSafeHandle& operator =(const LLSafeHandle& ptr) { assign(ptr.get()); return *this; @@ -399,11 +399,25 @@ protected: //---------------------------------------------------------------------------- -// LLSingleton implements the getInstance() method part of the Singleton pattern. It can't make -// the derived class constructors protected, though, so you have to do that yourself. -// The proper way to use LLSingleton is to inherit from it while using the typename that you'd -// like to be static as the template parameter, like so: -// class FooBar: public LLSingleton +// LLSingleton implements the getInstance() method part of the Singleton +// pattern. It can't make the derived class constructors protected, though, so +// you have to do that yourself. +// +// There are two ways to use LLSingleton. The first way is to inherit from it +// while using the typename that you'd like to be static as the template +// parameter, like so: +// +// class Foo: public LLSingleton{}; +// +// Foo* instance = Foo::getInstance(); +// +// The second way is to define a seperate class that exposes the singleton +// interface: +// +// class FooSingleton: public LLSingleton{}; +// +// Foo* instance = FooSingleton::getInstance(); +// // As currently written, it is not thread-safe. template class LLSingleton diff --git a/linden/indra/llcommon/llpreprocessor.h b/linden/indra/llcommon/llpreprocessor.h index 8bcad34..8d38904 100644 --- a/linden/indra/llcommon/llpreprocessor.h +++ b/linden/indra/llcommon/llpreprocessor.h @@ -52,33 +52,6 @@ #define LL_FORCE_INLINE __forceinline #endif -// Per-OS feature switches. - -#if LL_DARWIN - #define LL_QUICKTIME_ENABLED 1 - #define LL_LIBXUL_ENABLED 1 -#elif LL_WINDOWS - #define LL_QUICKTIME_ENABLED 1 - #define LL_LIBXUL_ENABLED 1 -#elif LL_LINUX - #define LL_QUICKTIME_ENABLED 0 - #ifndef LL_LIBXUL_ENABLED - #define LL_LIBXUL_ENABLED 1 - #endif // def LL_LIBXUL_ENABLED -#elif LL_SOLARIS - #define LL_QUICKTIME_ENABLED 0 - #ifndef LL_LIBXUL_ENABLED - #define LL_LIBXUL_ENABLED 0 - #endif // def LL_LIBXUL_ENABLED -#endif - -#if LL_LIBXUL_ENABLED && !defined(MOZILLA_INTERNAL_API) - // Without this, nsTAString.h errors out with: - // "Cannot use internal string classes without MOZILLA_INTERNAL_API defined. Use the frozen header nsStringAPI.h instead." - // It might be worth our while to figure out if we can use the frozen apis at some point... - #define MOZILLA_INTERNAL_API 1 -#endif - // Figure out differences between compilers #if defined(__GNUC__) #define GCC_VERSION (__GNUC__ * 10000 \ diff --git a/linden/indra/llcommon/llsdserialize.cpp b/linden/indra/llcommon/llsdserialize.cpp index 7813cf0..a556d5d 100644 --- a/linden/indra/llcommon/llsdserialize.cpp +++ b/linden/indra/llcommon/llsdserialize.cpp @@ -45,26 +45,18 @@ #include "lldate.h" #include "llsd.h" +#include "llstring.h" #include "lluri.h" // File constants static const int MAX_HDR_LEN = 20; static const char LEGACY_NON_HEADER[] = ""; +const std::string LLSD_BINARY_HEADER("LLSD/Binary"); +const std::string LLSD_XML_HEADER("LLSD/XML"); -//static -const char* LLSDSerialize::LLSDBinaryHeader = "LLSD/Binary"; - -//static -const char* LLSDSerialize::LLSDXMLHeader = "LLSD/XML"; - -// virtual -LLSDParser::~LLSDParser() -{ } - -// virtual -LLSDNotationParser::~LLSDNotationParser() -{ } - +/** + * LLSDSerialize + */ // static void LLSDSerialize::serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize type, U32 options) @@ -74,12 +66,12 @@ void LLSDSerialize::serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize switch (type) { case LLSD_BINARY: - str << "\n"; + str << "\n"; f = new LLSDBinaryFormatter; break; case LLSD_XML: - str << "\n"; + str << "\n"; f = new LLSDXMLFormatter; break; @@ -94,7 +86,7 @@ void LLSDSerialize::serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize } // static -bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str) +bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str, S32 max_bytes) { LLPointer p = NULL; char hdr_buf[MAX_HDR_LEN + 1] = ""; /* Flawfinder: ignore */ @@ -102,8 +94,8 @@ bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str) int inbuf = 0; bool legacy_no_header = false; bool fail_if_not_legacy = false; - std::string header = ""; - + std::string header; + /* * Get the first line before anything. */ @@ -155,15 +147,15 @@ bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str) */ if (legacy_no_header) { - LLSDXMLParser *x = new LLSDXMLParser; + LLSDXMLParser* x = new LLSDXMLParser; x->parsePart(hdr_buf, inbuf); p = x; } - else if (header == LLSDBinaryHeader) + else if (header == LLSD_BINARY_HEADER) { p = new LLSDBinaryParser; } - else if (header == LLSDXMLHeader) + else if (header == LLSD_XML_HEADER) { p = new LLSDXMLParser; } @@ -174,7 +166,7 @@ bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str) if (p.notNull()) { - p->parse(str, sd); + p->parse(str, sd, max_bytes); return true; } @@ -230,11 +222,71 @@ F64 ll_ntohd(F64 netdouble) /** * Local functions. */ -bool deserialize_string(std::istream& str, std::string& value); -bool deserialize_string_delim(std::istream& str, std::string& value, char d); -bool deserialize_string_raw(std::istream& str, std::string& value); +/** + * @brief Figure out what kind of string it is (raw or delimited) and handoff. + * + * @param istr The stream to read from. + * @param value [out] The string which was found. + * @param max_bytes The maximum possible length of the string. Passing in + * a negative value will skip this check. + * @return Returns number of bytes read off of the stream. Returns + * PARSE_FAILURE (-1) on failure. + */ +int deserialize_string(std::istream& istr, std::string& value, S32 max_bytes); + +/** + * @brief Parse a delimited string. + * + * @param istr The stream to read from, with the delimiter already popped. + * @param value [out] The string which was found. + * @param d The delimiter to use. + * @return Returns number of bytes read off of the stream. Returns + * PARSE_FAILURE (-1) on failure. + */ +int deserialize_string_delim(std::istream& istr, std::string& value, char d); + +/** + * @brief Read a raw string off the stream. + * + * @param istr The stream to read from, with the (len) parameter + * leading the stream. + * @param value [out] The string which was found. + * @param d The delimiter to use. + * @param max_bytes The maximum possible length of the string. Passing in + * a negative value will skip this check. + * @return Returns number of bytes read off of the stream. Returns + * PARSE_FAILURE (-1) on failure. + */ +int deserialize_string_raw( + std::istream& istr, + std::string& value, + S32 max_bytes); + +/** + * @brief helper method for dealing with the different notation boolean format. + * + * @param istr The stream to read from with the leading character stripped. + * @param data [out] the result of the parse. + * @param compare The string to compare the boolean against + * @param vale The value to assign to data if the parse succeeds. + * @return Returns number of bytes read off of the stream. Returns + * PARSE_FAILURE (-1) on failure. + */ +int deserialize_boolean( + std::istream& istr, + LLSD& data, + const std::string& compare, + bool value); + +/** + * @brief Do notation escaping of a string to an ostream. + * + * @param value The string to escape and serialize + * @param str The stream to serialize to. + */ void serialize_string(const std::string& value, std::ostream& str); + /** * Local constants. */ @@ -244,20 +296,96 @@ static const std::string NOTATION_FALSE_SERIAL("false"); static const char BINARY_TRUE_SERIAL = '1'; static const char BINARY_FALSE_SERIAL = '0'; -static const S32 NOTATION_PARSE_FAILURE = -1; /** * LLSDParser */ -LLSDParser::LLSDParser() +LLSDParser::LLSDParser() : mCheckLimits(true), mMaxBytesLeft(0) +{ +} + +// virtual +LLSDParser::~LLSDParser() +{ } + +S32 LLSDParser::parse(std::istream& istr, LLSD& data, S32 max_bytes) +{ + mCheckLimits = (LLSDSerialize::SIZE_UNLIMITED == max_bytes) ? false : true; + mMaxBytesLeft = max_bytes; + return doParse(istr, data); +} + + +int LLSDParser::get(std::istream& istr) const +{ + if(mCheckLimits) --mMaxBytesLeft; + return istr.get(); +} + +std::istream& LLSDParser::get( + std::istream& istr, + char* s, + std::streamsize n, + char delim) const +{ + istr.get(s, n, delim); + if(mCheckLimits) mMaxBytesLeft -= istr.gcount(); + return istr; +} + +std::istream& LLSDParser::get( + std::istream& istr, + std::streambuf& sb, + char delim) const +{ + istr.get(sb, delim); + if(mCheckLimits) mMaxBytesLeft -= istr.gcount(); + return istr; +} + +std::istream& LLSDParser::ignore(std::istream& istr) const { + istr.ignore(); + if(mCheckLimits) --mMaxBytesLeft; + return istr; } +std::istream& LLSDParser::putback(std::istream& istr, char c) const +{ + istr.putback(c); + if(mCheckLimits) ++mMaxBytesLeft; + return istr; +} + +std::istream& LLSDParser::read( + std::istream& istr, + char* s, + std::streamsize n) const +{ + istr.read(s, n); + if(mCheckLimits) mMaxBytesLeft -= istr.gcount(); + return istr; +} + +void LLSDParser::account(S32 bytes) const +{ + if(mCheckLimits) mMaxBytesLeft -= bytes; +} + + /** * LLSDNotationParser */ +LLSDNotationParser::LLSDNotationParser() +{ +} + +// virtual +LLSDNotationParser::~LLSDNotationParser() +{ } + // virtual -S32 LLSDNotationParser::parse(std::istream& istr, LLSD& data) const +S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data) const { // map: { string:object, string:object } // array: [ object, object, object ] @@ -275,7 +403,7 @@ S32 LLSDNotationParser::parse(std::istream& istr, LLSD& data) const while(isspace(c)) { // pop the whitespace. - c = istr.get(); + c = get(istr); c = istr.peek(); continue; } @@ -287,107 +415,142 @@ S32 LLSDNotationParser::parse(std::istream& istr, LLSD& data) const switch(c) { case '{': - parse_count += parseMap(istr, data); - if(istr.fail()) + { + S32 child_count = parseMap(istr, data); + if((child_count == PARSE_FAILURE) || data.isUndefined()) { - llinfos << "STREAM FAILURE reading map." << llendl; + parse_count = PARSE_FAILURE; } - if(data.isUndefined()) + else { - parse_count = NOTATION_PARSE_FAILURE; + parse_count += child_count; + } + if(istr.fail()) + { + llinfos << "STREAM FAILURE reading map." << llendl; + parse_count = PARSE_FAILURE; } break; + } case '[': - parse_count += parseArray(istr, data); - if(istr.fail()) + { + S32 child_count = parseArray(istr, data); + if((child_count == PARSE_FAILURE) || data.isUndefined()) { - llinfos << "STREAM FAILURE reading array." << llendl; + parse_count = PARSE_FAILURE; } - if(data.isUndefined()) + else { - parse_count = NOTATION_PARSE_FAILURE; + parse_count += child_count; + } + if(istr.fail()) + { + llinfos << "STREAM FAILURE reading array." << llendl; + parse_count = PARSE_FAILURE; } break; + } case '!': - c = istr.get(); + c = get(istr); data.clear(); break; case '0': - c = istr.get(); + c = get(istr); data = false; break; case 'F': case 'f': - do + ignore(istr); + c = istr.peek(); + if(isalpha(c)) { - istr.ignore(); - c = istr.peek(); - } while (isalpha(c)); - data = false; + int cnt = deserialize_boolean( + istr, + data, + NOTATION_FALSE_SERIAL, + false); + if(PARSE_FAILURE == cnt) parse_count = cnt; + else account(cnt); + } + else + { + data = false; + } if(istr.fail()) { llinfos << "STREAM FAILURE reading boolean." << llendl; + parse_count = PARSE_FAILURE; } break; case '1': - c = istr.get(); + c = get(istr); data = true; break; case 'T': case 't': - do + ignore(istr); + c = istr.peek(); + if(isalpha(c)) { - istr.ignore(); - c = istr.peek(); - } while (isalpha(c)); - data = true; + int cnt = deserialize_boolean(istr,data,NOTATION_TRUE_SERIAL,true); + if(PARSE_FAILURE == cnt) parse_count = cnt; + else account(cnt); + } + else + { + data = true; + } if(istr.fail()) { llinfos << "STREAM FAILURE reading boolean." << llendl; + parse_count = PARSE_FAILURE; } break; case 'i': { - c = istr.get(); + c = get(istr); S32 integer = 0; istr >> integer; data = integer; if(istr.fail()) { llinfos << "STREAM FAILURE reading integer." << llendl; + parse_count = PARSE_FAILURE; } break; } case 'r': { - c = istr.get(); + c = get(istr); F64 real = 0.0; istr >> real; data = real; if(istr.fail()) { llinfos << "STREAM FAILURE reading real." << llendl; + parse_count = PARSE_FAILURE; } break; } case 'u': { - c = istr.get(); + c = get(istr); LLUUID id; istr >> id; data = id; if(istr.fail()) { llinfos << "STREAM FAILURE reading uuid." << llendl; + parse_count = PARSE_FAILURE; } break; } @@ -395,126 +558,144 @@ S32 LLSDNotationParser::parse(std::istream& istr, LLSD& data) const case '\"': case '\'': case 's': - parseString(istr, data); - if(istr.fail()) + if(!parseString(istr, data)) { - llinfos << "STREAM FAILURE reading string." << llendl; + parse_count = PARSE_FAILURE; } - if(data.isUndefined()) + if(istr.fail()) { - parse_count = NOTATION_PARSE_FAILURE; + llinfos << "STREAM FAILURE reading string." << llendl; + parse_count = PARSE_FAILURE; } break; case 'l': { - c = istr.get(); // pop the 'l' - c = istr.get(); // pop the delimiter + c = get(istr); // pop the 'l' + c = get(istr); // pop the delimiter std::string str; - deserialize_string_delim(istr, str, c); - data = LLURI(str); + int cnt = deserialize_string_delim(istr, str, c); + if(PARSE_FAILURE == cnt) + { + parse_count = PARSE_FAILURE; + } + else + { + data = LLURI(str); + account(cnt); + } if(istr.fail()) { llinfos << "STREAM FAILURE reading link." << llendl; + parse_count = PARSE_FAILURE; } break; } case 'd': { - c = istr.get(); // pop the 'd' - c = istr.get(); // pop the delimiter + c = get(istr); // pop the 'd' + c = get(istr); // pop the delimiter std::string str; - deserialize_string_delim(istr, str, c); - data = LLDate(str); + int cnt = deserialize_string_delim(istr, str, c); + if(PARSE_FAILURE == cnt) + { + parse_count = PARSE_FAILURE; + } + else + { + data = LLDate(str); + account(cnt); + } if(istr.fail()) { llinfos << "STREAM FAILURE reading date." << llendl; + parse_count = PARSE_FAILURE; } break; } case 'b': - parseBinary(istr, data); - if(istr.fail()) + if(!parseBinary(istr, data)) { - llinfos << "STREAM FAILURE reading data." << llendl; + parse_count = PARSE_FAILURE; } - if(data.isUndefined()) + if(istr.fail()) { - parse_count = NOTATION_PARSE_FAILURE; + llinfos << "STREAM FAILURE reading data." << llendl; + parse_count = PARSE_FAILURE; } break; default: - data.clear(); - parse_count = NOTATION_PARSE_FAILURE; + parse_count = PARSE_FAILURE; llinfos << "Unrecognized character while parsing: int(" << (int)c - << ")" << llendl; + << ")" << llendl; break; } + if(PARSE_FAILURE == parse_count) + { + data.clear(); + } return parse_count; } -// static -LLSD LLSDNotationParser::parse(std::istream& istr) -{ - LLSDNotationParser parser; - LLSD rv; - S32 count = parser.parse(istr, rv); - lldebugs << "LLSDNotationParser::parse parsed " << count << " objects." - << llendl; - return rv; -} - S32 LLSDNotationParser::parseMap(std::istream& istr, LLSD& map) const { // map: { string:object, string:object } map = LLSD::emptyMap(); S32 parse_count = 0; - char c = istr.get(); + char c = get(istr); if(c == '{') { // eat commas, white bool found_name = false; std::string name; - c = istr.get(); + c = get(istr); while(c != '}' && istr.good()) { if(!found_name) { if((c == '\"') || (c == '\'') || (c == 's')) { - istr.putback(c); + putback(istr, c); found_name = true; - deserialize_string(istr, name); + int count = deserialize_string(istr, name, mMaxBytesLeft); + if(PARSE_FAILURE == count) return PARSE_FAILURE; + account(count); } - c = istr.get(); + c = get(istr); } else { if(isspace(c) || (c == ':')) { - c = istr.get(); + c = get(istr); continue; } - istr.putback(c); + putback(istr, c); LLSD child; - S32 count = parse(istr, child); + S32 count = doParse(istr, child); if(count > 0) { + // There must be a value for every key, thus + // child_count must be greater than 0. parse_count += count; map.insert(name, child); } else { - map.clear(); - return NOTATION_PARSE_FAILURE; + return PARSE_FAILURE; } found_name = false; - c = istr.get(); + c = get(istr); } } + if(c != '}') + { + map.clear(); + return PARSE_FAILURE; + } } return parse_count; } @@ -524,52 +705,51 @@ S32 LLSDNotationParser::parseArray(std::istream& istr, LLSD& array) const // array: [ object, object, object ] array = LLSD::emptyArray(); S32 parse_count = 0; - char c = istr.get(); + char c = get(istr); if(c == '[') { // eat commas, white - c = istr.get(); + c = get(istr); while((c != ']') && istr.good()) { LLSD child; if(isspace(c) || (c == ',')) { - c = istr.get(); + c = get(istr); continue; } - istr.putback(c); - S32 count = parse(istr, child); - if(count > 0) + putback(istr, c); + S32 count = doParse(istr, child); + if(PARSE_FAILURE == count) { - parse_count += count; - array.append(child); + return PARSE_FAILURE; } else { - array.clear(); - return NOTATION_PARSE_FAILURE; + parse_count += count; + array.append(child); } - c = istr.get(); + c = get(istr); + } + if(c != ']') + { + return PARSE_FAILURE; } } return parse_count; } -void LLSDNotationParser::parseString(std::istream& istr, LLSD& data) const +bool LLSDNotationParser::parseString(std::istream& istr, LLSD& data) const { std::string value; - if(deserialize_string(istr, value)) - { - data = value; - } - else - { - // failed to parse. - data.clear(); - } + int count = deserialize_string(istr, value, mMaxBytesLeft); + if(PARSE_FAILURE == count) return false; + account(count); + data = value; + return true; } -void LLSDNotationParser::parseBinary(std::istream& istr, LLSD& data) const +bool LLSDNotationParser::parseBinary(std::istream& istr, LLSD& data) const { // binary: b##"ff3120ab1" // or: b(len)"..." @@ -582,40 +762,44 @@ void LLSDNotationParser::parseBinary(std::istream& istr, LLSD& data) const // need to read the base out. char buf[BINARY_BUFFER_SIZE]; /* Flawfinder: ignore */ - istr.get(buf, STREAM_GET_COUNT, '"'); - char c = istr.get(); - if((c == '"') && (0 == strncmp("b(", buf, 2))) + get(istr, buf, STREAM_GET_COUNT, '"'); + char c = get(istr); + if(c != '"') return false; + if(0 == strncmp("b(", buf, 2)) { // We probably have a valid raw binary stream. determine // the size, and read it. - // *FIX: Should we set a maximum size? S32 len = strtol(buf + 2, NULL, 0); + if(mCheckLimits && (len > mMaxBytesLeft)) return false; std::vector value; if(len) { value.resize(len); - fullread(istr, (char *)&value[0], len); + account(fullread(istr, (char *)&value[0], len)); } - c = istr.get(); // strip off the trailing double-quote + c = get(istr); // strip off the trailing double-quote data = value; } - else if((c == '"') && (0 == strncmp("b64", buf, 3))) + else if(0 == strncmp("b64", buf, 3)) { // *FIX: A bit inefficient, but works for now. To make the // format better, I would need to add a hint into the // serialization format that indicated how long it was. std::stringstream coded_stream; - istr.get(*(coded_stream.rdbuf()), '\"'); - c = istr.get(); + get(istr, *(coded_stream.rdbuf()), '\"'); + c = get(istr); std::string encoded(coded_stream.str()); S32 len = apr_base64_decode_len(encoded.c_str()); std::vector value; - value.resize(len); - len = apr_base64_decode_binary(&value[0], encoded.c_str()); - value.resize(len); + if(len) + { + value.resize(len); + len = apr_base64_decode_binary(&value[0], encoded.c_str()); + value.resize(len); + } data = value; } - else if((c == '"') && (0 == strncmp("b16", buf, 3))) + else if(0 == strncmp("b16", buf, 3)) { // yay, base 16. We pop the next character which is either a // double quote or base 16 data. If it's a double quote, we're @@ -626,14 +810,14 @@ void LLSDNotationParser::parseBinary(std::istream& istr, LLSD& data) const U8 byte_buffer[BINARY_BUFFER_SIZE]; U8* write; std::vector value; - c = istr.get(); + c = get(istr); while(c != '"') { - istr.putback(c); + putback(istr, c); read = buf; write = byte_buffer; - istr.get(buf, STREAM_GET_COUNT, '"'); - c = istr.get(); + get(istr, buf, STREAM_GET_COUNT, '"'); + c = get(istr); while(*read != '\0') /*Flawfinder: ignore*/ { byte = hex_as_nybble(*read++); @@ -648,8 +832,9 @@ void LLSDNotationParser::parseBinary(std::istream& istr, LLSD& data) const } else { - data.clear(); + return false; } + return true; } @@ -666,7 +851,7 @@ LLSDBinaryParser::~LLSDBinaryParser() } // virtual -S32 LLSDBinaryParser::parse(std::istream& istr, LLSD& data) const +S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data) const { /** * Undefined: '!'
@@ -685,7 +870,7 @@ S32 LLSDBinaryParser::parse(std::istream& istr, LLSD& data) const * notation format. */ char c; - c = istr.get(); + c = get(istr); if(!istr.good()) { return 0; @@ -694,20 +879,42 @@ S32 LLSDBinaryParser::parse(std::istream& istr, LLSD& data) const switch(c) { case '{': - parse_count += parseMap(istr, data); + { + S32 child_count = parseMap(istr, data); + if((child_count == PARSE_FAILURE) || data.isUndefined()) + { + parse_count = PARSE_FAILURE; + } + else + { + parse_count += child_count; + } if(istr.fail()) { llinfos << "STREAM FAILURE reading binary map." << llendl; + parse_count = PARSE_FAILURE; } break; + } case '[': - parse_count += parseArray(istr, data); + { + S32 child_count = parseArray(istr, data); + if((child_count == PARSE_FAILURE) || data.isUndefined()) + { + parse_count = PARSE_FAILURE; + } + else + { + parse_count += child_count; + } if(istr.fail()) { llinfos << "STREAM FAILURE reading binary array." << llendl; + parse_count = PARSE_FAILURE; } break; + } case '!': data.clear(); @@ -724,7 +931,7 @@ S32 LLSDBinaryParser::parse(std::istream& istr, LLSD& data) const case 'i': { U32 value_nbo = 0; - istr.read((char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/ + read(istr, (char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/ data = (S32)ntohl(value_nbo); if(istr.fail()) { @@ -736,7 +943,7 @@ S32 LLSDBinaryParser::parse(std::istream& istr, LLSD& data) const case 'r': { F64 real_nbo = 0.0; - istr.read((char*)&real_nbo, sizeof(F64)); /*Flawfinder: ignore*/ + read(istr, (char*)&real_nbo, sizeof(F64)); /*Flawfinder: ignore*/ data = ll_ntohd(real_nbo); if(istr.fail()) { @@ -748,7 +955,7 @@ S32 LLSDBinaryParser::parse(std::istream& istr, LLSD& data) const case 'u': { LLUUID id; - istr.read((char*)(&id.mData), UUID_BYTES); /*Flawfinder: ignore*/ + read(istr, (char*)(&id.mData), UUID_BYTES); /*Flawfinder: ignore*/ data = id; if(istr.fail()) { @@ -761,19 +968,40 @@ S32 LLSDBinaryParser::parse(std::istream& istr, LLSD& data) const case '"': { std::string value; - deserialize_string_delim(istr, value, c); - data = value; + int cnt = deserialize_string_delim(istr, value, c); + if(PARSE_FAILURE == cnt) + { + parse_count = PARSE_FAILURE; + } + else + { + data = value; + account(cnt); + } + if(istr.fail()) + { + llinfos << "STREAM FAILURE reading binary (notation-style) string." + << llendl; + parse_count = PARSE_FAILURE; + } break; } case 's': { std::string value; - parseString(istr, value); - data = value; + if(parseString(istr, value)) + { + data = value; + } + else + { + parse_count = PARSE_FAILURE; + } if(istr.fail()) { llinfos << "STREAM FAILURE reading binary string." << llendl; + parse_count = PARSE_FAILURE; } break; } @@ -781,11 +1009,18 @@ S32 LLSDBinaryParser::parse(std::istream& istr, LLSD& data) const case 'l': { std::string value; - parseString(istr, value); - data = LLURI(value); + if(parseString(istr, value)) + { + data = LLURI(value); + } + else + { + parse_count = PARSE_FAILURE; + } if(istr.fail()) { llinfos << "STREAM FAILURE reading binary link." << llendl; + parse_count = PARSE_FAILURE; } break; } @@ -793,11 +1028,12 @@ S32 LLSDBinaryParser::parse(std::istream& istr, LLSD& data) const case 'd': { F64 real = 0.0; - istr.read((char*)&real, sizeof(F64)); /*Flawfinder: ignore*/ + read(istr, (char*)&real, sizeof(F64)); /*Flawfinder: ignore*/ data = LLDate(real); if(istr.fail()) { llinfos << "STREAM FAILURE reading binary date." << llendl; + parse_count = PARSE_FAILURE; } break; } @@ -806,75 +1042,94 @@ S32 LLSDBinaryParser::parse(std::istream& istr, LLSD& data) const { // We probably have a valid raw binary stream. determine // the size, and read it. - // *FIX: Should we set a maximum size? U32 size_nbo = 0; - istr.read((char*)&size_nbo, sizeof(U32)); /*Flawfinder: ignore*/ + read(istr, (char*)&size_nbo, sizeof(U32)); /*Flawfinder: ignore*/ S32 size = (S32)ntohl(size_nbo); - std::vector value; - if(size) + if(mCheckLimits && (size > mMaxBytesLeft)) { - value.resize(size); - istr.read((char*)&value[0], size); /*Flawfinder: ignore*/ + parse_count = PARSE_FAILURE; + } + else + { + std::vector value; + if(size > 0) + { + value.resize(size); + account(fullread(istr, (char*)&value[0], size)); + } + data = value; } - data = value; if(istr.fail()) { llinfos << "STREAM FAILURE reading binary." << llendl; + parse_count = PARSE_FAILURE; } break; } default: - --parse_count; + parse_count = PARSE_FAILURE; llinfos << "Unrecognized character while parsing: int(" << (int)c - << ")" << llendl; + << ")" << llendl; break; } + if(PARSE_FAILURE == parse_count) + { + data.clear(); + } return parse_count; } -// static -LLSD LLSDBinaryParser::parse(std::istream& istr) -{ - LLSDBinaryParser parser; - LLSD rv; - S32 count = parser.parse(istr, rv); - lldebugs << "LLSDBinaryParser::parse parsed " << count << " objects." - << llendl; - return rv; -} - S32 LLSDBinaryParser::parseMap(std::istream& istr, LLSD& map) const { map = LLSD::emptyMap(); U32 value_nbo = 0; - istr.read((char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/ + read(istr, (char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/ S32 size = (S32)ntohl(value_nbo); S32 parse_count = 0; S32 count = 0; - char c = istr.get(); + char c = get(istr); while(c != '}' && (count < size) && istr.good()) { std::string name; switch(c) { case 'k': - parseString(istr, name); + if(!parseString(istr, name)) + { + return PARSE_FAILURE; + } break; case '\'': case '"': - deserialize_string_delim(istr, name, c); + { + int cnt = deserialize_string_delim(istr, name, c); + if(PARSE_FAILURE == cnt) return PARSE_FAILURE; + account(cnt); break; } + } LLSD child; - S32 child_count = parse(istr, child); - if(child_count) + S32 child_count = doParse(istr, child); + if(child_count > 0) { + // There must be a value for every key, thus child_count + // must be greater than 0. parse_count += child_count; map.insert(name, child); } + else + { + return PARSE_FAILURE; + } ++count; - c = istr.get(); + c = get(istr); + } + if((c != '}') || (count < size)) + { + // Make sure it is correctly terminated and we parsed as many + // as were said to be there. + return PARSE_FAILURE; } return parse_count; } @@ -883,7 +1138,7 @@ S32 LLSDBinaryParser::parseArray(std::istream& istr, LLSD& array) const { array = LLSD::emptyArray(); U32 value_nbo = 0; - istr.read((char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/ + read(istr, (char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/ S32 size = (S32)ntohl(value_nbo); // *FIX: This would be a good place to reserve some space in the @@ -895,7 +1150,11 @@ S32 LLSDBinaryParser::parseArray(std::istream& istr, LLSD& array) const while((c != ']') && (count < size) && istr.good()) { LLSD child; - S32 child_count = parse(istr, child); + S32 child_count = doParse(istr, child); + if(PARSE_FAILURE == child_count) + { + return PARSE_FAILURE; + } if(child_count) { parse_count += child_count; @@ -904,22 +1163,33 @@ S32 LLSDBinaryParser::parseArray(std::istream& istr, LLSD& array) const ++count; c = istr.peek(); } - c = istr.get(); + c = get(istr); + if((c != ']') || (count < size)) + { + // Make sure it is correctly terminated and we parsed as many + // as were said to be there. + return PARSE_FAILURE; + } return parse_count; } -void LLSDBinaryParser::parseString( +bool LLSDBinaryParser::parseString( std::istream& istr, std::string& value) const { // *FIX: This is memory inefficient. U32 value_nbo = 0; - istr.read((char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/ + read(istr, (char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/ S32 size = (S32)ntohl(value_nbo); + if(mCheckLimits && (size > mMaxBytesLeft)) return false; std::vector buf; - buf.resize(size); - istr.read(&buf[0], size); /*Flawfinder: ignore*/ - value.assign(buf.begin(), buf.end()); + if(size) + { + buf.resize(size); + account(fullread(istr, &buf[0], size)); + value.assign(buf.begin(), buf.end()); + } + return true; } @@ -1217,33 +1487,38 @@ void LLSDBinaryFormatter::formatString( /** * local functions */ -bool deserialize_string(std::istream& str, std::string& value) +int deserialize_string(std::istream& istr, std::string& value, S32 max_bytes) { - char c = str.get(); - if (str.fail()) + char c = istr.get(); + if(istr.fail()) { - // No data in stream, bail out - return false; + // No data in stream, bail out but mention the character we + // grabbed. + return LLSDParser::PARSE_FAILURE; } - bool rv = false; + int rv = LLSDParser::PARSE_FAILURE; switch(c) { case '\'': case '"': - rv = deserialize_string_delim(str, value, c); + rv = deserialize_string_delim(istr, value, c); break; case 's': - rv = deserialize_string_raw(str, value); + // technically, less than max_bytes, but this is just meant to + // catch egregious protocol errors. parse errors will be + // caught in the case of incorrect counts. + rv = deserialize_string_raw(istr, value, max_bytes); break; default: break; } - return rv; + if(LLSDParser::PARSE_FAILURE == rv) return rv; + return rv + 1; // account for the character grabbed at the top. } -bool deserialize_string_delim( - std::istream& str, +int deserialize_string_delim( + std::istream& istr, std::string& value, char delim) { @@ -1252,16 +1527,18 @@ bool deserialize_string_delim( bool found_hex = false; bool found_digit = false; U8 byte = 0; - + int count = 0; + while (true) { - char next_char = str.get(); - - if(str.fail()) + char next_char = istr.get(); + ++count; + + if(istr.fail()) { // If our stream is empty, break out value = write_buffer.str(); - return false; + return LLSDParser::PARSE_FAILURE; } if(found_escape) @@ -1338,35 +1615,48 @@ bool deserialize_string_delim( } value = write_buffer.str(); - return true; + return count; } -bool deserialize_string_raw(std::istream& str, std::string& value) +int deserialize_string_raw( + std::istream& istr, + std::string& value, + S32 max_bytes) { - bool ok = false; + int count = 0; const S32 BUF_LEN = 20; char buf[BUF_LEN]; /* Flawfinder: ignore */ - str.get(buf, BUF_LEN - 1, ')'); - char c = str.get(); - c = str.get(); + istr.get(buf, BUF_LEN - 1, ')'); + count += istr.gcount(); + char c = istr.get(); + c = istr.get(); + count += 2; if(((c == '"') || (c == '\'')) && (buf[0] == '(')) { // We probably have a valid raw string. determine // the size, and read it. - // *FIX: Should we set a maximum size? // *FIX: This is memory inefficient. S32 len = strtol(buf + 1, NULL, 0); + if((max_bytes>0)&&(len>max_bytes)) return LLSDParser::PARSE_FAILURE; std::vector buf; - buf.resize(len); - str.read(&buf[0], len); /*Flawfinder: ignore*/ - value.assign(buf.begin(), buf.end()); - c = str.get(); - if((c == '"') || (c == '\'')) + if(len) + { + buf.resize(len); + count += fullread(istr, (char *)&buf[0], len); + value.assign(buf.begin(), buf.end()); + } + c = istr.get(); + ++count; + if(!((c == '"') || (c == '\''))) { - ok = true; + return LLSDParser::PARSE_FAILURE; } } - return ok; + else + { + return LLSDParser::PARSE_FAILURE; + } + return count; } static const char* NOTATION_STRING_CHARACTERS[256] = @@ -1641,6 +1931,43 @@ void serialize_string(const std::string& value, std::ostream& str) } } +int deserialize_boolean( + std::istream& istr, + LLSD& data, + const std::string& compare, + bool value) +{ + // + // this method is a little goofy, because it gets the stream at + // the point where the t or f has already been + // consumed. Basically, parse for a patch to the string passed in + // starting at index 1. If it's a match: + // * assign data to value + // * return the number of bytes read + // otherwise: + // * set data to LLSD::null + // * return LLSDParser::PARSE_FAILURE (-1) + // + int bytes_read = 0; + std::string::size_type ii = 0; + char c = istr.peek(); + while((++ii < compare.size()) + && (tolower(c) == (int)compare[ii]) + && istr.good()) + { + istr.ignore(); + ++bytes_read; + c = istr.peek(); + } + if(compare.size() != ii) + { + data.clear(); + return LLSDParser::PARSE_FAILURE; + } + data = value; + return bytes_read; +} + std::ostream& operator<<(std::ostream& s, const LLSD& llsd) { s << LLSDNotationStreamer(llsd); diff --git a/linden/indra/llcommon/llsdserialize.h b/linden/indra/llcommon/llsdserialize.h index e1e81d5..41e0aa5 100644 --- a/linden/indra/llcommon/llsdserialize.h +++ b/linden/indra/llcommon/llsdserialize.h @@ -40,7 +40,7 @@ /** * @class LLSDParser - * @brief Abstract base class for simple LLSD parsers. + * @brief Abstract base class for LLSD parsers. */ class LLSDParser : public LLRefCount { @@ -52,6 +52,14 @@ protected: public: /** + * @brief Anonymous enum to indicate parsing failure. + */ + enum + { + PARSE_FAILURE = -1 + }; + + /** * @brief Constructor */ LLSDParser(); @@ -67,12 +75,122 @@ public: * caller. * @param istr The input stream. * @param data[out] The newly parse structured data. - * @return Returns The number of LLSD objects parsed into data. + * @param max_bytes The maximum number of bytes that will be in + * the stream. Pass in LLSDSerialize::SIZE_UNLIMITED (-1) to set no + * byte limit. + * @return Returns the number of LLSD objects parsed into + * data. Returns PARSE_FAILURE (-1) on parse failure. + */ + S32 parse(std::istream& istr, LLSD& data, S32 max_bytes); + +protected: + /** + * @brief Pure virtual base for doing the parse. + * + * This method parses the istream for a structured data. This + * method assumes that the istream is a complete llsd object -- + * for example an opened and closed map with an arbitrary nesting + * of elements. This method will return after reading one data + * object, allowing continued reading from the stream by the + * caller. + * @param istr The input stream. + * @param data[out] The newly parse structured data. + * @return Returns the number of LLSD objects parsed into + * data. Returns PARSE_FAILURE (-1) on parse failure. + */ + virtual S32 doParse(std::istream& istr, LLSD& data) const = 0; + + /* @name Simple istream helper methods + * + * These helper methods exist to help correctly use the + * mMaxBytesLeft without really thinking about it for most simple + * operations. Use of the streamtools in llstreamtools.h will + * require custom wrapping. + */ + //@{ + /** + * @brief get a byte off the stream + * + * @param istr The istream to work with. + * @return returns the next character. + */ + int get(std::istream& istr) const; + + /** + * @brief get several bytes off the stream into a buffer. + * + * @param istr The istream to work with. + * @param s The buffer to get into + * @param n Extract maximum of n-1 bytes and null temrinate. + * @param delim Delimiter to get until found. + * @return Returns istr. + */ + std::istream& get( + std::istream& istr, + char* s, + std::streamsize n, + char delim) const; + + /** + * @brief get several bytes off the stream into a streambuf + * + * @param istr The istream to work with. + * @param sb The streambuf to read into + * @param delim Delimiter to get until found. + * @return Returns istr. + */ + std::istream& get( + std::istream& istr, + std::streambuf& sb, + char delim) const; + + /** + * @brief ignore the next byte on the istream + * + * @param istr The istream to work with. + * @return Returns istr. + */ + std::istream& ignore(std::istream& istr) const; + + /** + * @brief put the last character retrieved back on the stream + * + * @param istr The istream to work with. + * @param c The character to put back + * @return Returns istr. + */ + std::istream& putback(std::istream& istr, char c) const; + + /** + * @brief read a block of n characters into a buffer + * + * @param istr The istream to work with. + * @param s The buffer to read into + * @param n The number of bytes to read. + * @return Returns istr. + */ + std::istream& read(std::istream& istr, char* s, std::streamsize n) const; + //@} + +protected: + /** + * @brief Accunt for bytes read outside of the istream helpers. + * + * Conceptually const since it only modifies mutable members. + * @param bytes The number of bytes read. */ - virtual S32 parse(std::istream& istr, LLSD& data) const = 0; + void account(S32 bytes) const; protected: + /** + * @brief boolean to set if byte counts should be checked during parsing. + */ + bool mCheckLimits; + /** + * @brief The maximum number of bytes left to be parsed. + */ + mutable S32 mMaxBytesLeft; }; /** @@ -91,8 +209,9 @@ public: /** * @brief Constructor */ - LLSDNotationParser() {} + LLSDNotationParser(); +protected: /** * @brief Call this method to parse a stream for LLSD. * @@ -105,21 +224,9 @@ public: * @param istr The input stream. * @param data[out] The newly parse structured data. Undefined on failure. * @return Returns the number of LLSD objects parsed into - * data. Returns -1 on parse failure. - */ - virtual S32 parse(std::istream& istr, LLSD& data) const; - - /** - * @brief Simple notation parse. - * - * This simplified parser cannot not distinguish between a failed - * parse and a parse which yields a single undefined LLSD. You can - * use this if error checking will be implicit in the use of the - * results of the parse. - * @param istr The input stream. - * @return Returns the parsed LLSD object. + * data. Returns PARSE_FAILURE (-1) on parse failure. */ - static LLSD parse(std::istream& istr); + virtual S32 doParse(std::istream& istr, LLSD& data) const; private: /** @@ -145,16 +252,18 @@ private: * * @param istr The input stream. * @param data[out] The data to assign. + * @return Retuns true if a complete string was parsed. */ - void parseString(std::istream& istr, LLSD& data) const; + bool parseString(std::istream& istr, LLSD& data) const; /** * @brief Parse binary data from the stream. * * @param istr The input stream. * @param data[out] The data to assign. + * @return Retuns true if a complete blob was parsed. */ - void parseBinary(std::istream& istr, LLSD& data) const; + bool parseBinary(std::istream& istr, LLSD& data) const; }; /** @@ -175,6 +284,7 @@ public: */ LLSDXMLParser(); +protected: /** * @brief Call this method to parse a stream for LLSD. * @@ -186,15 +296,16 @@ public: * caller. * @param istr The input stream. * @param data[out] The newly parse structured data. - * @return Returns the number of LLSD objects parsed into data. + * @return Returns the number of LLSD objects parsed into + * data. Returns PARSE_FAILURE (-1) on parse failure. */ - virtual S32 parse(std::istream& istr, LLSD& data) const; + virtual S32 doParse(std::istream& istr, LLSD& data) const; private: class Impl; Impl& impl; - void parsePart(const char *buf, int len); + void parsePart(const char* buf, int len); friend class LLSDSerialize; }; @@ -216,6 +327,7 @@ public: */ LLSDBinaryParser(); +protected: /** * @brief Call this method to parse a stream for LLSD. * @@ -227,21 +339,10 @@ public: * caller. * @param istr The input stream. * @param data[out] The newly parse structured data. - * @return Returns the number of LLSD objects parsed into data. - */ - virtual S32 parse(std::istream& istr, LLSD& data) const; - - /** - * @brief Simple notation parse. - * - * This simplified parser cannot not distinguish between a failed - * parse and a parse which yields a single undefined LLSD. You can - * use this if error checking will be implicit in the use of the - * results of the parse. - * @param istr The input stream. - * @return Returns the parsed LLSD object. + * @return Returns the number of LLSD objects parsed into + * data. Returns -1 on parse failure. */ - static LLSD parse(std::istream& istr); + virtual S32 doParse(std::istream& istr, LLSD& data) const; private: /** @@ -267,8 +368,9 @@ private: * * @param istr The input stream. * @param value[out] The string to assign. + * @return Retuns true if a complete string was parsed. */ - void parseString(std::istream& istr, std::string& value) const; + bool parseString(std::istream& istr, std::string& value) const; }; @@ -544,7 +646,7 @@ typedef LLSDOStreamer LLSDXMLStreamer; /** * @class LLSDSerialize - * @Serializer / deserializer for the various LLSD formats + * @brief Serializer / deserializer for the various LLSD formats */ class LLSDSerialize { @@ -554,12 +656,32 @@ public: LLSD_BINARY, LLSD_XML }; + /** + * @brief anonymouse enumeration for useful max_bytes constants. + */ + enum + { + // Setting an unlimited size is discouraged and should only be + // used when reading cin or another stream source which does + // not provide access to size. + SIZE_UNLIMITED = -1, + }; + /* * Generic in/outs */ static void serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize, U32 options = LLSDFormatter::OPTIONS_NONE); - static bool deserialize(LLSD& sd, std::istream& str); + + /** + * @breif Examine a stream, and parse 1 sd object out based on contents. + * + * @param sd [out] The data found on the stream + * @param str The incoming stream + * @param max_bytes the maximum number of bytes to parse + * @return Returns true if the stream appears to contain valid data + */ + static bool deserialize(LLSD& sd, std::istream& str, S32 max_bytes); /* * Notation Methods @@ -569,10 +691,17 @@ public: LLPointer f = new LLSDNotationFormatter; return f->format(sd, str, LLSDFormatter::OPTIONS_NONE); } - static S32 fromNotation(LLSD& sd, std::istream& str) + static S32 fromNotation(LLSD& sd, std::istream& str, S32 max_bytes) + { + LLPointer p = new LLSDNotationParser; + return p->parse(str, sd, max_bytes); + } + static LLSD fromNotation(std::istream& str, S32 max_bytes) { LLPointer p = new LLSDNotationParser; - return p->parse(str, sd); + LLSD sd; + (void)p->parse(str, sd, max_bytes); + return sd; } /* @@ -588,10 +717,13 @@ public: LLPointer f = new LLSDXMLFormatter; return f->format(sd, str, LLSDFormatter::OPTIONS_PRETTY); } + static S32 fromXML(LLSD& sd, std::istream& str) { + // no need for max_bytes since xml formatting is not + // subvertable by bad sizes. LLPointer p = new LLSDXMLParser; - return p->parse(str, sd); + return p->parse(str, sd, LLSDSerialize::SIZE_UNLIMITED); } /* @@ -602,14 +734,18 @@ public: LLPointer f = new LLSDBinaryFormatter; return f->format(sd, str, LLSDFormatter::OPTIONS_NONE); } - static S32 fromBinary(LLSD& sd, std::istream& str) + static S32 fromBinary(LLSD& sd, std::istream& str, S32 max_bytes) { LLPointer p = new LLSDBinaryParser; - return p->parse(str, sd); + return p->parse(str, sd, max_bytes); + } + static LLSD fromBinary(std::istream& str, S32 max_bytes) + { + LLPointer p = new LLSDBinaryParser; + LLSD sd; + (void)p->parse(str, sd, max_bytes); + return sd; } -private: - static const char *LLSDBinaryHeader; - static const char *LLSDXMLHeader; }; #endif // LL_LLSDSERIALIZE_H diff --git a/linden/indra/llcommon/llsdserialize_xml.cpp b/linden/indra/llcommon/llsdserialize_xml.cpp index b8adcf1..7de0c35 100644 --- a/linden/indra/llcommon/llsdserialize_xml.cpp +++ b/linden/indra/llcommon/llsdserialize_xml.cpp @@ -305,6 +305,7 @@ private: XML_Parser mParser; LLSD mResult; + S32 mParseCount; bool mInLLSDElement; bool mGracefullStop; @@ -411,12 +412,12 @@ S32 LLSDXMLParser::Impl::parse(std::istream& input, LLSD& data) } llinfos << "LLSDXMLParser::Impl::parse: XML_STATUS_ERROR parsing:" << (char*) buffer << llendl; data = LLSD(); - return -1; + return LLSDParser::PARSE_FAILURE; } clear_eol(input); data = mResult; - return 1; + return mParseCount; } void LLSDXMLParser::Impl::reset() @@ -428,6 +429,7 @@ void LLSDXMLParser::Impl::reset() } mResult.clear(); + mParseCount = 0; mInLLSDElement = false; mDepth = 0; @@ -472,7 +474,7 @@ LLSDXMLParser::Impl::findAttribute(const XML_Char* name, const XML_Char** pairs) return NULL; } -void LLSDXMLParser::Impl::parsePart(const char *buf, int len) +void LLSDXMLParser::Impl::parsePart(const char* buf, int len) { void * buffer = XML_GetBuffer(mParser, len); if (buffer != NULL && buf != NULL) @@ -486,7 +488,7 @@ void LLSDXMLParser::Impl::parsePart(const char *buf, int len) void LLSDXMLParser::Impl::startElementHandler(const XML_Char* name, const XML_Char** attributes) { - mDepth += 1; + ++mDepth; if (mSkipping) { return; @@ -554,6 +556,7 @@ void LLSDXMLParser::Impl::startElementHandler(const XML_Char* name, const XML_Ch return startSkipping(); } + ++mParseCount; switch (element) { case ELEMENT_MAP: @@ -572,7 +575,7 @@ void LLSDXMLParser::Impl::startElementHandler(const XML_Char* name, const XML_Ch void LLSDXMLParser::Impl::endElementHandler(const XML_Char* name) { - mDepth -= 1; + --mDepth; if (mSkipping) { if (mDepth < mSkipThrough) @@ -715,10 +718,10 @@ LLSDXMLParser::Impl::Element LLSDXMLParser::Impl::readElement(const XML_Char* na - - -LLSDXMLParser::LLSDXMLParser() - : impl(* new Impl) +/** + * LLSDXMLParser + */ +LLSDXMLParser::LLSDXMLParser() : impl(* new Impl) { } @@ -733,7 +736,7 @@ void LLSDXMLParser::parsePart(const char *buf, int len) } // virtual -S32 LLSDXMLParser::parse(std::istream& input, LLSD& data) const +S32 LLSDXMLParser::doParse(std::istream& input, LLSD& data) const { return impl.parse(input, data); } diff --git a/linden/indra/llcommon/llstreamtools.cpp b/linden/indra/llcommon/llstreamtools.cpp index 5419b24..669bdd0 100644 --- a/linden/indra/llcommon/llstreamtools.cpp +++ b/linden/indra/llcommon/llstreamtools.cpp @@ -538,23 +538,32 @@ void get_keyword_and_value(std::string& keyword, } } -std::istream& fullread(std::istream& str, char *buf, std::streamsize requested) +std::streamsize fullread( + std::istream& istr, + char* buf, + std::streamsize requested) { std::streamsize got; std::streamsize total = 0; - str.read(buf, requested); /*Flawfinder: ignore*/ - got = str.gcount(); + istr.read(buf, requested); /*Flawfinder: ignore*/ + got = istr.gcount(); total += got; - while (got && total < requested) + while(got && total < requested) { - if (str.fail()) - str.clear(); - str.read(buf + total, requested - total); /*Flawfinder: ignore*/ - got = str.gcount(); + if(istr.fail()) + { + // If bad is true, not much we can doo -- it implies loss + // of stream integrity. Bail in that case, and otherwise + // clear and attempt to continue. + if(istr.bad()) return total; + istr.clear(); + } + istr.read(buf + total, requested - total); /*Flawfinder: ignore*/ + got = istr.gcount(); total += got; } - return str; + return total; } std::istream& operator>>(std::istream& str, const char *tocheck) diff --git a/linden/indra/llcommon/llstreamtools.h b/linden/indra/llcommon/llstreamtools.h index 0114e4c..9db90c8 100644 --- a/linden/indra/llcommon/llstreamtools.h +++ b/linden/indra/llcommon/llstreamtools.h @@ -114,7 +114,12 @@ void get_keyword_and_value(std::string& keyword, // continue to read from the stream until you really can't // read anymore or until we hit the count. Some istream // implimentations have a max that they will read. -std::istream& fullread(std::istream& str, char *buf, std::streamsize requested); +// Returns the number of bytes read. +std::streamsize fullread( + std::istream& istr, + char* buf, + std::streamsize requested); + std::istream& operator>>(std::istream& str, const char *tocheck); diff --git a/linden/indra/llcommon/llsys.cpp b/linden/indra/llcommon/llsys.cpp index 2a6e466..ccdcd00 100644 --- a/linden/indra/llcommon/llsys.cpp +++ b/linden/indra/llcommon/llsys.cpp @@ -679,7 +679,8 @@ BOOL gunzip_file(const char *srcfile, const char *dstfile) size_t nwrit = fwrite(buffer, sizeof(U8), bytes, dst); if (nwrit < (size_t) bytes) { - llerrs << "Short write on " << tmpfile << llendl; + llwarns << "Short write on " << tmpfile << ": Wrote " << nwrit << " of " << bytes << " bytes." << llendl; + goto err; } } while(gzeof(src) == 0); fclose(dst); diff --git a/linden/indra/llcommon/llthread.cpp b/linden/indra/llcommon/llthread.cpp index c0e92ca..48ebc4b 100644 --- a/linden/indra/llcommon/llthread.cpp +++ b/linden/indra/llcommon/llthread.cpp @@ -31,6 +31,8 @@ #include "linden_common.h" #include "llapr.h" +#include "apr-1/apr_portable.h" + #include "llthread.h" #include "lltimer.h" @@ -225,6 +227,11 @@ void LLThread::setQuitting() wake(); } +// static +U32 LLThread::currentID() +{ + return (U32)apr_os_thread_current(); +} // static void LLThread::yield() diff --git a/linden/indra/llcommon/llthread.h b/linden/indra/llcommon/llthread.h index 5c71c34..608b218 100644 --- a/linden/indra/llcommon/llthread.h +++ b/linden/indra/llcommon/llthread.h @@ -56,14 +56,14 @@ public: virtual ~LLThread(); // Warning! You almost NEVER want to destroy a thread unless it's in the STOPPED state. virtual void shutdown(); // stops the thread - static void yield(); // Static because it can be called by the main thread, which doesn't have an LLThread data structure. - - bool isQuitting() const { return (QUITTING == mStatus); } bool isStopped() const { return (STOPPED == mStatus); } - // PAUSE / RESUME functionality. See source code for important usage notes. + static U32 currentID(); // Return ID of current thread + static void yield(); // Static because it can be called by the main thread, which doesn't have an LLThread data structure. + public: + // PAUSE / RESUME functionality. See source code for important usage notes. // Called from MAIN THREAD. void pause(); void unpause(); @@ -127,7 +127,7 @@ protected: class LLMutex { public: - LLMutex(apr_pool_t *apr_poolp); // Defaults to global pool, could use the thread pool as well. + LLMutex(apr_pool_t *apr_poolp); // NULL pool constructs a new pool for the mutex ~LLMutex(); void lock(); // blocks diff --git a/linden/indra/llcommon/llversionserver.h b/linden/indra/llcommon/llversionserver.h index 079fa85..922e4ac 100644 --- a/linden/indra/llcommon/llversionserver.h +++ b/linden/indra/llcommon/llversionserver.h @@ -33,9 +33,9 @@ #define LL_LLVERSIONSERVER_H const S32 LL_VERSION_MAJOR = 1; -const S32 LL_VERSION_MINOR = 18; -const S32 LL_VERSION_PATCH = 6; -const S32 LL_VERSION_BUILD = 76747; +const S32 LL_VERSION_MINOR = 19; +const S32 LL_VERSION_PATCH = 1; +const S32 LL_VERSION_BUILD = 80913; const char * const LL_CHANNEL = "Second Life Server"; diff --git a/linden/indra/llcommon/llversionviewer.h b/linden/indra/llcommon/llversionviewer.h index c14efe8..c1e6778 100644 --- a/linden/indra/llcommon/llversionviewer.h +++ b/linden/indra/llcommon/llversionviewer.h @@ -34,8 +34,8 @@ const S32 LL_VERSION_MAJOR = 1; const S32 LL_VERSION_MINOR = 19; -const S32 LL_VERSION_PATCH = 0; -const S32 LL_VERSION_BUILD = 5; +const S32 LL_VERSION_PATCH = 1; +const S32 LL_VERSION_BUILD = 0; const char * const LL_CHANNEL = "Second Life Release"; -- cgit v1.1