summaryrefslogtreecommitdiff
path: root/indra/llcommon/llsdserialize.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcommon/llsdserialize.cpp')
-rw-r--r--indra/llcommon/llsdserialize.cpp265
1 files changed, 157 insertions, 108 deletions
diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp
index a510b73096..e7482b601d 100644
--- a/indra/llcommon/llsdserialize.cpp
+++ b/indra/llcommon/llsdserialize.cpp
@@ -48,12 +48,13 @@
#endif
#include "lldate.h"
+#include "llmemorystream.h"
#include "llsd.h"
#include "llstring.h"
#include "lluri.h"
// File constants
-static const int MAX_HDR_LEN = 20;
+static const size_t MAX_HDR_LEN = 20;
static const S32 UNZIP_LLSD_MAX_DEPTH = 96;
static const char LEGACY_NON_HEADER[] = "<llsd>";
const std::string LLSD_BINARY_HEADER("LLSD/Binary");
@@ -64,6 +65,23 @@ const std::string LLSD_NOTATION_HEADER("llsd/notation");
#define windowBits 15
#define ENABLE_ZLIB_GZIP 32
+// If we published this in llsdserialize.h, we could use it in the
+// implementation of LLSDOStreamer's operator<<().
+template <class Formatter>
+void format_using(const LLSD& data, std::ostream& ostr,
+ LLSDFormatter::EFormatterOptions options=LLSDFormatter::OPTIONS_PRETTY_BINARY)
+{
+ LLPointer<Formatter> f{ new Formatter };
+ f->format(data, ostr, options);
+}
+
+template <class Parser>
+S32 parse_using(std::istream& istr, LLSD& data, size_t max_bytes, S32 max_depth=-1)
+{
+ LLPointer<Parser> p{ new Parser };
+ return p->parse(istr, data, max_bytes, max_depth);
+}
+
/**
* LLSDSerialize
*/
@@ -86,10 +104,10 @@ void LLSDSerialize::serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize
f = new LLSDXMLFormatter;
break;
- case LLSD_NOTATION:
- str << "<? " << LLSD_NOTATION_HEADER << " ?>\n";
- f = new LLSDNotationFormatter;
- break;
+ case LLSD_NOTATION:
+ str << "<? " << LLSD_NOTATION_HEADER << " ?>\n";
+ f = new LLSDNotationFormatter;
+ break;
default:
LL_WARNS() << "serialize request for unknown ELLSD_Serialize" << LL_ENDL;
@@ -102,20 +120,33 @@ void LLSDSerialize::serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize
}
// static
-bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str, S32 max_bytes)
+bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str, llssize max_bytes)
{
- LLPointer<LLSDParser> p = NULL;
char hdr_buf[MAX_HDR_LEN + 1] = ""; /* Flawfinder: ignore */
- int i;
- int inbuf = 0;
- bool legacy_no_header = false;
bool fail_if_not_legacy = false;
- std::string header;
/*
- * Get the first line before anything.
+ * Get the first line before anything. Don't read more than max_bytes:
+ * this get() overload reads no more than (count-1) bytes into the
+ * specified buffer. In the usual case when max_bytes exceeds
+ * sizeof(hdr_buf), get() will read no more than sizeof(hdr_buf)-2.
*/
- str.get(hdr_buf, MAX_HDR_LEN, '\n');
+ str.get(hdr_buf, llmin(max_bytes+1, sizeof(hdr_buf)-1), '\n');
+ auto inbuf = str.gcount();
+ // https://en.cppreference.com/w/cpp/io/basic_istream/get
+ // When the get() above sees the specified delimiter '\n', it stops there
+ // without pulling it from the stream. If it turns out that the stream
+ // does NOT contain a header, and the content includes meaningful '\n',
+ // it's important to pull that into hdr_buf too.
+ if (inbuf < max_bytes && str.get(hdr_buf[inbuf]))
+ {
+ // got the delimiting '\n'
+ ++inbuf;
+ // None of the following requires that hdr_buf contain a final '\0'
+ // byte. We could store one if needed, since even the incremented
+ // inbuf won't exceed sizeof(hdr_buf)-1, but there's no need.
+ }
+ std::string header{ hdr_buf, static_cast<std::string::size_type>(inbuf) };
if (str.fail())
{
str.clear();
@@ -123,79 +154,97 @@ bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str, S32 max_bytes)
}
if (!strncasecmp(LEGACY_NON_HEADER, hdr_buf, strlen(LEGACY_NON_HEADER))) /* Flawfinder: ignore */
+ { // Create a LLSD XML parser, and parse the first chunk read above.
+ LLSDXMLParser x;
+ x.parsePart(hdr_buf, inbuf); // Parse the first part that was already read
+ auto parsed = x.parse(str, sd, max_bytes - inbuf); // Parse the rest of it
+ // Formally we should probably check (parsed != PARSE_FAILURE &&
+ // parsed > 0), but since PARSE_FAILURE is -1, this suffices.
+ return (parsed > 0);
+ }
+
+ if (fail_if_not_legacy)
{
- legacy_no_header = true;
- inbuf = (int)str.gcount();
+ LL_WARNS() << "deserialize LLSD parse failure" << LL_ENDL;
+ return false;
}
- else
+
+ /*
+ * Remove the newline chars
+ */
+ std::string::size_type lastchar = header.find_last_not_of("\r\n");
+ if (lastchar != std::string::npos)
{
- if (fail_if_not_legacy)
- goto fail;
- /*
- * Remove the newline chars
- */
- for (i = 0; i < MAX_HDR_LEN; i++)
- {
- if (hdr_buf[i] == 0 || hdr_buf[i] == '\r' ||
- hdr_buf[i] == '\n')
- {
- hdr_buf[i] = 0;
- break;
- }
- }
- header = hdr_buf;
+ // It's important that find_last_not_of() returns size_type, which is
+ // why lastchar explicitly declares the type above. erase(size_type)
+ // erases from that offset to the end of the string, whereas
+ // erase(iterator) erases only a single character.
+ header.erase(lastchar+1);
+ }
- std::string::size_type start = std::string::npos;
- std::string::size_type end = std::string::npos;
- start = header.find_first_not_of("<? ");
- if (start != std::string::npos)
+ // trim off the <? ... ?> header syntax
+ auto start = header.find_first_not_of("<? ");
+ if (start != std::string::npos)
+ {
+ auto end = header.find_first_of(" ?", start);
+ if (end != std::string::npos)
{
- end = header.find_first_of(" ?", start);
+ header = header.substr(start, end - start);
+ ws(str);
}
- if ((start == std::string::npos) || (end == std::string::npos))
- goto fail;
-
- header = header.substr(start, end - start);
- ws(str);
}
/*
* Create the parser as appropriate
*/
- if (legacy_no_header)
- { // Create a LLSD XML parser, and parse the first chunk read above
- LLSDXMLParser* x = new LLSDXMLParser();
- x->parsePart(hdr_buf, inbuf); // Parse the first part that was already read
- x->parseLines(str, sd); // Parse the rest of it
- delete x;
- return true;
- }
-
- if (header == LLSD_BINARY_HEADER)
+ if (0 == LLStringUtil::compareInsensitive(header, LLSD_BINARY_HEADER))
{
- p = new LLSDBinaryParser;
+ return (parse_using<LLSDBinaryParser>(str, sd, max_bytes-inbuf) > 0);
}
- else if (header == LLSD_XML_HEADER)
+ else if (0 == LLStringUtil::compareInsensitive(header, LLSD_XML_HEADER))
{
- p = new LLSDXMLParser;
+ return (parse_using<LLSDXMLParser>(str, sd, max_bytes-inbuf) > 0);
}
- else if (header == LLSD_NOTATION_HEADER)
+ else if (0 == LLStringUtil::compareInsensitive(header, LLSD_NOTATION_HEADER))
{
- p = new LLSDNotationParser;
+ return (parse_using<LLSDNotationParser>(str, sd, max_bytes-inbuf) > 0);
}
- else
+ else // no header we recognize
{
- LL_WARNS() << "deserialize request for unknown ELLSD_Serialize" << LL_ENDL;
- }
-
- if (p.notNull())
- {
- p->parse(str, sd, max_bytes);
- return true;
+ LLPointer<LLSDParser> p;
+ if (inbuf && hdr_buf[0] == '<')
+ {
+ // looks like XML
+ LL_DEBUGS() << "deserialize request with no header, assuming XML" << LL_ENDL;
+ p = new LLSDXMLParser;
+ }
+ else
+ {
+ // assume notation
+ LL_DEBUGS() << "deserialize request with no header, assuming notation" << LL_ENDL;
+ p = new LLSDNotationParser;
+ }
+ // Since we've already read 'inbuf' bytes into 'hdr_buf', prepend that
+ // data to whatever remains in 'str'.
+ LLMemoryStreamBuf already(reinterpret_cast<const U8*>(hdr_buf), inbuf);
+ cat_streambuf prebuff(&already, str.rdbuf());
+ std::istream prepend(&prebuff);
+#if 1
+ return (p->parse(prepend, sd, max_bytes) > 0);
+#else
+ // debugging the reconstituted 'prepend' stream
+ // allocate a buffer that we hope is big enough for the whole thing
+ std::vector<char> wholemsg((max_bytes == size_t(SIZE_UNLIMITED))? 1024 : max_bytes);
+ prepend.read(wholemsg.data(), std::min(max_bytes, wholemsg.size()));
+ LLMemoryStream replay(reinterpret_cast<const U8*>(wholemsg.data()), prepend.gcount());
+ auto success{ p->parse(replay, sd, prepend.gcount()) > 0 };
+ {
+ LL_DEBUGS() << (success? "parsed: $$" : "failed: '")
+ << std::string(wholemsg.data(), llmin(prepend.gcount(), 100)) << "$$"
+ << LL_ENDL;
+ }
+ return success;
+#endif
}
-
-fail:
- LL_WARNS() << "deserialize LLSD parse failure" << LL_ENDL;
- return false;
}
/**
@@ -255,7 +304,7 @@ F64 ll_ntohd(F64 netdouble)
* @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);
+llssize deserialize_string(std::istream& istr, std::string& value, llssize max_bytes);
/**
* @brief Parse a delimited string.
@@ -266,7 +315,7 @@ int deserialize_string(std::istream& istr, std::string& value, S32 max_bytes);
* @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);
+llssize deserialize_string_delim(std::istream& istr, std::string& value, char d);
/**
* @brief Read a raw string off the stream.
@@ -280,10 +329,10 @@ int deserialize_string_delim(std::istream& istr, std::string& value, char d);
* @return Returns number of bytes read off of the stream. Returns
* PARSE_FAILURE (-1) on failure.
*/
-int deserialize_string_raw(
+llssize deserialize_string_raw(
std::istream& istr,
std::string& value,
- S32 max_bytes);
+ llssize max_bytes);
/**
* @brief helper method for dealing with the different notation boolean format.
@@ -295,7 +344,7 @@ int deserialize_string_raw(
* @return Returns number of bytes read off of the stream. Returns
* PARSE_FAILURE (-1) on failure.
*/
-int deserialize_boolean(
+llssize deserialize_boolean(
std::istream& istr,
LLSD& data,
const std::string& compare,
@@ -332,7 +381,7 @@ LLSDParser::LLSDParser()
LLSDParser::~LLSDParser()
{ }
-S32 LLSDParser::parse(std::istream& istr, LLSD& data, S32 max_bytes, S32 max_depth)
+S32 LLSDParser::parse(std::istream& istr, LLSD& data, llssize max_bytes, S32 max_depth)
{
mCheckLimits = (LLSDSerialize::SIZE_UNLIMITED == max_bytes) ? false : true;
mMaxBytesLeft = max_bytes;
@@ -362,7 +411,7 @@ std::istream& LLSDParser::get(
char delim) const
{
istr.get(s, n, delim);
- if(mCheckLimits) mMaxBytesLeft -= (int)istr.gcount();
+ if(mCheckLimits) mMaxBytesLeft -= istr.gcount();
return istr;
}
@@ -372,7 +421,7 @@ std::istream& LLSDParser::get(
char delim) const
{
istr.get(sb, delim);
- if(mCheckLimits) mMaxBytesLeft -= (int)istr.gcount();
+ if(mCheckLimits) mMaxBytesLeft -= istr.gcount();
return istr;
}
@@ -396,11 +445,11 @@ std::istream& LLSDParser::read(
std::streamsize n) const
{
istr.read(s, n);
- if(mCheckLimits) mMaxBytesLeft -= (int)istr.gcount();
+ if(mCheckLimits) mMaxBytesLeft -= istr.gcount();
return istr;
}
-void LLSDParser::account(S32 bytes) const
+void LLSDParser::account(llssize bytes) const
{
if(mCheckLimits) mMaxBytesLeft -= bytes;
}
@@ -505,7 +554,7 @@ S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) c
c = istr.peek();
if(isalpha(c))
{
- int cnt = deserialize_boolean(
+ auto cnt = deserialize_boolean(
istr,
data,
NOTATION_FALSE_SERIAL,
@@ -535,7 +584,7 @@ S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) c
c = istr.peek();
if(isalpha(c))
{
- int cnt = deserialize_boolean(istr,data,NOTATION_TRUE_SERIAL,true);
+ auto cnt = deserialize_boolean(istr,data,NOTATION_TRUE_SERIAL,true);
if(PARSE_FAILURE == cnt) parse_count = cnt;
else account(cnt);
}
@@ -611,7 +660,7 @@ S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) c
c = get(istr); // pop the 'l'
c = get(istr); // pop the delimiter
std::string str;
- int cnt = deserialize_string_delim(istr, str, c);
+ auto cnt = deserialize_string_delim(istr, str, c);
if(PARSE_FAILURE == cnt)
{
parse_count = PARSE_FAILURE;
@@ -634,7 +683,7 @@ S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) c
c = get(istr); // pop the 'd'
c = get(istr); // pop the delimiter
std::string str;
- int cnt = deserialize_string_delim(istr, str, c);
+ auto cnt = deserialize_string_delim(istr, str, c);
if(PARSE_FAILURE == cnt)
{
parse_count = PARSE_FAILURE;
@@ -666,7 +715,7 @@ S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) c
default:
parse_count = PARSE_FAILURE;
- LL_INFOS() << "Unrecognized character while parsing: int(" << (int)c
+ LL_INFOS() << "Unrecognized character while parsing: int(" << int(c)
<< ")" << LL_ENDL;
break;
}
@@ -697,7 +746,7 @@ S32 LLSDNotationParser::parseMap(std::istream& istr, LLSD& map, S32 max_depth) c
{
putback(istr, c);
found_name = true;
- int count = deserialize_string(istr, name, mMaxBytesLeft);
+ auto count = deserialize_string(istr, name, mMaxBytesLeft);
if(PARSE_FAILURE == count) return PARSE_FAILURE;
account(count);
}
@@ -779,7 +828,7 @@ S32 LLSDNotationParser::parseArray(std::istream& istr, LLSD& array, S32 max_dept
bool LLSDNotationParser::parseString(std::istream& istr, LLSD& data) const
{
std::string value;
- int count = deserialize_string(istr, value, mMaxBytesLeft);
+ auto count = deserialize_string(istr, value, mMaxBytesLeft);
if(PARSE_FAILURE == count) return false;
account(count);
data = value;
@@ -806,13 +855,13 @@ bool LLSDNotationParser::parseBinary(std::istream& istr, LLSD& data) const
{
// We probably have a valid raw binary stream. determine
// the size, and read it.
- S32 len = strtol(buf + 2, NULL, 0);
+ auto len = strtol(buf + 2, NULL, 0);
if(mCheckLimits && (len > mMaxBytesLeft)) return false;
std::vector<U8> value;
if(len)
{
value.resize(len);
- account((int)fullread(istr, (char *)&value[0], len));
+ account(fullread(istr, (char *)&value[0], len));
}
c = get(istr); // strip off the trailing double-quote
data = value;
@@ -1009,7 +1058,7 @@ S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) con
case '"':
{
std::string value;
- int cnt = deserialize_string_delim(istr, value, c);
+ auto cnt = deserialize_string_delim(istr, value, c);
if(PARSE_FAILURE == cnt)
{
parse_count = PARSE_FAILURE;
@@ -1096,7 +1145,7 @@ S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) con
if(size > 0)
{
value.resize(size);
- account((int)fullread(istr, (char*)&value[0], size));
+ account(fullread(istr, (char*)&value[0], size));
}
data = value;
}
@@ -1110,7 +1159,7 @@ S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) con
default:
parse_count = PARSE_FAILURE;
- LL_INFOS() << "Unrecognized character while parsing: int(" << (int)c
+ LL_INFOS() << "Unrecognized character while parsing: int(" << int(c)
<< ")" << LL_ENDL;
break;
}
@@ -1144,7 +1193,7 @@ S32 LLSDBinaryParser::parseMap(std::istream& istr, LLSD& map, S32 max_depth) con
case '\'':
case '"':
{
- int cnt = deserialize_string_delim(istr, name, c);
+ auto cnt = deserialize_string_delim(istr, name, c);
if(PARSE_FAILURE == cnt) return PARSE_FAILURE;
account(cnt);
break;
@@ -1228,7 +1277,7 @@ bool LLSDBinaryParser::parseString(
if(size)
{
buf.resize(size);
- account((int)fullread(istr, &buf[0], size));
+ account(fullread(istr, &buf[0], size));
value.assign(buf.begin(), buf.end());
}
return true;
@@ -1432,7 +1481,7 @@ S32 LLSDNotationFormatter::format_impl(const LLSD& data, std::ostream& ostr,
ostr << std::uppercase;
auto oldfill(ostr.fill('0'));
auto oldwidth(ostr.width());
- for (int i = 0; i < buffer.size(); i++)
+ for (size_t i = 0; i < buffer.size(); i++)
{
// have to restate setw() before every conversion
ostr << std::setw(2) << (int) buffer[i];
@@ -1595,7 +1644,7 @@ void LLSDBinaryFormatter::formatString(
/**
* local functions
*/
-int deserialize_string(std::istream& istr, std::string& value, S32 max_bytes)
+llssize deserialize_string(std::istream& istr, std::string& value, llssize max_bytes)
{
int c = istr.get();
if(istr.fail())
@@ -1605,7 +1654,7 @@ int deserialize_string(std::istream& istr, std::string& value, S32 max_bytes)
return LLSDParser::PARSE_FAILURE;
}
- int rv = LLSDParser::PARSE_FAILURE;
+ llssize rv = LLSDParser::PARSE_FAILURE;
switch(c)
{
case '\'':
@@ -1625,7 +1674,7 @@ int deserialize_string(std::istream& istr, std::string& value, S32 max_bytes)
return rv + 1; // account for the character grabbed at the top.
}
-int deserialize_string_delim(
+llssize deserialize_string_delim(
std::istream& istr,
std::string& value,
char delim)
@@ -1635,7 +1684,7 @@ int deserialize_string_delim(
bool found_hex = false;
bool found_digit = false;
U8 byte = 0;
- int count = 0;
+ llssize count = 0;
while (true)
{
@@ -1650,7 +1699,7 @@ int deserialize_string_delim(
}
char next_char = (char)next_byte; // Now that we know it's not EOF
-
+
if(found_escape)
{
// next character(s) is a special sequence.
@@ -1728,16 +1777,16 @@ int deserialize_string_delim(
return count;
}
-int deserialize_string_raw(
+llssize deserialize_string_raw(
std::istream& istr,
std::string& value,
- S32 max_bytes)
+ llssize max_bytes)
{
- int count = 0;
+ llssize count = 0;
const S32 BUF_LEN = 20;
char buf[BUF_LEN]; /* Flawfinder: ignore */
istr.get(buf, BUF_LEN - 1, ')');
- count += (int)istr.gcount();
+ count += istr.gcount();
int c = istr.get();
c = istr.get();
count += 2;
@@ -1746,13 +1795,13 @@ int deserialize_string_raw(
// We probably have a valid raw string. determine
// the size, and read it.
// *FIX: This is memory inefficient.
- S32 len = strtol(buf + 1, NULL, 0);
+ auto len = strtol(buf + 1, NULL, 0);
if((max_bytes>0)&&(len>max_bytes)) return LLSDParser::PARSE_FAILURE;
std::vector<char> buf;
if(len)
{
buf.resize(len);
- count += (int)fullread(istr, (char *)&buf[0], len);
+ count += fullread(istr, (char *)&buf[0], len);
value.assign(buf.begin(), buf.end());
}
c = istr.get();
@@ -2041,7 +2090,7 @@ void serialize_string(const std::string& value, std::ostream& str)
}
}
-int deserialize_boolean(
+llssize deserialize_boolean(
std::istream& istr,
LLSD& data,
const std::string& compare,
@@ -2058,7 +2107,7 @@ int deserialize_boolean(
// * set data to LLSD::null
// * return LLSDParser::PARSE_FAILURE (-1)
//
- int bytes_read = 0;
+ llssize bytes_read = 0;
std::string::size_type ii = 0;
char c = istr.peek();
while((++ii < compare.size())
@@ -2113,7 +2162,7 @@ std::string zip_llsd(LLSD& data)
U8 out[CHUNK];
- strm.avail_in = source.size();
+ strm.avail_in = narrow(source.size());
strm.next_in = (U8*) source.data();
U8* output = NULL;
@@ -2287,7 +2336,7 @@ LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, const U8* in, S32
//This unzip function will only work with a gzip header and trailer - while the contents
//of the actual compressed data is the same for either format (gzip vs zlib ), the headers
//and trailers are different for the formats.
-U8* unzip_llsdNavMesh( bool& valid, unsigned int& outsize, std::istream& is, S32 size )
+U8* unzip_llsdNavMesh( bool& valid, size_t& outsize, std::istream& is, S32 size )
{
if (size == 0)
{