Permalink
Browse files

Fix connection parsing in SOCI Firebird backend to handle spaces in v…

…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 17, 2013
1 parent 42a6cc2 commit 27068592a0748690a0fd88cf095965e97bd8b767
Showing with 144 additions and 15 deletions.
  1. +144 −15 src/backends/firebird/session.cpp
@@ -9,6 +9,7 @@
#include "soci-firebird.h"
#include "error-firebird.h"
#include "session.h"
#include <locale>
#include <map>
#include <sstream>
#include <string>
@@ -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
@@ -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;

0 comments on commit 2706859

Please sign in to comment.