diff options
author | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 21:25:21 +0200 |
---|---|---|
committer | Andrey Lihatskiy <alihatskiy@productengine.com> | 2024-05-22 22:40:26 +0300 |
commit | e2e37cced861b98de8c1a7c9c0d3a50d2d90e433 (patch) | |
tree | 1bb897489ce524986f6196201c10ac0d8861aa5f /indra/llcommon/llsdserialize.cpp | |
parent | 069ea06848f766466f1a281144c82a0f2bd79f3a (diff) |
Fix line endlings
Diffstat (limited to 'indra/llcommon/llsdserialize.cpp')
-rw-r--r-- | indra/llcommon/llsdserialize.cpp | 4940 |
1 files changed, 2470 insertions, 2470 deletions
diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp index 612f71e8bc..92d9392477 100644 --- a/indra/llcommon/llsdserialize.cpp +++ b/indra/llcommon/llsdserialize.cpp @@ -1,2470 +1,2470 @@ -/**
- * @file llsdserialize.cpp
- * @author Phoenix
- * @date 2006-03-05
- * @brief Implementation of LLSD parsers and formatters
- *
- * $LicenseInfo:firstyear=2006&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-#include "llsdserialize.h"
-#include "llpointer.h"
-#include "llstreamtools.h" // for fullread
-
-#include <iostream>
-#include "apr_base64.h"
-
-#include <boost/iostreams/device/array.hpp>
-#include <boost/iostreams/stream.hpp>
-
-#ifdef LL_USESYSTEMLIBS
-# include <zlib.h>
-#else
-# include "zlib-ng/zlib.h" // for davep's dirty little zip functions
-#endif
-
-#if !LL_WINDOWS
-#include <netinet/in.h> // htonl & ntohl
-#endif
-
-#include "lldate.h"
-#include "llmemorystream.h"
-#include "llsd.h"
-#include "llstring.h"
-#include "lluri.h"
-
-// File constants
-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");
-const std::string LLSD_XML_HEADER("LLSD/XML");
-const std::string LLSD_NOTATION_HEADER("llsd/notation");
-
-//used to deflate a gzipped asset (currently used for navmeshes)
-#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
- */
-
-// static
-void LLSDSerialize::serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize type,
- LLSDFormatter::EFormatterOptions options)
-{
- LLPointer<LLSDFormatter> f = NULL;
-
- switch (type)
- {
- case LLSD_BINARY:
- str << "<? " << LLSD_BINARY_HEADER << " ?>\n";
- f = new LLSDBinaryFormatter;
- break;
-
- case LLSD_XML:
- str << "<? " << LLSD_XML_HEADER << " ?>\n";
- f = new LLSDXMLFormatter;
- break;
-
- case LLSD_NOTATION:
- str << "<? " << LLSD_NOTATION_HEADER << " ?>\n";
- f = new LLSDNotationFormatter;
- break;
-
- default:
- LL_WARNS() << "serialize request for unknown ELLSD_Serialize" << LL_ENDL;
- }
-
- if (f.notNull())
- {
- f->format(sd, str, options);
- }
-}
-
-// static
-bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str, llssize max_bytes)
-{
- char hdr_buf[MAX_HDR_LEN + 1] = ""; /* Flawfinder: ignore */
- bool fail_if_not_legacy = false;
-
- /*
- * 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.
- */
- llssize max_hdr_read = MAX_HDR_LEN;
- if (max_bytes != LLSDSerialize::SIZE_UNLIMITED)
- {
- max_hdr_read = llmin(max_bytes + 1, max_hdr_read);
- }
- str.get(hdr_buf, max_hdr_read, '\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();
- fail_if_not_legacy = true;
- }
-
- 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)
- {
- LL_WARNS() << "deserialize LLSD parse failure" << LL_ENDL;
- return false;
- }
-
- /*
- * Remove the newline chars
- */
- std::string::size_type lastchar = header.find_last_not_of("\r\n");
- if (lastchar != std::string::npos)
- {
- // 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);
- }
-
- // 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)
- {
- header = header.substr(start, end - start);
- ws(str);
- }
- }
- /*
- * Create the parser as appropriate
- */
- if (0 == LLStringUtil::compareInsensitive(header, LLSD_BINARY_HEADER))
- {
- return (parse_using<LLSDBinaryParser>(str, sd, max_bytes-inbuf) > 0);
- }
- else if (0 == LLStringUtil::compareInsensitive(header, LLSD_XML_HEADER))
- {
- return (parse_using<LLSDXMLParser>(str, sd, max_bytes-inbuf) > 0);
- }
- else if (0 == LLStringUtil::compareInsensitive(header, LLSD_NOTATION_HEADER))
- {
- return (parse_using<LLSDNotationParser>(str, sd, max_bytes-inbuf) > 0);
- }
- else // no header we recognize
- {
- 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
- }
-}
-
-/**
- * Endian handlers
- */
-#if LL_BIG_ENDIAN
-U64 ll_htonll(U64 hostlonglong) { return hostlonglong; }
-U64 ll_ntohll(U64 netlonglong) { return netlonglong; }
-F64 ll_htond(F64 hostlonglong) { return hostlonglong; }
-F64 ll_ntohd(F64 netlonglong) { return netlonglong; }
-#else
-// I read some comments one a indicating that doing an integer add
-// here would be faster than a bitwise or. For now, the or has
-// programmer clarity, since the intended outcome matches the
-// operation.
-U64 ll_htonll(U64 hostlonglong)
-{
- return ((U64)(htonl((U32)((hostlonglong >> 32) & 0xFFFFFFFF))) |
- ((U64)(htonl((U32)(hostlonglong & 0xFFFFFFFF))) << 32));
-}
-U64 ll_ntohll(U64 netlonglong)
-{
- return ((U64)(ntohl((U32)((netlonglong >> 32) & 0xFFFFFFFF))) |
- ((U64)(ntohl((U32)(netlonglong & 0xFFFFFFFF))) << 32));
-}
-union LLEndianSwapper
-{
- F64 d;
- U64 i;
-};
-F64 ll_htond(F64 hostdouble)
-{
- LLEndianSwapper tmp;
- tmp.d = hostdouble;
- tmp.i = ll_htonll(tmp.i);
- return tmp.d;
-}
-F64 ll_ntohd(F64 netdouble)
-{
- LLEndianSwapper tmp;
- tmp.d = netdouble;
- tmp.i = ll_ntohll(tmp.i);
- return tmp.d;
-}
-#endif
-
-/**
- * Local functions.
- */
-/**
- * @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.
- */
-llssize deserialize_string(std::istream& istr, std::string& value, llssize 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.
- */
-llssize 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.
- */
-llssize deserialize_string_raw(
- std::istream& istr,
- std::string& value,
- llssize 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.
- */
-llssize 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.
- */
-static const std::string NOTATION_TRUE_SERIAL("true");
-static const std::string NOTATION_FALSE_SERIAL("false");
-
-static const char BINARY_TRUE_SERIAL = '1';
-static const char BINARY_FALSE_SERIAL = '0';
-
-
-/**
- * LLSDParser
- */
-LLSDParser::LLSDParser()
- : mCheckLimits(true), mMaxBytesLeft(0), mParseLines(false)
-{
-}
-
-// virtual
-LLSDParser::~LLSDParser()
-{ }
-
-S32 LLSDParser::parse(std::istream& istr, LLSD& data, llssize max_bytes, S32 max_depth)
-{
- mCheckLimits = LLSDSerialize::SIZE_UNLIMITED != max_bytes;
- mMaxBytesLeft = max_bytes;
- return doParse(istr, data, max_depth);
-}
-
-
-// Parse using routine to get() lines, faster than parse()
-S32 LLSDParser::parseLines(std::istream& istr, LLSD& data)
-{
- mCheckLimits = false;
- mParseLines = true;
- 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(llssize bytes) const
-{
- if(mCheckLimits) mMaxBytesLeft -= bytes;
-}
-
-
-/**
- * LLSDNotationParser
- */
-LLSDNotationParser::LLSDNotationParser()
-{
-}
-
-// virtual
-LLSDNotationParser::~LLSDNotationParser()
-{ }
-
-// virtual
-S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) const
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD
- // map: { string:object, string:object }
- // array: [ object, object, object ]
- // undef: !
- // boolean: true | false | 1 | 0 | T | F | t | f | TRUE | FALSE
- // integer: i####
- // real: r####
- // uuid: u####
- // string: "g'day" | 'have a "nice" day' | s(size)"raw data"
- // uri: l"escaped"
- // date: d"YYYY-MM-DDTHH:MM:SS.FFZ"
- // binary: b##"ff3120ab1" | b(size)"raw data"
- char c;
- c = istr.peek();
- if (max_depth == 0)
- {
- return PARSE_FAILURE;
- }
- while(isspace(c))
- {
- // pop the whitespace.
- c = get(istr);
- c = istr.peek();
- continue;
- }
- if(!istr.good())
- {
- return 0;
- }
- S32 parse_count = 1;
- switch(c)
- {
- case '{':
- {
- S32 child_count = parseMap(istr, data, max_depth - 1);
- if((child_count == PARSE_FAILURE) || data.isUndefined())
- {
- parse_count = PARSE_FAILURE;
- }
- else
- {
- parse_count += child_count;
- }
- if(istr.fail())
- {
- LL_INFOS() << "STREAM FAILURE reading map." << LL_ENDL;
- parse_count = PARSE_FAILURE;
- }
- break;
- }
-
- case '[':
- {
- S32 child_count = parseArray(istr, data, max_depth - 1);
- if((child_count == PARSE_FAILURE) || data.isUndefined())
- {
- parse_count = PARSE_FAILURE;
- }
- else
- {
- parse_count += child_count;
- }
- if(istr.fail())
- {
- LL_INFOS() << "STREAM FAILURE reading array." << LL_ENDL;
- parse_count = PARSE_FAILURE;
- }
- break;
- }
-
- case '!':
- c = get(istr);
- data.clear();
- break;
-
- case '0':
- c = get(istr);
- data = false;
- break;
-
- case 'F':
- case 'f':
- ignore(istr);
- c = istr.peek();
- if(isalpha(c))
- {
- auto 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())
- {
- LL_INFOS() << "STREAM FAILURE reading boolean." << LL_ENDL;
- parse_count = PARSE_FAILURE;
- }
- break;
-
- case '1':
- c = get(istr);
- data = true;
- break;
-
- case 'T':
- case 't':
- ignore(istr);
- c = istr.peek();
- if(isalpha(c))
- {
- auto 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())
- {
- LL_INFOS() << "STREAM FAILURE reading boolean." << LL_ENDL;
- parse_count = PARSE_FAILURE;
- }
- break;
-
- case 'i':
- {
- c = get(istr);
- S32 integer = 0;
- istr >> integer;
- data = integer;
- if(istr.fail())
- {
- LL_INFOS() << "STREAM FAILURE reading integer." << LL_ENDL;
- parse_count = PARSE_FAILURE;
- }
- break;
- }
-
- case 'r':
- {
- c = get(istr);
- F64 real = 0.0;
- istr >> real;
- data = real;
- if(istr.fail())
- {
- LL_INFOS() << "STREAM FAILURE reading real." << LL_ENDL;
- parse_count = PARSE_FAILURE;
- }
- break;
- }
-
- case 'u':
- {
- c = get(istr);
- LLUUID id;
- istr >> id;
- data = id;
- if(istr.fail())
- {
- LL_INFOS() << "STREAM FAILURE reading uuid." << LL_ENDL;
- parse_count = PARSE_FAILURE;
- }
- break;
- }
-
- case '\"':
- case '\'':
- case 's':
- if(!parseString(istr, data))
- {
- parse_count = PARSE_FAILURE;
- }
- if(istr.fail())
- {
- LL_INFOS() << "STREAM FAILURE reading string." << LL_ENDL;
- parse_count = PARSE_FAILURE;
- }
- break;
-
- case 'l':
- {
- c = get(istr); // pop the 'l'
- c = get(istr); // pop the delimiter
- std::string str;
- auto cnt = deserialize_string_delim(istr, str, c);
- if(PARSE_FAILURE == cnt)
- {
- parse_count = PARSE_FAILURE;
- }
- else
- {
- data = LLURI(str);
- account(cnt);
- }
- if(istr.fail())
- {
- LL_INFOS() << "STREAM FAILURE reading link." << LL_ENDL;
- parse_count = PARSE_FAILURE;
- }
- break;
- }
-
- case 'd':
- {
- c = get(istr); // pop the 'd'
- c = get(istr); // pop the delimiter
- std::string str;
- auto cnt = deserialize_string_delim(istr, str, c);
- if(PARSE_FAILURE == cnt)
- {
- parse_count = PARSE_FAILURE;
- }
- else
- {
- data = LLDate(str);
- account(cnt);
- }
- if(istr.fail())
- {
- LL_INFOS() << "STREAM FAILURE reading date." << LL_ENDL;
- parse_count = PARSE_FAILURE;
- }
- break;
- }
-
- case 'b':
- if(!parseBinary(istr, data))
- {
- parse_count = PARSE_FAILURE;
- }
- if(istr.fail())
- {
- LL_INFOS() << "STREAM FAILURE reading data." << LL_ENDL;
- parse_count = PARSE_FAILURE;
- }
- break;
-
- default:
- parse_count = PARSE_FAILURE;
- LL_INFOS() << "Unrecognized character while parsing: int(" << int(c)
- << ")" << LL_ENDL;
- break;
- }
- if(PARSE_FAILURE == parse_count)
- {
- data.clear();
- }
- return parse_count;
-}
-
-S32 LLSDNotationParser::parseMap(std::istream& istr, LLSD& map, S32 max_depth) const
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD
- // map: { string:object, string:object }
- map = LLSD::emptyMap();
- S32 parse_count = 0;
- char c = get(istr);
- if(c == '{')
- {
- // eat commas, white
- bool found_name = false;
- std::string name;
- c = get(istr);
- while(c != '}' && istr.good())
- {
- if(!found_name)
- {
- if((c == '\"') || (c == '\'') || (c == 's'))
- {
- putback(istr, c);
- found_name = true;
- auto count = deserialize_string(istr, name, mMaxBytesLeft);
- if(PARSE_FAILURE == count) return PARSE_FAILURE;
- account(count);
- }
- c = get(istr);
- }
- else
- {
- if(isspace(c) || (c == ':'))
- {
- c = get(istr);
- continue;
- }
- putback(istr, c);
- LLSD child;
- S32 count = doParse(istr, child, max_depth);
- 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
- {
- return PARSE_FAILURE;
- }
- found_name = false;
- c = get(istr);
- }
- }
- if(c != '}')
- {
- map.clear();
- return PARSE_FAILURE;
- }
- }
- return parse_count;
-}
-
-S32 LLSDNotationParser::parseArray(std::istream& istr, LLSD& array, S32 max_depth) const
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD
- // array: [ object, object, object ]
- array = LLSD::emptyArray();
- S32 parse_count = 0;
- char c = get(istr);
- if(c == '[')
- {
- // eat commas, white
- c = get(istr);
- while((c != ']') && istr.good())
- {
- LLSD child;
- if(isspace(c) || (c == ','))
- {
- c = get(istr);
- continue;
- }
- putback(istr, c);
- S32 count = doParse(istr, child, max_depth);
- if(PARSE_FAILURE == count)
- {
- return PARSE_FAILURE;
- }
- else
- {
- parse_count += count;
- array.append(child);
- }
- c = get(istr);
- }
- if(c != ']')
- {
- return PARSE_FAILURE;
- }
- }
- return parse_count;
-}
-
-bool LLSDNotationParser::parseString(std::istream& istr, LLSD& data) const
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD
- std::string value;
- auto count = deserialize_string(istr, value, mMaxBytesLeft);
- if(PARSE_FAILURE == count) return false;
- account(count);
- data = value;
- return true;
-}
-
-bool LLSDNotationParser::parseBinary(std::istream& istr, LLSD& data) const
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD
- // binary: b##"ff3120ab1"
- // or: b(len)"..."
-
- // I want to manually control those values here to make sure the
- // parser doesn't break when someone changes a constant somewhere
- // else.
- const U32 BINARY_BUFFER_SIZE = 256;
- const U32 STREAM_GET_COUNT = 255;
-
- // need to read the base out.
- char buf[BINARY_BUFFER_SIZE]; /* Flawfinder: ignore */
- 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.
- auto len = strtol(buf + 2, NULL, 0);
- if(mCheckLimits && (len > mMaxBytesLeft)) return false;
- std::vector<U8> value;
- if(len)
- {
- value.resize(len);
- account(fullread(istr, (char *)&value[0], len));
- }
- c = get(istr); // strip off the trailing double-quote
- data = value;
- }
- 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;
- 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<U8> value;
- if(len)
- {
- value.resize(len);
- len = apr_base64_decode_binary(&value[0], encoded.c_str());
- value.resize(len);
- }
- data = value;
- }
- 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
- // done parsing. If it's not, put the data back, and read the
- // stream until the next double quote.
- char* read; /*Flawfinder: ignore*/
- U8 byte;
- U8 byte_buffer[BINARY_BUFFER_SIZE];
- U8* write;
- std::vector<U8> value;
- c = get(istr);
- while(c != '"')
- {
- putback(istr, c);
- read = buf;
- write = byte_buffer;
- get(istr, buf, STREAM_GET_COUNT, '"');
- c = get(istr);
- while(*read != '\0') /*Flawfinder: ignore*/
- {
- byte = hex_as_nybble(*read++);
- byte = byte << 4;
- byte |= hex_as_nybble(*read++);
- *write++ = byte;
- }
- // copy the data out of the byte buffer
- value.insert(value.end(), byte_buffer, write);
- }
- data = value;
- }
- else
- {
- return false;
- }
- return true;
-}
-
-
-/**
- * LLSDBinaryParser
- */
-LLSDBinaryParser::LLSDBinaryParser()
-{
-}
-
-// virtual
-LLSDBinaryParser::~LLSDBinaryParser()
-{
-}
-
-// virtual
-S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) const
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD
-/**
- * Undefined: '!'<br>
- * Boolean: '1' for true '0' for false<br>
- * Integer: 'i' + 4 bytes network byte order<br>
- * Real: 'r' + 8 bytes IEEE double<br>
- * UUID: 'u' + 16 byte unsigned integer<br>
- * String: 's' + 4 byte integer size + string<br>
- * strings also secretly support the notation format
- * Date: 'd' + 8 byte IEEE double for seconds since epoch<br>
- * URI: 'l' + 4 byte integer size + string uri<br>
- * Binary: 'b' + 4 byte integer size + binary data<br>
- * Array: '[' + 4 byte integer size + all values + ']'<br>
- * Map: '{' + 4 byte integer size every(key + value) + '}'<br>
- * map keys are serialized as s + 4 byte integer size + string or in the
- * notation format.
- */
- char c;
- c = get(istr);
- if(!istr.good())
- {
- return 0;
- }
- if (max_depth == 0)
- {
- return PARSE_FAILURE;
- }
- S32 parse_count = 1;
- switch(c)
- {
- case '{':
- {
- S32 child_count = parseMap(istr, data, max_depth - 1);
- if((child_count == PARSE_FAILURE) || data.isUndefined())
- {
- parse_count = PARSE_FAILURE;
- }
- else
- {
- parse_count += child_count;
- }
- if(istr.fail())
- {
- LL_INFOS() << "STREAM FAILURE reading binary map." << LL_ENDL;
- parse_count = PARSE_FAILURE;
- }
- break;
- }
-
- case '[':
- {
- S32 child_count = parseArray(istr, data, max_depth - 1);
- if((child_count == PARSE_FAILURE) || data.isUndefined())
- {
- parse_count = PARSE_FAILURE;
- }
- else
- {
- parse_count += child_count;
- }
- if(istr.fail())
- {
- LL_INFOS() << "STREAM FAILURE reading binary array." << LL_ENDL;
- parse_count = PARSE_FAILURE;
- }
- break;
- }
-
- case '!':
- data.clear();
- break;
-
- case '0':
- data = false;
- break;
-
- case '1':
- data = true;
- break;
-
- case 'i':
- {
- U32 value_nbo = 0;
- read(istr, (char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
- data = (S32)ntohl(value_nbo);
- if(istr.fail())
- {
- LL_INFOS() << "STREAM FAILURE reading binary integer." << LL_ENDL;
- }
- break;
- }
-
- case 'r':
- {
- F64 real_nbo = 0.0;
- read(istr, (char*)&real_nbo, sizeof(F64)); /*Flawfinder: ignore*/
- data = ll_ntohd(real_nbo);
- if(istr.fail())
- {
- LL_INFOS() << "STREAM FAILURE reading binary real." << LL_ENDL;
- }
- break;
- }
-
- case 'u':
- {
- LLUUID id;
- read(istr, (char*)(&id.mData), UUID_BYTES); /*Flawfinder: ignore*/
- data = id;
- if(istr.fail())
- {
- LL_INFOS() << "STREAM FAILURE reading binary uuid." << LL_ENDL;
- }
- break;
- }
-
- case '\'':
- case '"':
- {
- std::string value;
- auto cnt = deserialize_string_delim(istr, value, c);
- if(PARSE_FAILURE == cnt)
- {
- parse_count = PARSE_FAILURE;
- }
- else
- {
- data = value;
- account(cnt);
- }
- if(istr.fail())
- {
- LL_INFOS() << "STREAM FAILURE reading binary (notation-style) string."
- << LL_ENDL;
- parse_count = PARSE_FAILURE;
- }
- break;
- }
-
- case 's':
- {
- std::string value;
- if(parseString(istr, value))
- {
- data = value;
- }
- else
- {
- parse_count = PARSE_FAILURE;
- }
- if(istr.fail())
- {
- LL_INFOS() << "STREAM FAILURE reading binary string." << LL_ENDL;
- parse_count = PARSE_FAILURE;
- }
- break;
- }
-
- case 'l':
- {
- std::string value;
- if(parseString(istr, value))
- {
- data = LLURI(value);
- }
- else
- {
- parse_count = PARSE_FAILURE;
- }
- if(istr.fail())
- {
- LL_INFOS() << "STREAM FAILURE reading binary link." << LL_ENDL;
- parse_count = PARSE_FAILURE;
- }
- break;
- }
-
- case 'd':
- {
- F64 real = 0.0;
- read(istr, (char*)&real, sizeof(F64)); /*Flawfinder: ignore*/
- data = LLDate(real);
- if(istr.fail())
- {
- LL_INFOS() << "STREAM FAILURE reading binary date." << LL_ENDL;
- parse_count = PARSE_FAILURE;
- }
- break;
- }
-
- case 'b':
- {
- // We probably have a valid raw binary stream. determine
- // the size, and read it.
- U32 size_nbo = 0;
- read(istr, (char*)&size_nbo, sizeof(U32)); /*Flawfinder: ignore*/
- S32 size = (S32)ntohl(size_nbo);
- if(mCheckLimits && (size > mMaxBytesLeft))
- {
- parse_count = PARSE_FAILURE;
- }
- else
- {
- std::vector<U8> value;
- if(size > 0)
- {
- value.resize(size);
- account(fullread(istr, (char*)&value[0], size));
- }
- data = value;
- }
- if(istr.fail())
- {
- LL_INFOS() << "STREAM FAILURE reading binary." << LL_ENDL;
- parse_count = PARSE_FAILURE;
- }
- break;
- }
-
- default:
- parse_count = PARSE_FAILURE;
- LL_INFOS() << "Unrecognized character while parsing: int(" << int(c)
- << ")" << LL_ENDL;
- break;
- }
- if(PARSE_FAILURE == parse_count)
- {
- data.clear();
- }
- return parse_count;
-}
-
-S32 LLSDBinaryParser::parseMap(std::istream& istr, LLSD& map, S32 max_depth) const
-{
- map = LLSD::emptyMap();
- U32 value_nbo = 0;
- read(istr, (char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
- S32 size = (S32)ntohl(value_nbo);
- S32 parse_count = 0;
- S32 count = 0;
- char c = get(istr);
- while(c != '}' && (count < size) && istr.good())
- {
- std::string name;
- switch(c)
- {
- case 'k':
- if(!parseString(istr, name))
- {
- return PARSE_FAILURE;
- }
- break;
- case '\'':
- case '"':
- {
- auto cnt = deserialize_string_delim(istr, name, c);
- if(PARSE_FAILURE == cnt) return PARSE_FAILURE;
- account(cnt);
- break;
- }
- }
- LLSD child;
- S32 child_count = doParse(istr, child, max_depth);
- 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 = 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;
-}
-
-S32 LLSDBinaryParser::parseArray(std::istream& istr, LLSD& array, S32 max_depth) const
-{
- array = LLSD::emptyArray();
- U32 value_nbo = 0;
- 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
- // array...
-
- S32 parse_count = 0;
- S32 count = 0;
- char c = istr.peek();
- while((c != ']') && (count < size) && istr.good())
- {
- LLSD child;
- S32 child_count = doParse(istr, child, max_depth);
- if(PARSE_FAILURE == child_count)
- {
- return PARSE_FAILURE;
- }
- if(child_count)
- {
- parse_count += child_count;
- array.append(child);
- }
- ++count;
- c = istr.peek();
- }
- 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;
-}
-
-bool LLSDBinaryParser::parseString(
- std::istream& istr,
- std::string& value) const
-{
- // *FIX: This is memory inefficient.
- U32 value_nbo = 0;
- read(istr, (char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
- S32 size = (S32)ntohl(value_nbo);
- if(mCheckLimits && (size > mMaxBytesLeft)) return false;
- if(size < 0) return false;
- std::vector<char> buf;
- if(size)
- {
- buf.resize(size);
- account(fullread(istr, &buf[0], size));
- value.assign(buf.begin(), buf.end());
- }
- return true;
-}
-
-
-/**
- * LLSDFormatter
- */
-LLSDFormatter::LLSDFormatter(bool boolAlpha, const std::string& realFmt, EFormatterOptions options):
- mOptions(options)
-{
- boolalpha(boolAlpha);
- realFormat(realFmt);
-}
-
-// virtual
-LLSDFormatter::~LLSDFormatter()
-{ }
-
-void LLSDFormatter::boolalpha(bool alpha)
-{
- mBoolAlpha = alpha;
-}
-
-void LLSDFormatter::realFormat(const std::string& format)
-{
- mRealFormat = format;
-}
-
-S32 LLSDFormatter::format(const LLSD& data, std::ostream& ostr) const
-{
- // pass options captured by constructor
- return format(data, ostr, mOptions);
-}
-
-S32 LLSDFormatter::format(const LLSD& data, std::ostream& ostr, EFormatterOptions options) const
-{
- return format_impl(data, ostr, options, 0);
-}
-
-void LLSDFormatter::formatReal(LLSD::Real real, std::ostream& ostr) const
-{
- std::string buffer = llformat(mRealFormat.c_str(), real);
- ostr << buffer;
-}
-
-/**
- * LLSDNotationFormatter
- */
-LLSDNotationFormatter::LLSDNotationFormatter(bool boolAlpha, const std::string& realFormat,
- EFormatterOptions options):
- LLSDFormatter(boolAlpha, realFormat, options)
-{
-}
-
-// virtual
-LLSDNotationFormatter::~LLSDNotationFormatter()
-{ }
-
-// static
-std::string LLSDNotationFormatter::escapeString(const std::string& in)
-{
- std::ostringstream ostr;
- serialize_string(in, ostr);
- return ostr.str();
-}
-
-S32 LLSDNotationFormatter::format_impl(const LLSD& data, std::ostream& ostr,
- EFormatterOptions options, U32 level) const
-{
- S32 format_count = 1;
- std::string pre;
- std::string post;
-
- if (options & LLSDFormatter::OPTIONS_PRETTY)
- {
- for (U32 i = 0; i < level; i++)
- {
- pre += " ";
- }
- post = "\n";
- }
-
- switch(data.type())
- {
- case LLSD::TypeMap:
- {
- if (0 != level) ostr << post << pre;
- ostr << "{";
- std::string inner_pre;
- if (options & LLSDFormatter::OPTIONS_PRETTY)
- {
- inner_pre = pre + " ";
- }
-
- bool need_comma = false;
- LLSD::map_const_iterator iter = data.beginMap();
- LLSD::map_const_iterator end = data.endMap();
- for(; iter != end; ++iter)
- {
- if(need_comma) ostr << ",";
- need_comma = true;
- ostr << post << inner_pre << '\'';
- serialize_string((*iter).first, ostr);
- ostr << "':";
- format_count += format_impl((*iter).second, ostr, options, level + 2);
- }
- ostr << post << pre << "}";
- break;
- }
-
- case LLSD::TypeArray:
- {
- ostr << post << pre << "[";
- bool need_comma = false;
- LLSD::array_const_iterator iter = data.beginArray();
- LLSD::array_const_iterator end = data.endArray();
- for(; iter != end; ++iter)
- {
- if(need_comma) ostr << ",";
- need_comma = true;
- format_count += format_impl(*iter, ostr, options, level + 1);
- }
- ostr << "]";
- break;
- }
-
- case LLSD::TypeUndefined:
- ostr << "!";
- break;
-
- case LLSD::TypeBoolean:
- if(mBoolAlpha ||
-#if( LL_WINDOWS || __GNUC__ > 2)
- (ostr.flags() & std::ios::boolalpha)
-#else
- (ostr.flags() & 0x0100)
-#endif
- )
- {
- ostr << (data.asBoolean()
- ? NOTATION_TRUE_SERIAL : NOTATION_FALSE_SERIAL);
- }
- else
- {
- ostr << (data.asBoolean() ? 1 : 0);
- }
- break;
-
- case LLSD::TypeInteger:
- ostr << "i" << data.asInteger();
- break;
-
- case LLSD::TypeReal:
- ostr << "r";
- if(mRealFormat.empty())
- {
- ostr << data.asReal();
- }
- else
- {
- formatReal(data.asReal(), ostr);
- }
- break;
-
- case LLSD::TypeUUID:
- ostr << "u" << data.asUUID();
- break;
-
- case LLSD::TypeString:
- ostr << '\'';
- serialize_string(data.asStringRef(), ostr);
- ostr << '\'';
- break;
-
- case LLSD::TypeDate:
- ostr << "d\"" << data.asDate() << "\"";
- break;
-
- case LLSD::TypeURI:
- ostr << "l\"";
- serialize_string(data.asString(), ostr);
- ostr << "\"";
- break;
-
- case LLSD::TypeBinary:
- {
- // *FIX: memory inefficient.
- const std::vector<U8>& buffer = data.asBinary();
- if (options & LLSDFormatter::OPTIONS_PRETTY_BINARY)
- {
- ostr << "b16\"";
- if (! buffer.empty())
- {
- std::ios_base::fmtflags old_flags = ostr.flags();
- ostr.setf( std::ios::hex, std::ios::basefield );
- // It shouldn't strictly matter whether the emitted hex digits
- // are uppercase; LLSDNotationParser handles either; but as of
- // 2020-05-13, Python's llbase.llsd requires uppercase hex.
- ostr << std::uppercase;
- auto oldfill(ostr.fill('0'));
- auto oldwidth(ostr.width());
- for (size_t i = 0; i < buffer.size(); i++)
- {
- // have to restate setw() before every conversion
- ostr << std::setw(2) << (int) buffer[i];
- }
- ostr.width(oldwidth);
- ostr.fill(oldfill);
- ostr.flags(old_flags);
- }
- }
- else // ! OPTIONS_PRETTY_BINARY
- {
- ostr << "b(" << buffer.size() << ")\"";
- if (! buffer.empty())
- {
- ostr.write((const char*)&buffer[0], buffer.size());
- }
- }
- ostr << "\"";
- break;
- }
-
- default:
- // *NOTE: This should never happen.
- ostr << "!";
- break;
- }
- return format_count;
-}
-
-/**
- * LLSDBinaryFormatter
- */
-LLSDBinaryFormatter::LLSDBinaryFormatter(bool boolAlpha, const std::string& realFormat,
- EFormatterOptions options):
- LLSDFormatter(boolAlpha, realFormat, options)
-{
-}
-
-// virtual
-LLSDBinaryFormatter::~LLSDBinaryFormatter()
-{ }
-
-// virtual
-S32 LLSDBinaryFormatter::format_impl(const LLSD& data, std::ostream& ostr,
- EFormatterOptions options, U32 level) const
-{
- S32 format_count = 1;
- switch(data.type())
- {
- case LLSD::TypeMap:
- {
- ostr.put('{');
- U32 size_nbo = htonl(data.size());
- ostr.write((const char*)(&size_nbo), sizeof(U32));
- LLSD::map_const_iterator iter = data.beginMap();
- LLSD::map_const_iterator end = data.endMap();
- for(; iter != end; ++iter)
- {
- ostr.put('k');
- formatString((*iter).first, ostr);
- format_count += format_impl((*iter).second, ostr, options, level+1);
- }
- ostr.put('}');
- break;
- }
-
- case LLSD::TypeArray:
- {
- ostr.put('[');
- U32 size_nbo = htonl(data.size());
- ostr.write((const char*)(&size_nbo), sizeof(U32));
- LLSD::array_const_iterator iter = data.beginArray();
- LLSD::array_const_iterator end = data.endArray();
- for(; iter != end; ++iter)
- {
- format_count += format_impl(*iter, ostr, options, level+1);
- }
- ostr.put(']');
- break;
- }
-
- case LLSD::TypeUndefined:
- ostr.put('!');
- break;
-
- case LLSD::TypeBoolean:
- if(data.asBoolean()) ostr.put(BINARY_TRUE_SERIAL);
- else ostr.put(BINARY_FALSE_SERIAL);
- break;
-
- case LLSD::TypeInteger:
- {
- ostr.put('i');
- U32 value_nbo = htonl(data.asInteger());
- ostr.write((const char*)(&value_nbo), sizeof(U32));
- break;
- }
-
- case LLSD::TypeReal:
- {
- ostr.put('r');
- F64 value_nbo = ll_htond(data.asReal());
- ostr.write((const char*)(&value_nbo), sizeof(F64));
- break;
- }
-
- case LLSD::TypeUUID:
- {
- ostr.put('u');
- LLUUID temp = data.asUUID();
- ostr.write((const char*)(&(temp.mData)), UUID_BYTES);
- break;
- }
-
- case LLSD::TypeString:
- ostr.put('s');
- formatString(data.asStringRef(), ostr);
- break;
-
- case LLSD::TypeDate:
- {
- ostr.put('d');
- F64 value = data.asReal();
- ostr.write((const char*)(&value), sizeof(F64));
- break;
- }
-
- case LLSD::TypeURI:
- ostr.put('l');
- formatString(data.asString(), ostr);
- break;
-
- case LLSD::TypeBinary:
- {
- ostr.put('b');
- const std::vector<U8>& buffer = data.asBinary();
- U32 size_nbo = htonl(buffer.size());
- ostr.write((const char*)(&size_nbo), sizeof(U32));
- if(buffer.size()) ostr.write((const char*)&buffer[0], buffer.size());
- break;
- }
-
- default:
- // *NOTE: This should never happen.
- ostr.put('!');
- break;
- }
- return format_count;
-}
-
-void LLSDBinaryFormatter::formatString(
- const std::string& string,
- std::ostream& ostr) const
-{
- U32 size_nbo = htonl(string.size());
- ostr.write((const char*)(&size_nbo), sizeof(U32));
- ostr.write(string.c_str(), string.size());
-}
-
-/**
- * local functions
- */
-llssize deserialize_string(std::istream& istr, std::string& value, llssize max_bytes)
-{
- int c = istr.get();
- if(istr.fail())
- {
- // No data in stream, bail out but mention the character we
- // grabbed.
- return LLSDParser::PARSE_FAILURE;
- }
-
- llssize rv = LLSDParser::PARSE_FAILURE;
- switch(c)
- {
- case '\'':
- case '"':
- rv = deserialize_string_delim(istr, value, c);
- break;
- case 's':
- // 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;
- }
- if(LLSDParser::PARSE_FAILURE == rv) return rv;
- return rv + 1; // account for the character grabbed at the top.
-}
-
-llssize deserialize_string_delim(
- std::istream& istr,
- std::string& value,
- char delim)
-{
- std::ostringstream write_buffer;
- bool found_escape = false;
- bool found_hex = false;
- bool found_digit = false;
- U8 byte = 0;
- llssize count = 0;
-
- while (true)
- {
- int next_byte = istr.get();
- ++count;
-
- if(istr.fail())
- {
- // If our stream is empty, break out
- value = write_buffer.str();
- return LLSDParser::PARSE_FAILURE;
- }
-
- char next_char = (char)next_byte; // Now that we know it's not EOF
-
- if(found_escape)
- {
- // next character(s) is a special sequence.
- if(found_hex)
- {
- if(found_digit)
- {
- found_digit = false;
- found_hex = false;
- found_escape = false;
- byte = byte << 4;
- byte |= hex_as_nybble(next_char);
- write_buffer << byte;
- byte = 0;
- }
- else
- {
- // next character is the first nybble of
- //
- found_digit = true;
- byte = hex_as_nybble(next_char);
- }
- }
- else if(next_char == 'x')
- {
- found_hex = true;
- }
- else
- {
- switch(next_char)
- {
- case 'a':
- write_buffer << '\a';
- break;
- case 'b':
- write_buffer << '\b';
- break;
- case 'f':
- write_buffer << '\f';
- break;
- case 'n':
- write_buffer << '\n';
- break;
- case 'r':
- write_buffer << '\r';
- break;
- case 't':
- write_buffer << '\t';
- break;
- case 'v':
- write_buffer << '\v';
- break;
- default:
- write_buffer << next_char;
- break;
- }
- found_escape = false;
- }
- }
- else if(next_char == '\\')
- {
- found_escape = true;
- }
- else if(next_char == delim)
- {
- break;
- }
- else
- {
- write_buffer << next_char;
- }
- }
-
- value = write_buffer.str();
- return count;
-}
-
-llssize deserialize_string_raw(
- std::istream& istr,
- std::string& value,
- llssize max_bytes)
-{
- llssize count = 0;
- const S32 BUF_LEN = 20;
- char buf[BUF_LEN]; /* Flawfinder: ignore */
- istr.get(buf, BUF_LEN - 1, ')');
- count += istr.gcount();
- int 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: This is memory inefficient.
- 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 += fullread(istr, (char *)&buf[0], len);
- value.assign(buf.begin(), buf.end());
- }
- c = istr.get();
- ++count;
- if(!((c == '"') || (c == '\'')))
- {
- return LLSDParser::PARSE_FAILURE;
- }
- }
- else
- {
- return LLSDParser::PARSE_FAILURE;
- }
- return count;
-}
-
-static const char* NOTATION_STRING_CHARACTERS[256] =
-{
- "\\x00", // 0
- "\\x01", // 1
- "\\x02", // 2
- "\\x03", // 3
- "\\x04", // 4
- "\\x05", // 5
- "\\x06", // 6
- "\\a", // 7
- "\\b", // 8
- "\\t", // 9
- "\\n", // 10
- "\\v", // 11
- "\\f", // 12
- "\\r", // 13
- "\\x0e", // 14
- "\\x0f", // 15
- "\\x10", // 16
- "\\x11", // 17
- "\\x12", // 18
- "\\x13", // 19
- "\\x14", // 20
- "\\x15", // 21
- "\\x16", // 22
- "\\x17", // 23
- "\\x18", // 24
- "\\x19", // 25
- "\\x1a", // 26
- "\\x1b", // 27
- "\\x1c", // 28
- "\\x1d", // 29
- "\\x1e", // 30
- "\\x1f", // 31
- " ", // 32
- "!", // 33
- "\"", // 34
- "#", // 35
- "$", // 36
- "%", // 37
- "&", // 38
- "\\'", // 39
- "(", // 40
- ")", // 41
- "*", // 42
- "+", // 43
- ",", // 44
- "-", // 45
- ".", // 46
- "/", // 47
- "0", // 48
- "1", // 49
- "2", // 50
- "3", // 51
- "4", // 52
- "5", // 53
- "6", // 54
- "7", // 55
- "8", // 56
- "9", // 57
- ":", // 58
- ";", // 59
- "<", // 60
- "=", // 61
- ">", // 62
- "?", // 63
- "@", // 64
- "A", // 65
- "B", // 66
- "C", // 67
- "D", // 68
- "E", // 69
- "F", // 70
- "G", // 71
- "H", // 72
- "I", // 73
- "J", // 74
- "K", // 75
- "L", // 76
- "M", // 77
- "N", // 78
- "O", // 79
- "P", // 80
- "Q", // 81
- "R", // 82
- "S", // 83
- "T", // 84
- "U", // 85
- "V", // 86
- "W", // 87
- "X", // 88
- "Y", // 89
- "Z", // 90
- "[", // 91
- "\\\\", // 92
- "]", // 93
- "^", // 94
- "_", // 95
- "`", // 96
- "a", // 97
- "b", // 98
- "c", // 99
- "d", // 100
- "e", // 101
- "f", // 102
- "g", // 103
- "h", // 104
- "i", // 105
- "j", // 106
- "k", // 107
- "l", // 108
- "m", // 109
- "n", // 110
- "o", // 111
- "p", // 112
- "q", // 113
- "r", // 114
- "s", // 115
- "t", // 116
- "u", // 117
- "v", // 118
- "w", // 119
- "x", // 120
- "y", // 121
- "z", // 122
- "{", // 123
- "|", // 124
- "}", // 125
- "~", // 126
- "\\x7f", // 127
- "\\x80", // 128
- "\\x81", // 129
- "\\x82", // 130
- "\\x83", // 131
- "\\x84", // 132
- "\\x85", // 133
- "\\x86", // 134
- "\\x87", // 135
- "\\x88", // 136
- "\\x89", // 137
- "\\x8a", // 138
- "\\x8b", // 139
- "\\x8c", // 140
- "\\x8d", // 141
- "\\x8e", // 142
- "\\x8f", // 143
- "\\x90", // 144
- "\\x91", // 145
- "\\x92", // 146
- "\\x93", // 147
- "\\x94", // 148
- "\\x95", // 149
- "\\x96", // 150
- "\\x97", // 151
- "\\x98", // 152
- "\\x99", // 153
- "\\x9a", // 154
- "\\x9b", // 155
- "\\x9c", // 156
- "\\x9d", // 157
- "\\x9e", // 158
- "\\x9f", // 159
- "\\xa0", // 160
- "\\xa1", // 161
- "\\xa2", // 162
- "\\xa3", // 163
- "\\xa4", // 164
- "\\xa5", // 165
- "\\xa6", // 166
- "\\xa7", // 167
- "\\xa8", // 168
- "\\xa9", // 169
- "\\xaa", // 170
- "\\xab", // 171
- "\\xac", // 172
- "\\xad", // 173
- "\\xae", // 174
- "\\xaf", // 175
- "\\xb0", // 176
- "\\xb1", // 177
- "\\xb2", // 178
- "\\xb3", // 179
- "\\xb4", // 180
- "\\xb5", // 181
- "\\xb6", // 182
- "\\xb7", // 183
- "\\xb8", // 184
- "\\xb9", // 185
- "\\xba", // 186
- "\\xbb", // 187
- "\\xbc", // 188
- "\\xbd", // 189
- "\\xbe", // 190
- "\\xbf", // 191
- "\\xc0", // 192
- "\\xc1", // 193
- "\\xc2", // 194
- "\\xc3", // 195
- "\\xc4", // 196
- "\\xc5", // 197
- "\\xc6", // 198
- "\\xc7", // 199
- "\\xc8", // 200
- "\\xc9", // 201
- "\\xca", // 202
- "\\xcb", // 203
- "\\xcc", // 204
- "\\xcd", // 205
- "\\xce", // 206
- "\\xcf", // 207
- "\\xd0", // 208
- "\\xd1", // 209
- "\\xd2", // 210
- "\\xd3", // 211
- "\\xd4", // 212
- "\\xd5", // 213
- "\\xd6", // 214
- "\\xd7", // 215
- "\\xd8", // 216
- "\\xd9", // 217
- "\\xda", // 218
- "\\xdb", // 219
- "\\xdc", // 220
- "\\xdd", // 221
- "\\xde", // 222
- "\\xdf", // 223
- "\\xe0", // 224
- "\\xe1", // 225
- "\\xe2", // 226
- "\\xe3", // 227
- "\\xe4", // 228
- "\\xe5", // 229
- "\\xe6", // 230
- "\\xe7", // 231
- "\\xe8", // 232
- "\\xe9", // 233
- "\\xea", // 234
- "\\xeb", // 235
- "\\xec", // 236
- "\\xed", // 237
- "\\xee", // 238
- "\\xef", // 239
- "\\xf0", // 240
- "\\xf1", // 241
- "\\xf2", // 242
- "\\xf3", // 243
- "\\xf4", // 244
- "\\xf5", // 245
- "\\xf6", // 246
- "\\xf7", // 247
- "\\xf8", // 248
- "\\xf9", // 249
- "\\xfa", // 250
- "\\xfb", // 251
- "\\xfc", // 252
- "\\xfd", // 253
- "\\xfe", // 254
- "\\xff" // 255
-};
-
-void serialize_string(const std::string& value, std::ostream& str)
-{
- std::string::const_iterator it = value.begin();
- std::string::const_iterator end = value.end();
- U8 c;
- for(; it != end; ++it)
- {
- c = (U8)(*it);
- str << NOTATION_STRING_CHARACTERS[c];
- }
-}
-
-llssize 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)
- //
- llssize 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);
- return s;
-}
-
-
-//dirty little zippers -- yell at davep if these are horrid
-
-//return a string containing gzipped bytes of binary serialized LLSD
-// VERY inefficient -- creates several copies of LLSD block in memory
-std::string zip_llsd(LLSD& data)
-{
- std::stringstream llsd_strm;
-
- LLSDSerialize::toBinary(data, llsd_strm);
-
- const U32 CHUNK = 65536;
-
- z_stream strm;
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
- strm.opaque = Z_NULL;
-
- S32 ret = deflateInit(&strm, Z_BEST_COMPRESSION);
- if (ret != Z_OK)
- {
- LL_WARNS() << "Failed to compress LLSD block." << LL_ENDL;
- return std::string();
- }
-
- std::string source = llsd_strm.str();
-
- U8 out[CHUNK];
-
- strm.avail_in = narrow<size_t>(source.size());
- strm.next_in = (U8*) source.data();
- U8* output = NULL;
-
- U32 cur_size = 0;
-
- U32 have = 0;
-
- do
- {
- strm.avail_out = CHUNK;
- strm.next_out = out;
-
- ret = deflate(&strm, Z_FINISH);
- if (ret == Z_OK || ret == Z_STREAM_END)
- { //copy result into output
- if (strm.avail_out >= CHUNK)
- {
- deflateEnd(&strm);
- if(output)
- free(output);
- LL_WARNS() << "Failed to compress LLSD block." << LL_ENDL;
- return std::string();
- }
-
- have = CHUNK-strm.avail_out;
- U8* new_output = (U8*) realloc(output, cur_size+have);
- if (new_output == NULL)
- {
- LL_WARNS() << "Failed to compress LLSD block: can't reallocate memory, current size: " << cur_size << " bytes; requested " << cur_size + have << " bytes." << LL_ENDL;
- deflateEnd(&strm);
- if (output)
- {
- free(output);
- }
- return std::string();
- }
- output = new_output;
- memcpy(output+cur_size, out, have);
- cur_size += have;
- }
- else
- {
- deflateEnd(&strm);
- if(output)
- free(output);
- LL_WARNS() << "Failed to compress LLSD block." << LL_ENDL;
- return std::string();
- }
- }
- while (ret == Z_OK);
-
- std::string::size_type size = cur_size;
-
- std::string result((char*) output, size);
- deflateEnd(&strm);
- if(output)
- free(output);
-
- return result;
-}
-
-//decompress a block of LLSD from provided istream
-// not very efficient -- creats a copy of decompressed LLSD block in memory
-// and deserializes from that copy using LLSDSerialize
-LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, std::istream& is, S32 size)
-{
- std::unique_ptr<U8[]> in = std::unique_ptr<U8[]>(new(std::nothrow) U8[size]);
- if (!in)
- {
- return ZR_MEM_ERROR;
- }
- is.read((char*) in.get(), size);
-
- return unzip_llsd(data, in.get(), size);
-}
-
-LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, const U8* in, S32 size)
-{
- U8* result = NULL;
- llssize cur_size = 0;
- z_stream strm;
-
- constexpr U32 CHUNK = 1024 * 512;
-
- static thread_local std::unique_ptr<U8[]> out;
- if (!out)
- {
- out = std::unique_ptr<U8[]>(new(std::nothrow) U8[CHUNK]);
- }
-
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
- strm.opaque = Z_NULL;
- strm.avail_in = size;
- strm.next_in = const_cast<U8*>(in);
-
- S32 ret = inflateInit(&strm);
-
- do
- {
- strm.avail_out = CHUNK;
- strm.next_out = out.get();
- ret = inflate(&strm, Z_NO_FLUSH);
- switch (ret)
- {
- case Z_NEED_DICT:
- case Z_DATA_ERROR:
- {
- inflateEnd(&strm);
- free(result);
- return ZR_DATA_ERROR;
- }
- case Z_STREAM_ERROR:
- case Z_BUF_ERROR:
- {
- inflateEnd(&strm);
- free(result);
- return ZR_BUFFER_ERROR;
- }
-
- case Z_MEM_ERROR:
- {
- inflateEnd(&strm);
- free(result);
- return ZR_MEM_ERROR;
- }
- }
-
- U32 have = CHUNK-strm.avail_out;
-
- U8* new_result = (U8*)realloc(result, cur_size + have);
- if (new_result == NULL)
- {
- inflateEnd(&strm);
- if (result)
- {
- free(result);
- }
- return ZR_MEM_ERROR;
- }
- result = new_result;
- memcpy(result+cur_size, out.get(), have);
- cur_size += have;
-
- } while (ret == Z_OK && ret != Z_STREAM_END);
-
- inflateEnd(&strm);
-
- if (ret != Z_STREAM_END)
- {
- free(result);
- return ZR_DATA_ERROR;
- }
-
- //result now points to the decompressed LLSD block
- {
- char* result_ptr = strip_deprecated_header((char*)result, cur_size);
-
- boost::iostreams::stream<boost::iostreams::array_source> istrm(result_ptr, cur_size);
-
- if (!LLSDSerialize::fromBinary(data, istrm, cur_size, UNZIP_LLSD_MAX_DEPTH))
- {
- free(result);
- return ZR_PARSE_ERROR;
- }
- }
-
- free(result);
- return ZR_OK;
-}
-//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, size_t& outsize, std::istream& is, S32 size )
-{
- if (size == 0)
- {
- LL_WARNS() << "No data to unzip." << LL_ENDL;
- return NULL;
- }
-
- U8* result = NULL;
- U32 cur_size = 0;
- z_stream strm;
-
- const U32 CHUNK = 0x4000;
-
- U8 *in = new(std::nothrow) U8[size];
- if (in == NULL)
- {
- LL_WARNS() << "Memory allocation failure." << LL_ENDL;
- return NULL;
- }
- is.read((char*) in, size);
-
- U8 out[CHUNK];
-
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
- strm.opaque = Z_NULL;
- strm.avail_in = size;
- strm.next_in = in;
-
-
- S32 ret = inflateInit2(&strm, windowBits | ENABLE_ZLIB_GZIP );
- do
- {
- strm.avail_out = CHUNK;
- strm.next_out = out;
- ret = inflate(&strm, Z_NO_FLUSH);
- if (ret == Z_STREAM_ERROR)
- {
- inflateEnd(&strm);
- free(result);
- delete [] in;
- valid = false;
- }
-
- switch (ret)
- {
- case Z_NEED_DICT:
- ret = Z_DATA_ERROR;
- case Z_DATA_ERROR:
- case Z_MEM_ERROR:
- inflateEnd(&strm);
- free(result);
- delete [] in;
- valid = false;
- break;
- }
-
- U32 have = CHUNK-strm.avail_out;
-
- U8* new_result = (U8*) realloc(result, cur_size + have);
- if (new_result == NULL)
- {
- LL_WARNS() << "Failed to unzip LLSD NavMesh block: can't reallocate memory, current size: " << cur_size
- << " bytes; requested " << cur_size + have
- << " bytes; total syze: ." << size << " bytes."
- << LL_ENDL;
- inflateEnd(&strm);
- if (result)
- {
- free(result);
- }
- delete[] in;
- valid = false;
- return NULL;
- }
- result = new_result;
- memcpy(result+cur_size, out, have);
- cur_size += have;
-
- } while (ret == Z_OK);
-
- inflateEnd(&strm);
- delete [] in;
-
- if (ret != Z_STREAM_END)
- {
- free(result);
- valid = false;
- return NULL;
- }
-
- //result now points to the decompressed LLSD block
- {
- outsize= cur_size;
- valid = true;
- }
-
- return result;
-}
-
-char* strip_deprecated_header(char* in, llssize& cur_size, llssize* header_size)
-{
- const char* deprecated_header = "<? LLSD/Binary ?>";
- constexpr size_t deprecated_header_size = 17;
-
- if (cur_size > deprecated_header_size
- && memcmp(in, deprecated_header, deprecated_header_size) == 0)
- {
- in = in + deprecated_header_size;
- cur_size = cur_size - deprecated_header_size;
- if (header_size)
- {
- *header_size = deprecated_header_size + 1;
- }
- }
-
- return in;
-}
-
+/** + * @file llsdserialize.cpp + * @author Phoenix + * @date 2006-03-05 + * @brief Implementation of LLSD parsers and formatters + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "llsdserialize.h" +#include "llpointer.h" +#include "llstreamtools.h" // for fullread + +#include <iostream> +#include "apr_base64.h" + +#include <boost/iostreams/device/array.hpp> +#include <boost/iostreams/stream.hpp> + +#ifdef LL_USESYSTEMLIBS +# include <zlib.h> +#else +# include "zlib-ng/zlib.h" // for davep's dirty little zip functions +#endif + +#if !LL_WINDOWS +#include <netinet/in.h> // htonl & ntohl +#endif + +#include "lldate.h" +#include "llmemorystream.h" +#include "llsd.h" +#include "llstring.h" +#include "lluri.h" + +// File constants +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"); +const std::string LLSD_XML_HEADER("LLSD/XML"); +const std::string LLSD_NOTATION_HEADER("llsd/notation"); + +//used to deflate a gzipped asset (currently used for navmeshes) +#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 + */ + +// static +void LLSDSerialize::serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize type, + LLSDFormatter::EFormatterOptions options) +{ + LLPointer<LLSDFormatter> f = NULL; + + switch (type) + { + case LLSD_BINARY: + str << "<? " << LLSD_BINARY_HEADER << " ?>\n"; + f = new LLSDBinaryFormatter; + break; + + case LLSD_XML: + str << "<? " << LLSD_XML_HEADER << " ?>\n"; + f = new LLSDXMLFormatter; + break; + + case LLSD_NOTATION: + str << "<? " << LLSD_NOTATION_HEADER << " ?>\n"; + f = new LLSDNotationFormatter; + break; + + default: + LL_WARNS() << "serialize request for unknown ELLSD_Serialize" << LL_ENDL; + } + + if (f.notNull()) + { + f->format(sd, str, options); + } +} + +// static +bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str, llssize max_bytes) +{ + char hdr_buf[MAX_HDR_LEN + 1] = ""; /* Flawfinder: ignore */ + bool fail_if_not_legacy = false; + + /* + * 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. + */ + llssize max_hdr_read = MAX_HDR_LEN; + if (max_bytes != LLSDSerialize::SIZE_UNLIMITED) + { + max_hdr_read = llmin(max_bytes + 1, max_hdr_read); + } + str.get(hdr_buf, max_hdr_read, '\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(); + fail_if_not_legacy = true; + } + + 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) + { + LL_WARNS() << "deserialize LLSD parse failure" << LL_ENDL; + return false; + } + + /* + * Remove the newline chars + */ + std::string::size_type lastchar = header.find_last_not_of("\r\n"); + if (lastchar != std::string::npos) + { + // 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); + } + + // 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) + { + header = header.substr(start, end - start); + ws(str); + } + } + /* + * Create the parser as appropriate + */ + if (0 == LLStringUtil::compareInsensitive(header, LLSD_BINARY_HEADER)) + { + return (parse_using<LLSDBinaryParser>(str, sd, max_bytes-inbuf) > 0); + } + else if (0 == LLStringUtil::compareInsensitive(header, LLSD_XML_HEADER)) + { + return (parse_using<LLSDXMLParser>(str, sd, max_bytes-inbuf) > 0); + } + else if (0 == LLStringUtil::compareInsensitive(header, LLSD_NOTATION_HEADER)) + { + return (parse_using<LLSDNotationParser>(str, sd, max_bytes-inbuf) > 0); + } + else // no header we recognize + { + 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 + } +} + +/** + * Endian handlers + */ +#if LL_BIG_ENDIAN +U64 ll_htonll(U64 hostlonglong) { return hostlonglong; } +U64 ll_ntohll(U64 netlonglong) { return netlonglong; } +F64 ll_htond(F64 hostlonglong) { return hostlonglong; } +F64 ll_ntohd(F64 netlonglong) { return netlonglong; } +#else +// I read some comments one a indicating that doing an integer add +// here would be faster than a bitwise or. For now, the or has +// programmer clarity, since the intended outcome matches the +// operation. +U64 ll_htonll(U64 hostlonglong) +{ + return ((U64)(htonl((U32)((hostlonglong >> 32) & 0xFFFFFFFF))) | + ((U64)(htonl((U32)(hostlonglong & 0xFFFFFFFF))) << 32)); +} +U64 ll_ntohll(U64 netlonglong) +{ + return ((U64)(ntohl((U32)((netlonglong >> 32) & 0xFFFFFFFF))) | + ((U64)(ntohl((U32)(netlonglong & 0xFFFFFFFF))) << 32)); +} +union LLEndianSwapper +{ + F64 d; + U64 i; +}; +F64 ll_htond(F64 hostdouble) +{ + LLEndianSwapper tmp; + tmp.d = hostdouble; + tmp.i = ll_htonll(tmp.i); + return tmp.d; +} +F64 ll_ntohd(F64 netdouble) +{ + LLEndianSwapper tmp; + tmp.d = netdouble; + tmp.i = ll_ntohll(tmp.i); + return tmp.d; +} +#endif + +/** + * Local functions. + */ +/** + * @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. + */ +llssize deserialize_string(std::istream& istr, std::string& value, llssize 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. + */ +llssize 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. + */ +llssize deserialize_string_raw( + std::istream& istr, + std::string& value, + llssize 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. + */ +llssize 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. + */ +static const std::string NOTATION_TRUE_SERIAL("true"); +static const std::string NOTATION_FALSE_SERIAL("false"); + +static const char BINARY_TRUE_SERIAL = '1'; +static const char BINARY_FALSE_SERIAL = '0'; + + +/** + * LLSDParser + */ +LLSDParser::LLSDParser() + : mCheckLimits(true), mMaxBytesLeft(0), mParseLines(false) +{ +} + +// virtual +LLSDParser::~LLSDParser() +{ } + +S32 LLSDParser::parse(std::istream& istr, LLSD& data, llssize max_bytes, S32 max_depth) +{ + mCheckLimits = LLSDSerialize::SIZE_UNLIMITED != max_bytes; + mMaxBytesLeft = max_bytes; + return doParse(istr, data, max_depth); +} + + +// Parse using routine to get() lines, faster than parse() +S32 LLSDParser::parseLines(std::istream& istr, LLSD& data) +{ + mCheckLimits = false; + mParseLines = true; + 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(llssize bytes) const +{ + if(mCheckLimits) mMaxBytesLeft -= bytes; +} + + +/** + * LLSDNotationParser + */ +LLSDNotationParser::LLSDNotationParser() +{ +} + +// virtual +LLSDNotationParser::~LLSDNotationParser() +{ } + +// virtual +S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) const +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD + // map: { string:object, string:object } + // array: [ object, object, object ] + // undef: ! + // boolean: true | false | 1 | 0 | T | F | t | f | TRUE | FALSE + // integer: i#### + // real: r#### + // uuid: u#### + // string: "g'day" | 'have a "nice" day' | s(size)"raw data" + // uri: l"escaped" + // date: d"YYYY-MM-DDTHH:MM:SS.FFZ" + // binary: b##"ff3120ab1" | b(size)"raw data" + char c; + c = istr.peek(); + if (max_depth == 0) + { + return PARSE_FAILURE; + } + while(isspace(c)) + { + // pop the whitespace. + c = get(istr); + c = istr.peek(); + continue; + } + if(!istr.good()) + { + return 0; + } + S32 parse_count = 1; + switch(c) + { + case '{': + { + S32 child_count = parseMap(istr, data, max_depth - 1); + if((child_count == PARSE_FAILURE) || data.isUndefined()) + { + parse_count = PARSE_FAILURE; + } + else + { + parse_count += child_count; + } + if(istr.fail()) + { + LL_INFOS() << "STREAM FAILURE reading map." << LL_ENDL; + parse_count = PARSE_FAILURE; + } + break; + } + + case '[': + { + S32 child_count = parseArray(istr, data, max_depth - 1); + if((child_count == PARSE_FAILURE) || data.isUndefined()) + { + parse_count = PARSE_FAILURE; + } + else + { + parse_count += child_count; + } + if(istr.fail()) + { + LL_INFOS() << "STREAM FAILURE reading array." << LL_ENDL; + parse_count = PARSE_FAILURE; + } + break; + } + + case '!': + c = get(istr); + data.clear(); + break; + + case '0': + c = get(istr); + data = false; + break; + + case 'F': + case 'f': + ignore(istr); + c = istr.peek(); + if(isalpha(c)) + { + auto 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()) + { + LL_INFOS() << "STREAM FAILURE reading boolean." << LL_ENDL; + parse_count = PARSE_FAILURE; + } + break; + + case '1': + c = get(istr); + data = true; + break; + + case 'T': + case 't': + ignore(istr); + c = istr.peek(); + if(isalpha(c)) + { + auto 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()) + { + LL_INFOS() << "STREAM FAILURE reading boolean." << LL_ENDL; + parse_count = PARSE_FAILURE; + } + break; + + case 'i': + { + c = get(istr); + S32 integer = 0; + istr >> integer; + data = integer; + if(istr.fail()) + { + LL_INFOS() << "STREAM FAILURE reading integer." << LL_ENDL; + parse_count = PARSE_FAILURE; + } + break; + } + + case 'r': + { + c = get(istr); + F64 real = 0.0; + istr >> real; + data = real; + if(istr.fail()) + { + LL_INFOS() << "STREAM FAILURE reading real." << LL_ENDL; + parse_count = PARSE_FAILURE; + } + break; + } + + case 'u': + { + c = get(istr); + LLUUID id; + istr >> id; + data = id; + if(istr.fail()) + { + LL_INFOS() << "STREAM FAILURE reading uuid." << LL_ENDL; + parse_count = PARSE_FAILURE; + } + break; + } + + case '\"': + case '\'': + case 's': + if(!parseString(istr, data)) + { + parse_count = PARSE_FAILURE; + } + if(istr.fail()) + { + LL_INFOS() << "STREAM FAILURE reading string." << LL_ENDL; + parse_count = PARSE_FAILURE; + } + break; + + case 'l': + { + c = get(istr); // pop the 'l' + c = get(istr); // pop the delimiter + std::string str; + auto cnt = deserialize_string_delim(istr, str, c); + if(PARSE_FAILURE == cnt) + { + parse_count = PARSE_FAILURE; + } + else + { + data = LLURI(str); + account(cnt); + } + if(istr.fail()) + { + LL_INFOS() << "STREAM FAILURE reading link." << LL_ENDL; + parse_count = PARSE_FAILURE; + } + break; + } + + case 'd': + { + c = get(istr); // pop the 'd' + c = get(istr); // pop the delimiter + std::string str; + auto cnt = deserialize_string_delim(istr, str, c); + if(PARSE_FAILURE == cnt) + { + parse_count = PARSE_FAILURE; + } + else + { + data = LLDate(str); + account(cnt); + } + if(istr.fail()) + { + LL_INFOS() << "STREAM FAILURE reading date." << LL_ENDL; + parse_count = PARSE_FAILURE; + } + break; + } + + case 'b': + if(!parseBinary(istr, data)) + { + parse_count = PARSE_FAILURE; + } + if(istr.fail()) + { + LL_INFOS() << "STREAM FAILURE reading data." << LL_ENDL; + parse_count = PARSE_FAILURE; + } + break; + + default: + parse_count = PARSE_FAILURE; + LL_INFOS() << "Unrecognized character while parsing: int(" << int(c) + << ")" << LL_ENDL; + break; + } + if(PARSE_FAILURE == parse_count) + { + data.clear(); + } + return parse_count; +} + +S32 LLSDNotationParser::parseMap(std::istream& istr, LLSD& map, S32 max_depth) const +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD + // map: { string:object, string:object } + map = LLSD::emptyMap(); + S32 parse_count = 0; + char c = get(istr); + if(c == '{') + { + // eat commas, white + bool found_name = false; + std::string name; + c = get(istr); + while(c != '}' && istr.good()) + { + if(!found_name) + { + if((c == '\"') || (c == '\'') || (c == 's')) + { + putback(istr, c); + found_name = true; + auto count = deserialize_string(istr, name, mMaxBytesLeft); + if(PARSE_FAILURE == count) return PARSE_FAILURE; + account(count); + } + c = get(istr); + } + else + { + if(isspace(c) || (c == ':')) + { + c = get(istr); + continue; + } + putback(istr, c); + LLSD child; + S32 count = doParse(istr, child, max_depth); + 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 + { + return PARSE_FAILURE; + } + found_name = false; + c = get(istr); + } + } + if(c != '}') + { + map.clear(); + return PARSE_FAILURE; + } + } + return parse_count; +} + +S32 LLSDNotationParser::parseArray(std::istream& istr, LLSD& array, S32 max_depth) const +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD + // array: [ object, object, object ] + array = LLSD::emptyArray(); + S32 parse_count = 0; + char c = get(istr); + if(c == '[') + { + // eat commas, white + c = get(istr); + while((c != ']') && istr.good()) + { + LLSD child; + if(isspace(c) || (c == ',')) + { + c = get(istr); + continue; + } + putback(istr, c); + S32 count = doParse(istr, child, max_depth); + if(PARSE_FAILURE == count) + { + return PARSE_FAILURE; + } + else + { + parse_count += count; + array.append(child); + } + c = get(istr); + } + if(c != ']') + { + return PARSE_FAILURE; + } + } + return parse_count; +} + +bool LLSDNotationParser::parseString(std::istream& istr, LLSD& data) const +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD + std::string value; + auto count = deserialize_string(istr, value, mMaxBytesLeft); + if(PARSE_FAILURE == count) return false; + account(count); + data = value; + return true; +} + +bool LLSDNotationParser::parseBinary(std::istream& istr, LLSD& data) const +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD + // binary: b##"ff3120ab1" + // or: b(len)"..." + + // I want to manually control those values here to make sure the + // parser doesn't break when someone changes a constant somewhere + // else. + const U32 BINARY_BUFFER_SIZE = 256; + const U32 STREAM_GET_COUNT = 255; + + // need to read the base out. + char buf[BINARY_BUFFER_SIZE]; /* Flawfinder: ignore */ + 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. + auto len = strtol(buf + 2, NULL, 0); + if(mCheckLimits && (len > mMaxBytesLeft)) return false; + std::vector<U8> value; + if(len) + { + value.resize(len); + account(fullread(istr, (char *)&value[0], len)); + } + c = get(istr); // strip off the trailing double-quote + data = value; + } + 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; + 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<U8> value; + if(len) + { + value.resize(len); + len = apr_base64_decode_binary(&value[0], encoded.c_str()); + value.resize(len); + } + data = value; + } + 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 + // done parsing. If it's not, put the data back, and read the + // stream until the next double quote. + char* read; /*Flawfinder: ignore*/ + U8 byte; + U8 byte_buffer[BINARY_BUFFER_SIZE]; + U8* write; + std::vector<U8> value; + c = get(istr); + while(c != '"') + { + putback(istr, c); + read = buf; + write = byte_buffer; + get(istr, buf, STREAM_GET_COUNT, '"'); + c = get(istr); + while(*read != '\0') /*Flawfinder: ignore*/ + { + byte = hex_as_nybble(*read++); + byte = byte << 4; + byte |= hex_as_nybble(*read++); + *write++ = byte; + } + // copy the data out of the byte buffer + value.insert(value.end(), byte_buffer, write); + } + data = value; + } + else + { + return false; + } + return true; +} + + +/** + * LLSDBinaryParser + */ +LLSDBinaryParser::LLSDBinaryParser() +{ +} + +// virtual +LLSDBinaryParser::~LLSDBinaryParser() +{ +} + +// virtual +S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) const +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD +/** + * Undefined: '!'<br> + * Boolean: '1' for true '0' for false<br> + * Integer: 'i' + 4 bytes network byte order<br> + * Real: 'r' + 8 bytes IEEE double<br> + * UUID: 'u' + 16 byte unsigned integer<br> + * String: 's' + 4 byte integer size + string<br> + * strings also secretly support the notation format + * Date: 'd' + 8 byte IEEE double for seconds since epoch<br> + * URI: 'l' + 4 byte integer size + string uri<br> + * Binary: 'b' + 4 byte integer size + binary data<br> + * Array: '[' + 4 byte integer size + all values + ']'<br> + * Map: '{' + 4 byte integer size every(key + value) + '}'<br> + * map keys are serialized as s + 4 byte integer size + string or in the + * notation format. + */ + char c; + c = get(istr); + if(!istr.good()) + { + return 0; + } + if (max_depth == 0) + { + return PARSE_FAILURE; + } + S32 parse_count = 1; + switch(c) + { + case '{': + { + S32 child_count = parseMap(istr, data, max_depth - 1); + if((child_count == PARSE_FAILURE) || data.isUndefined()) + { + parse_count = PARSE_FAILURE; + } + else + { + parse_count += child_count; + } + if(istr.fail()) + { + LL_INFOS() << "STREAM FAILURE reading binary map." << LL_ENDL; + parse_count = PARSE_FAILURE; + } + break; + } + + case '[': + { + S32 child_count = parseArray(istr, data, max_depth - 1); + if((child_count == PARSE_FAILURE) || data.isUndefined()) + { + parse_count = PARSE_FAILURE; + } + else + { + parse_count += child_count; + } + if(istr.fail()) + { + LL_INFOS() << "STREAM FAILURE reading binary array." << LL_ENDL; + parse_count = PARSE_FAILURE; + } + break; + } + + case '!': + data.clear(); + break; + + case '0': + data = false; + break; + + case '1': + data = true; + break; + + case 'i': + { + U32 value_nbo = 0; + read(istr, (char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/ + data = (S32)ntohl(value_nbo); + if(istr.fail()) + { + LL_INFOS() << "STREAM FAILURE reading binary integer." << LL_ENDL; + } + break; + } + + case 'r': + { + F64 real_nbo = 0.0; + read(istr, (char*)&real_nbo, sizeof(F64)); /*Flawfinder: ignore*/ + data = ll_ntohd(real_nbo); + if(istr.fail()) + { + LL_INFOS() << "STREAM FAILURE reading binary real." << LL_ENDL; + } + break; + } + + case 'u': + { + LLUUID id; + read(istr, (char*)(&id.mData), UUID_BYTES); /*Flawfinder: ignore*/ + data = id; + if(istr.fail()) + { + LL_INFOS() << "STREAM FAILURE reading binary uuid." << LL_ENDL; + } + break; + } + + case '\'': + case '"': + { + std::string value; + auto cnt = deserialize_string_delim(istr, value, c); + if(PARSE_FAILURE == cnt) + { + parse_count = PARSE_FAILURE; + } + else + { + data = value; + account(cnt); + } + if(istr.fail()) + { + LL_INFOS() << "STREAM FAILURE reading binary (notation-style) string." + << LL_ENDL; + parse_count = PARSE_FAILURE; + } + break; + } + + case 's': + { + std::string value; + if(parseString(istr, value)) + { + data = value; + } + else + { + parse_count = PARSE_FAILURE; + } + if(istr.fail()) + { + LL_INFOS() << "STREAM FAILURE reading binary string." << LL_ENDL; + parse_count = PARSE_FAILURE; + } + break; + } + + case 'l': + { + std::string value; + if(parseString(istr, value)) + { + data = LLURI(value); + } + else + { + parse_count = PARSE_FAILURE; + } + if(istr.fail()) + { + LL_INFOS() << "STREAM FAILURE reading binary link." << LL_ENDL; + parse_count = PARSE_FAILURE; + } + break; + } + + case 'd': + { + F64 real = 0.0; + read(istr, (char*)&real, sizeof(F64)); /*Flawfinder: ignore*/ + data = LLDate(real); + if(istr.fail()) + { + LL_INFOS() << "STREAM FAILURE reading binary date." << LL_ENDL; + parse_count = PARSE_FAILURE; + } + break; + } + + case 'b': + { + // We probably have a valid raw binary stream. determine + // the size, and read it. + U32 size_nbo = 0; + read(istr, (char*)&size_nbo, sizeof(U32)); /*Flawfinder: ignore*/ + S32 size = (S32)ntohl(size_nbo); + if(mCheckLimits && (size > mMaxBytesLeft)) + { + parse_count = PARSE_FAILURE; + } + else + { + std::vector<U8> value; + if(size > 0) + { + value.resize(size); + account(fullread(istr, (char*)&value[0], size)); + } + data = value; + } + if(istr.fail()) + { + LL_INFOS() << "STREAM FAILURE reading binary." << LL_ENDL; + parse_count = PARSE_FAILURE; + } + break; + } + + default: + parse_count = PARSE_FAILURE; + LL_INFOS() << "Unrecognized character while parsing: int(" << int(c) + << ")" << LL_ENDL; + break; + } + if(PARSE_FAILURE == parse_count) + { + data.clear(); + } + return parse_count; +} + +S32 LLSDBinaryParser::parseMap(std::istream& istr, LLSD& map, S32 max_depth) const +{ + map = LLSD::emptyMap(); + U32 value_nbo = 0; + read(istr, (char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/ + S32 size = (S32)ntohl(value_nbo); + S32 parse_count = 0; + S32 count = 0; + char c = get(istr); + while(c != '}' && (count < size) && istr.good()) + { + std::string name; + switch(c) + { + case 'k': + if(!parseString(istr, name)) + { + return PARSE_FAILURE; + } + break; + case '\'': + case '"': + { + auto cnt = deserialize_string_delim(istr, name, c); + if(PARSE_FAILURE == cnt) return PARSE_FAILURE; + account(cnt); + break; + } + } + LLSD child; + S32 child_count = doParse(istr, child, max_depth); + 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 = 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; +} + +S32 LLSDBinaryParser::parseArray(std::istream& istr, LLSD& array, S32 max_depth) const +{ + array = LLSD::emptyArray(); + U32 value_nbo = 0; + 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 + // array... + + S32 parse_count = 0; + S32 count = 0; + char c = istr.peek(); + while((c != ']') && (count < size) && istr.good()) + { + LLSD child; + S32 child_count = doParse(istr, child, max_depth); + if(PARSE_FAILURE == child_count) + { + return PARSE_FAILURE; + } + if(child_count) + { + parse_count += child_count; + array.append(child); + } + ++count; + c = istr.peek(); + } + 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; +} + +bool LLSDBinaryParser::parseString( + std::istream& istr, + std::string& value) const +{ + // *FIX: This is memory inefficient. + U32 value_nbo = 0; + read(istr, (char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/ + S32 size = (S32)ntohl(value_nbo); + if(mCheckLimits && (size > mMaxBytesLeft)) return false; + if(size < 0) return false; + std::vector<char> buf; + if(size) + { + buf.resize(size); + account(fullread(istr, &buf[0], size)); + value.assign(buf.begin(), buf.end()); + } + return true; +} + + +/** + * LLSDFormatter + */ +LLSDFormatter::LLSDFormatter(bool boolAlpha, const std::string& realFmt, EFormatterOptions options): + mOptions(options) +{ + boolalpha(boolAlpha); + realFormat(realFmt); +} + +// virtual +LLSDFormatter::~LLSDFormatter() +{ } + +void LLSDFormatter::boolalpha(bool alpha) +{ + mBoolAlpha = alpha; +} + +void LLSDFormatter::realFormat(const std::string& format) +{ + mRealFormat = format; +} + +S32 LLSDFormatter::format(const LLSD& data, std::ostream& ostr) const +{ + // pass options captured by constructor + return format(data, ostr, mOptions); +} + +S32 LLSDFormatter::format(const LLSD& data, std::ostream& ostr, EFormatterOptions options) const +{ + return format_impl(data, ostr, options, 0); +} + +void LLSDFormatter::formatReal(LLSD::Real real, std::ostream& ostr) const +{ + std::string buffer = llformat(mRealFormat.c_str(), real); + ostr << buffer; +} + +/** + * LLSDNotationFormatter + */ +LLSDNotationFormatter::LLSDNotationFormatter(bool boolAlpha, const std::string& realFormat, + EFormatterOptions options): + LLSDFormatter(boolAlpha, realFormat, options) +{ +} + +// virtual +LLSDNotationFormatter::~LLSDNotationFormatter() +{ } + +// static +std::string LLSDNotationFormatter::escapeString(const std::string& in) +{ + std::ostringstream ostr; + serialize_string(in, ostr); + return ostr.str(); +} + +S32 LLSDNotationFormatter::format_impl(const LLSD& data, std::ostream& ostr, + EFormatterOptions options, U32 level) const +{ + S32 format_count = 1; + std::string pre; + std::string post; + + if (options & LLSDFormatter::OPTIONS_PRETTY) + { + for (U32 i = 0; i < level; i++) + { + pre += " "; + } + post = "\n"; + } + + switch(data.type()) + { + case LLSD::TypeMap: + { + if (0 != level) ostr << post << pre; + ostr << "{"; + std::string inner_pre; + if (options & LLSDFormatter::OPTIONS_PRETTY) + { + inner_pre = pre + " "; + } + + bool need_comma = false; + LLSD::map_const_iterator iter = data.beginMap(); + LLSD::map_const_iterator end = data.endMap(); + for(; iter != end; ++iter) + { + if(need_comma) ostr << ","; + need_comma = true; + ostr << post << inner_pre << '\''; + serialize_string((*iter).first, ostr); + ostr << "':"; + format_count += format_impl((*iter).second, ostr, options, level + 2); + } + ostr << post << pre << "}"; + break; + } + + case LLSD::TypeArray: + { + ostr << post << pre << "["; + bool need_comma = false; + LLSD::array_const_iterator iter = data.beginArray(); + LLSD::array_const_iterator end = data.endArray(); + for(; iter != end; ++iter) + { + if(need_comma) ostr << ","; + need_comma = true; + format_count += format_impl(*iter, ostr, options, level + 1); + } + ostr << "]"; + break; + } + + case LLSD::TypeUndefined: + ostr << "!"; + break; + + case LLSD::TypeBoolean: + if(mBoolAlpha || +#if( LL_WINDOWS || __GNUC__ > 2) + (ostr.flags() & std::ios::boolalpha) +#else + (ostr.flags() & 0x0100) +#endif + ) + { + ostr << (data.asBoolean() + ? NOTATION_TRUE_SERIAL : NOTATION_FALSE_SERIAL); + } + else + { + ostr << (data.asBoolean() ? 1 : 0); + } + break; + + case LLSD::TypeInteger: + ostr << "i" << data.asInteger(); + break; + + case LLSD::TypeReal: + ostr << "r"; + if(mRealFormat.empty()) + { + ostr << data.asReal(); + } + else + { + formatReal(data.asReal(), ostr); + } + break; + + case LLSD::TypeUUID: + ostr << "u" << data.asUUID(); + break; + + case LLSD::TypeString: + ostr << '\''; + serialize_string(data.asStringRef(), ostr); + ostr << '\''; + break; + + case LLSD::TypeDate: + ostr << "d\"" << data.asDate() << "\""; + break; + + case LLSD::TypeURI: + ostr << "l\""; + serialize_string(data.asString(), ostr); + ostr << "\""; + break; + + case LLSD::TypeBinary: + { + // *FIX: memory inefficient. + const std::vector<U8>& buffer = data.asBinary(); + if (options & LLSDFormatter::OPTIONS_PRETTY_BINARY) + { + ostr << "b16\""; + if (! buffer.empty()) + { + std::ios_base::fmtflags old_flags = ostr.flags(); + ostr.setf( std::ios::hex, std::ios::basefield ); + // It shouldn't strictly matter whether the emitted hex digits + // are uppercase; LLSDNotationParser handles either; but as of + // 2020-05-13, Python's llbase.llsd requires uppercase hex. + ostr << std::uppercase; + auto oldfill(ostr.fill('0')); + auto oldwidth(ostr.width()); + for (size_t i = 0; i < buffer.size(); i++) + { + // have to restate setw() before every conversion + ostr << std::setw(2) << (int) buffer[i]; + } + ostr.width(oldwidth); + ostr.fill(oldfill); + ostr.flags(old_flags); + } + } + else // ! OPTIONS_PRETTY_BINARY + { + ostr << "b(" << buffer.size() << ")\""; + if (! buffer.empty()) + { + ostr.write((const char*)&buffer[0], buffer.size()); + } + } + ostr << "\""; + break; + } + + default: + // *NOTE: This should never happen. + ostr << "!"; + break; + } + return format_count; +} + +/** + * LLSDBinaryFormatter + */ +LLSDBinaryFormatter::LLSDBinaryFormatter(bool boolAlpha, const std::string& realFormat, + EFormatterOptions options): + LLSDFormatter(boolAlpha, realFormat, options) +{ +} + +// virtual +LLSDBinaryFormatter::~LLSDBinaryFormatter() +{ } + +// virtual +S32 LLSDBinaryFormatter::format_impl(const LLSD& data, std::ostream& ostr, + EFormatterOptions options, U32 level) const +{ + S32 format_count = 1; + switch(data.type()) + { + case LLSD::TypeMap: + { + ostr.put('{'); + U32 size_nbo = htonl(data.size()); + ostr.write((const char*)(&size_nbo), sizeof(U32)); + LLSD::map_const_iterator iter = data.beginMap(); + LLSD::map_const_iterator end = data.endMap(); + for(; iter != end; ++iter) + { + ostr.put('k'); + formatString((*iter).first, ostr); + format_count += format_impl((*iter).second, ostr, options, level+1); + } + ostr.put('}'); + break; + } + + case LLSD::TypeArray: + { + ostr.put('['); + U32 size_nbo = htonl(data.size()); + ostr.write((const char*)(&size_nbo), sizeof(U32)); + LLSD::array_const_iterator iter = data.beginArray(); + LLSD::array_const_iterator end = data.endArray(); + for(; iter != end; ++iter) + { + format_count += format_impl(*iter, ostr, options, level+1); + } + ostr.put(']'); + break; + } + + case LLSD::TypeUndefined: + ostr.put('!'); + break; + + case LLSD::TypeBoolean: + if(data.asBoolean()) ostr.put(BINARY_TRUE_SERIAL); + else ostr.put(BINARY_FALSE_SERIAL); + break; + + case LLSD::TypeInteger: + { + ostr.put('i'); + U32 value_nbo = htonl(data.asInteger()); + ostr.write((const char*)(&value_nbo), sizeof(U32)); + break; + } + + case LLSD::TypeReal: + { + ostr.put('r'); + F64 value_nbo = ll_htond(data.asReal()); + ostr.write((const char*)(&value_nbo), sizeof(F64)); + break; + } + + case LLSD::TypeUUID: + { + ostr.put('u'); + LLUUID temp = data.asUUID(); + ostr.write((const char*)(&(temp.mData)), UUID_BYTES); + break; + } + + case LLSD::TypeString: + ostr.put('s'); + formatString(data.asStringRef(), ostr); + break; + + case LLSD::TypeDate: + { + ostr.put('d'); + F64 value = data.asReal(); + ostr.write((const char*)(&value), sizeof(F64)); + break; + } + + case LLSD::TypeURI: + ostr.put('l'); + formatString(data.asString(), ostr); + break; + + case LLSD::TypeBinary: + { + ostr.put('b'); + const std::vector<U8>& buffer = data.asBinary(); + U32 size_nbo = htonl(buffer.size()); + ostr.write((const char*)(&size_nbo), sizeof(U32)); + if(buffer.size()) ostr.write((const char*)&buffer[0], buffer.size()); + break; + } + + default: + // *NOTE: This should never happen. + ostr.put('!'); + break; + } + return format_count; +} + +void LLSDBinaryFormatter::formatString( + const std::string& string, + std::ostream& ostr) const +{ + U32 size_nbo = htonl(string.size()); + ostr.write((const char*)(&size_nbo), sizeof(U32)); + ostr.write(string.c_str(), string.size()); +} + +/** + * local functions + */ +llssize deserialize_string(std::istream& istr, std::string& value, llssize max_bytes) +{ + int c = istr.get(); + if(istr.fail()) + { + // No data in stream, bail out but mention the character we + // grabbed. + return LLSDParser::PARSE_FAILURE; + } + + llssize rv = LLSDParser::PARSE_FAILURE; + switch(c) + { + case '\'': + case '"': + rv = deserialize_string_delim(istr, value, c); + break; + case 's': + // 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; + } + if(LLSDParser::PARSE_FAILURE == rv) return rv; + return rv + 1; // account for the character grabbed at the top. +} + +llssize deserialize_string_delim( + std::istream& istr, + std::string& value, + char delim) +{ + std::ostringstream write_buffer; + bool found_escape = false; + bool found_hex = false; + bool found_digit = false; + U8 byte = 0; + llssize count = 0; + + while (true) + { + int next_byte = istr.get(); + ++count; + + if(istr.fail()) + { + // If our stream is empty, break out + value = write_buffer.str(); + return LLSDParser::PARSE_FAILURE; + } + + char next_char = (char)next_byte; // Now that we know it's not EOF + + if(found_escape) + { + // next character(s) is a special sequence. + if(found_hex) + { + if(found_digit) + { + found_digit = false; + found_hex = false; + found_escape = false; + byte = byte << 4; + byte |= hex_as_nybble(next_char); + write_buffer << byte; + byte = 0; + } + else + { + // next character is the first nybble of + // + found_digit = true; + byte = hex_as_nybble(next_char); + } + } + else if(next_char == 'x') + { + found_hex = true; + } + else + { + switch(next_char) + { + case 'a': + write_buffer << '\a'; + break; + case 'b': + write_buffer << '\b'; + break; + case 'f': + write_buffer << '\f'; + break; + case 'n': + write_buffer << '\n'; + break; + case 'r': + write_buffer << '\r'; + break; + case 't': + write_buffer << '\t'; + break; + case 'v': + write_buffer << '\v'; + break; + default: + write_buffer << next_char; + break; + } + found_escape = false; + } + } + else if(next_char == '\\') + { + found_escape = true; + } + else if(next_char == delim) + { + break; + } + else + { + write_buffer << next_char; + } + } + + value = write_buffer.str(); + return count; +} + +llssize deserialize_string_raw( + std::istream& istr, + std::string& value, + llssize max_bytes) +{ + llssize count = 0; + const S32 BUF_LEN = 20; + char buf[BUF_LEN]; /* Flawfinder: ignore */ + istr.get(buf, BUF_LEN - 1, ')'); + count += istr.gcount(); + int 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: This is memory inefficient. + 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 += fullread(istr, (char *)&buf[0], len); + value.assign(buf.begin(), buf.end()); + } + c = istr.get(); + ++count; + if(!((c == '"') || (c == '\''))) + { + return LLSDParser::PARSE_FAILURE; + } + } + else + { + return LLSDParser::PARSE_FAILURE; + } + return count; +} + +static const char* NOTATION_STRING_CHARACTERS[256] = +{ + "\\x00", // 0 + "\\x01", // 1 + "\\x02", // 2 + "\\x03", // 3 + "\\x04", // 4 + "\\x05", // 5 + "\\x06", // 6 + "\\a", // 7 + "\\b", // 8 + "\\t", // 9 + "\\n", // 10 + "\\v", // 11 + "\\f", // 12 + "\\r", // 13 + "\\x0e", // 14 + "\\x0f", // 15 + "\\x10", // 16 + "\\x11", // 17 + "\\x12", // 18 + "\\x13", // 19 + "\\x14", // 20 + "\\x15", // 21 + "\\x16", // 22 + "\\x17", // 23 + "\\x18", // 24 + "\\x19", // 25 + "\\x1a", // 26 + "\\x1b", // 27 + "\\x1c", // 28 + "\\x1d", // 29 + "\\x1e", // 30 + "\\x1f", // 31 + " ", // 32 + "!", // 33 + "\"", // 34 + "#", // 35 + "$", // 36 + "%", // 37 + "&", // 38 + "\\'", // 39 + "(", // 40 + ")", // 41 + "*", // 42 + "+", // 43 + ",", // 44 + "-", // 45 + ".", // 46 + "/", // 47 + "0", // 48 + "1", // 49 + "2", // 50 + "3", // 51 + "4", // 52 + "5", // 53 + "6", // 54 + "7", // 55 + "8", // 56 + "9", // 57 + ":", // 58 + ";", // 59 + "<", // 60 + "=", // 61 + ">", // 62 + "?", // 63 + "@", // 64 + "A", // 65 + "B", // 66 + "C", // 67 + "D", // 68 + "E", // 69 + "F", // 70 + "G", // 71 + "H", // 72 + "I", // 73 + "J", // 74 + "K", // 75 + "L", // 76 + "M", // 77 + "N", // 78 + "O", // 79 + "P", // 80 + "Q", // 81 + "R", // 82 + "S", // 83 + "T", // 84 + "U", // 85 + "V", // 86 + "W", // 87 + "X", // 88 + "Y", // 89 + "Z", // 90 + "[", // 91 + "\\\\", // 92 + "]", // 93 + "^", // 94 + "_", // 95 + "`", // 96 + "a", // 97 + "b", // 98 + "c", // 99 + "d", // 100 + "e", // 101 + "f", // 102 + "g", // 103 + "h", // 104 + "i", // 105 + "j", // 106 + "k", // 107 + "l", // 108 + "m", // 109 + "n", // 110 + "o", // 111 + "p", // 112 + "q", // 113 + "r", // 114 + "s", // 115 + "t", // 116 + "u", // 117 + "v", // 118 + "w", // 119 + "x", // 120 + "y", // 121 + "z", // 122 + "{", // 123 + "|", // 124 + "}", // 125 + "~", // 126 + "\\x7f", // 127 + "\\x80", // 128 + "\\x81", // 129 + "\\x82", // 130 + "\\x83", // 131 + "\\x84", // 132 + "\\x85", // 133 + "\\x86", // 134 + "\\x87", // 135 + "\\x88", // 136 + "\\x89", // 137 + "\\x8a", // 138 + "\\x8b", // 139 + "\\x8c", // 140 + "\\x8d", // 141 + "\\x8e", // 142 + "\\x8f", // 143 + "\\x90", // 144 + "\\x91", // 145 + "\\x92", // 146 + "\\x93", // 147 + "\\x94", // 148 + "\\x95", // 149 + "\\x96", // 150 + "\\x97", // 151 + "\\x98", // 152 + "\\x99", // 153 + "\\x9a", // 154 + "\\x9b", // 155 + "\\x9c", // 156 + "\\x9d", // 157 + "\\x9e", // 158 + "\\x9f", // 159 + "\\xa0", // 160 + "\\xa1", // 161 + "\\xa2", // 162 + "\\xa3", // 163 + "\\xa4", // 164 + "\\xa5", // 165 + "\\xa6", // 166 + "\\xa7", // 167 + "\\xa8", // 168 + "\\xa9", // 169 + "\\xaa", // 170 + "\\xab", // 171 + "\\xac", // 172 + "\\xad", // 173 + "\\xae", // 174 + "\\xaf", // 175 + "\\xb0", // 176 + "\\xb1", // 177 + "\\xb2", // 178 + "\\xb3", // 179 + "\\xb4", // 180 + "\\xb5", // 181 + "\\xb6", // 182 + "\\xb7", // 183 + "\\xb8", // 184 + "\\xb9", // 185 + "\\xba", // 186 + "\\xbb", // 187 + "\\xbc", // 188 + "\\xbd", // 189 + "\\xbe", // 190 + "\\xbf", // 191 + "\\xc0", // 192 + "\\xc1", // 193 + "\\xc2", // 194 + "\\xc3", // 195 + "\\xc4", // 196 + "\\xc5", // 197 + "\\xc6", // 198 + "\\xc7", // 199 + "\\xc8", // 200 + "\\xc9", // 201 + "\\xca", // 202 + "\\xcb", // 203 + "\\xcc", // 204 + "\\xcd", // 205 + "\\xce", // 206 + "\\xcf", // 207 + "\\xd0", // 208 + "\\xd1", // 209 + "\\xd2", // 210 + "\\xd3", // 211 + "\\xd4", // 212 + "\\xd5", // 213 + "\\xd6", // 214 + "\\xd7", // 215 + "\\xd8", // 216 + "\\xd9", // 217 + "\\xda", // 218 + "\\xdb", // 219 + "\\xdc", // 220 + "\\xdd", // 221 + "\\xde", // 222 + "\\xdf", // 223 + "\\xe0", // 224 + "\\xe1", // 225 + "\\xe2", // 226 + "\\xe3", // 227 + "\\xe4", // 228 + "\\xe5", // 229 + "\\xe6", // 230 + "\\xe7", // 231 + "\\xe8", // 232 + "\\xe9", // 233 + "\\xea", // 234 + "\\xeb", // 235 + "\\xec", // 236 + "\\xed", // 237 + "\\xee", // 238 + "\\xef", // 239 + "\\xf0", // 240 + "\\xf1", // 241 + "\\xf2", // 242 + "\\xf3", // 243 + "\\xf4", // 244 + "\\xf5", // 245 + "\\xf6", // 246 + "\\xf7", // 247 + "\\xf8", // 248 + "\\xf9", // 249 + "\\xfa", // 250 + "\\xfb", // 251 + "\\xfc", // 252 + "\\xfd", // 253 + "\\xfe", // 254 + "\\xff" // 255 +}; + +void serialize_string(const std::string& value, std::ostream& str) +{ + std::string::const_iterator it = value.begin(); + std::string::const_iterator end = value.end(); + U8 c; + for(; it != end; ++it) + { + c = (U8)(*it); + str << NOTATION_STRING_CHARACTERS[c]; + } +} + +llssize 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) + // + llssize 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); + return s; +} + + +//dirty little zippers -- yell at davep if these are horrid + +//return a string containing gzipped bytes of binary serialized LLSD +// VERY inefficient -- creates several copies of LLSD block in memory +std::string zip_llsd(LLSD& data) +{ + std::stringstream llsd_strm; + + LLSDSerialize::toBinary(data, llsd_strm); + + const U32 CHUNK = 65536; + + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + S32 ret = deflateInit(&strm, Z_BEST_COMPRESSION); + if (ret != Z_OK) + { + LL_WARNS() << "Failed to compress LLSD block." << LL_ENDL; + return std::string(); + } + + std::string source = llsd_strm.str(); + + U8 out[CHUNK]; + + strm.avail_in = narrow<size_t>(source.size()); + strm.next_in = (U8*) source.data(); + U8* output = NULL; + + U32 cur_size = 0; + + U32 have = 0; + + do + { + strm.avail_out = CHUNK; + strm.next_out = out; + + ret = deflate(&strm, Z_FINISH); + if (ret == Z_OK || ret == Z_STREAM_END) + { //copy result into output + if (strm.avail_out >= CHUNK) + { + deflateEnd(&strm); + if(output) + free(output); + LL_WARNS() << "Failed to compress LLSD block." << LL_ENDL; + return std::string(); + } + + have = CHUNK-strm.avail_out; + U8* new_output = (U8*) realloc(output, cur_size+have); + if (new_output == NULL) + { + LL_WARNS() << "Failed to compress LLSD block: can't reallocate memory, current size: " << cur_size << " bytes; requested " << cur_size + have << " bytes." << LL_ENDL; + deflateEnd(&strm); + if (output) + { + free(output); + } + return std::string(); + } + output = new_output; + memcpy(output+cur_size, out, have); + cur_size += have; + } + else + { + deflateEnd(&strm); + if(output) + free(output); + LL_WARNS() << "Failed to compress LLSD block." << LL_ENDL; + return std::string(); + } + } + while (ret == Z_OK); + + std::string::size_type size = cur_size; + + std::string result((char*) output, size); + deflateEnd(&strm); + if(output) + free(output); + + return result; +} + +//decompress a block of LLSD from provided istream +// not very efficient -- creats a copy of decompressed LLSD block in memory +// and deserializes from that copy using LLSDSerialize +LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, std::istream& is, S32 size) +{ + std::unique_ptr<U8[]> in = std::unique_ptr<U8[]>(new(std::nothrow) U8[size]); + if (!in) + { + return ZR_MEM_ERROR; + } + is.read((char*) in.get(), size); + + return unzip_llsd(data, in.get(), size); +} + +LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, const U8* in, S32 size) +{ + U8* result = NULL; + llssize cur_size = 0; + z_stream strm; + + constexpr U32 CHUNK = 1024 * 512; + + static thread_local std::unique_ptr<U8[]> out; + if (!out) + { + out = std::unique_ptr<U8[]>(new(std::nothrow) U8[CHUNK]); + } + + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = size; + strm.next_in = const_cast<U8*>(in); + + S32 ret = inflateInit(&strm); + + do + { + strm.avail_out = CHUNK; + strm.next_out = out.get(); + ret = inflate(&strm, Z_NO_FLUSH); + switch (ret) + { + case Z_NEED_DICT: + case Z_DATA_ERROR: + { + inflateEnd(&strm); + free(result); + return ZR_DATA_ERROR; + } + case Z_STREAM_ERROR: + case Z_BUF_ERROR: + { + inflateEnd(&strm); + free(result); + return ZR_BUFFER_ERROR; + } + + case Z_MEM_ERROR: + { + inflateEnd(&strm); + free(result); + return ZR_MEM_ERROR; + } + } + + U32 have = CHUNK-strm.avail_out; + + U8* new_result = (U8*)realloc(result, cur_size + have); + if (new_result == NULL) + { + inflateEnd(&strm); + if (result) + { + free(result); + } + return ZR_MEM_ERROR; + } + result = new_result; + memcpy(result+cur_size, out.get(), have); + cur_size += have; + + } while (ret == Z_OK && ret != Z_STREAM_END); + + inflateEnd(&strm); + + if (ret != Z_STREAM_END) + { + free(result); + return ZR_DATA_ERROR; + } + + //result now points to the decompressed LLSD block + { + char* result_ptr = strip_deprecated_header((char*)result, cur_size); + + boost::iostreams::stream<boost::iostreams::array_source> istrm(result_ptr, cur_size); + + if (!LLSDSerialize::fromBinary(data, istrm, cur_size, UNZIP_LLSD_MAX_DEPTH)) + { + free(result); + return ZR_PARSE_ERROR; + } + } + + free(result); + return ZR_OK; +} +//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, size_t& outsize, std::istream& is, S32 size ) +{ + if (size == 0) + { + LL_WARNS() << "No data to unzip." << LL_ENDL; + return NULL; + } + + U8* result = NULL; + U32 cur_size = 0; + z_stream strm; + + const U32 CHUNK = 0x4000; + + U8 *in = new(std::nothrow) U8[size]; + if (in == NULL) + { + LL_WARNS() << "Memory allocation failure." << LL_ENDL; + return NULL; + } + is.read((char*) in, size); + + U8 out[CHUNK]; + + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = size; + strm.next_in = in; + + + S32 ret = inflateInit2(&strm, windowBits | ENABLE_ZLIB_GZIP ); + do + { + strm.avail_out = CHUNK; + strm.next_out = out; + ret = inflate(&strm, Z_NO_FLUSH); + if (ret == Z_STREAM_ERROR) + { + inflateEnd(&strm); + free(result); + delete [] in; + valid = false; + } + + switch (ret) + { + case Z_NEED_DICT: + ret = Z_DATA_ERROR; + case Z_DATA_ERROR: + case Z_MEM_ERROR: + inflateEnd(&strm); + free(result); + delete [] in; + valid = false; + break; + } + + U32 have = CHUNK-strm.avail_out; + + U8* new_result = (U8*) realloc(result, cur_size + have); + if (new_result == NULL) + { + LL_WARNS() << "Failed to unzip LLSD NavMesh block: can't reallocate memory, current size: " << cur_size + << " bytes; requested " << cur_size + have + << " bytes; total syze: ." << size << " bytes." + << LL_ENDL; + inflateEnd(&strm); + if (result) + { + free(result); + } + delete[] in; + valid = false; + return NULL; + } + result = new_result; + memcpy(result+cur_size, out, have); + cur_size += have; + + } while (ret == Z_OK); + + inflateEnd(&strm); + delete [] in; + + if (ret != Z_STREAM_END) + { + free(result); + valid = false; + return NULL; + } + + //result now points to the decompressed LLSD block + { + outsize= cur_size; + valid = true; + } + + return result; +} + +char* strip_deprecated_header(char* in, llssize& cur_size, llssize* header_size) +{ + const char* deprecated_header = "<? LLSD/Binary ?>"; + constexpr size_t deprecated_header_size = 17; + + if (cur_size > deprecated_header_size + && memcmp(in, deprecated_header, deprecated_header_size) == 0) + { + in = in + deprecated_header_size; + cur_size = cur_size - deprecated_header_size; + if (header_size) + { + *header_size = deprecated_header_size + 1; + } + } + + return in; +} + |