Skip to content

Commit

Permalink
Fix connection parsing in SOCI Firebird backend to handle spaces in v…
Browse files Browse the repository at this point in the history
…alues.

Really parse the connection strings instead of just breaking them on spaces.
This allows to detect errors such as not using "key=value" syntax at all but
most importantly allows to use spaces inside the values, as can be necessary
for the "service" parameter value when using embedded Firebird as the file
names can contain spaces.

Signed-off-by: Vadim Zeitlin <vz-soci@zeitlins.org>
  • Loading branch information
vadz committed Feb 18, 2013
1 parent 42a6cc2 commit 2706859
Showing 1 changed file with 144 additions and 15 deletions.
159 changes: 144 additions & 15 deletions src/backends/firebird/session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "soci-firebird.h"
#include "error-firebird.h"
#include "session.h"
#include <locale>
#include <map>
#include <sstream>
#include <string>
Expand All @@ -19,32 +20,160 @@ using namespace soci::details::firebird;
namespace
{

// retrieves parameters from the uniform connect string
void explodeISCConnectString(std::string const &connectString,
std::map<std::string, std::string> &parameters)
// Helpers of explodeISCConnectString() for reading words from a string. "Word"
// here is defined very loosely as just a sequence of non-space characters.
//
// All these helper functions update the input iterator to point to the first
// character not consumed by them.

// Advance the input iterator until the first non-space character or end of the
// string.
void skipWhiteSpace(std::string::const_iterator& i, std::string::const_iterator const &end)
{
std::string tmp;
for (std::string::const_iterator i = connectString.begin(),
end = connectString.end(); i != end; ++i)
std::locale const loc;
for (; i != end; ++i)
{
if (*i == '=')
if (!std::isspace(*i, loc))
break;
}
}

// Return the string of all characters until the first space or the specified
// delimiter.
//
// Throws if the first non-space character after the end of the word is not the
// delimiter. However just returns en empty string, without throwing, if
// nothing is left at all in the string except for white space.
std::string
getWordUntil(std::string const &s, std::string::const_iterator &i, char delim)
{
std::string::const_iterator const end = s.end();
skipWhiteSpace(i, end);

// We need to handle this case specially because it's not an error if
// nothing at all remains in the string. But if anything does remain, then
// we must have the delimiter.
if (i == end)
return std::string();

// Simply put anything until the delimiter into the word, stopping at the
// first white space character.
std::string word;
std::locale const loc;
for (; i != end; ++i)
{
if (*i == delim)
break;

if (std::isspace(*i, loc))
{
tmp += ' ';
skipWhiteSpace(i, end);
if (i == end || *i != delim)
{
std::ostringstream os;
os << "Expected '" << delim << "' at position "
<< (i - s.begin() + 1)
<< " in Firebird connection string \""
<< s << "\".";

throw soci_error(os.str());
}

break;
}
else

word += *i;
}

if (i == end)
{
std::ostringstream os;
os << "Expected '" << delim
<< "' not found before the end of the string "
<< "in Firebird connection string \""
<< s << "\".";

throw soci_error(os.str());
}

++i; // Skip the delimiter itself.

return word;
}

// Return a possibly quoted word, i.e. either just a sequence of non-space
// characters or everything inside a double-quoted string.
//
// Throws if the word is quoted and the closing quote is not found. However
// doesn't throw, just returns an empty string if there is nothing left.
std::string
getPossiblyQuotedWord(std::string const &s, std::string::const_iterator &i)
{
std::string::const_iterator const end = s.end();
skipWhiteSpace(i, end);

std::string word;

if (i != end && *i == '"')
{
for (;;)
{
tmp += *i;
if (++i == end)
{
std::ostringstream os;
os << "Expected '\"' not found before the end of the string "
"in Firebird connection string \""
<< s << "\".";

throw soci_error(os.str());
}

if (*i == '"')
{
++i;
break;
}

word += *i;
}
}
else // Not quoted.
{
std::locale const loc;
for (; i != end; ++i)
{
if (std::isspace(*i, loc))
break;

parameters.clear();
word += *i;
}
}

return word;
}

// retrieves parameters from the uniform connect string which is supposed to be
// in the form "key=value[ key2=value2 ...]" and the values may be quoted to
// allow including spaces into them. Notice that currently there is no way to
// include both a space and a double quote in a value.
std::map<std::string, std::string>
explodeISCConnectString(std::string const &connectString)
{
std::map<std::string, std::string> parameters;

std::istringstream iss(tmp);
std::string key, value;
while (iss >> key >> value)
for (std::string::const_iterator i = connectString.begin(); ; )
{
key = getWordUntil(connectString, i, '=');
if (key.empty())
break;

value = getPossiblyQuotedWord(connectString, i);

parameters.insert(std::pair<std::string, std::string>(key, value));
}

return parameters;
}

// extracts given parameter from map previusly build with explodeISCConnectString
Expand Down Expand Up @@ -74,8 +203,8 @@ firebird_session_backend::firebird_session_backend(
, decimals_as_strings_(false)
{
// extract connection parameters
std::map<std::string, std::string> params;
explodeISCConnectString(connectString, params);
std::map<std::string, std::string>
params(explodeISCConnectString(connectString));

ISC_STATUS stat[stat_size];
std::string param;
Expand Down

0 comments on commit 2706859

Please sign in to comment.