| 1 | /// ----------------------------------------*- mode: C++; -*--
 | 
|---|
| 2 | /// @file configuration.cpp
 | 
|---|
| 3 | /// A configuration file parser
 | 
|---|
| 4 | /// ----------------------------------------------------------
 | 
|---|
| 5 | /// $Id: configuration.cpp 2549 2007-04-02 22:17:37Z bless $
 | 
|---|
| 6 | /// $HeadURL: https://svn.ipv6.tm.uka.de/nsis/protlib/trunk/src/configuration.cpp $
 | 
|---|
| 7 | // ===========================================================
 | 
|---|
| 8 | //                      
 | 
|---|
| 9 | // Copyright (C) 2005-2007, all rights reserved by
 | 
|---|
| 10 | // - Institute of Telematics, Universitaet Karlsruhe (TH)
 | 
|---|
| 11 | //
 | 
|---|
| 12 | // More information and contact:
 | 
|---|
| 13 | // https://projekte.tm.uka.de/trac/NSIS
 | 
|---|
| 14 | //                      
 | 
|---|
| 15 | // This program is free software; you can redistribute it and/or modify
 | 
|---|
| 16 | // it under the terms of the GNU General Public License as published by
 | 
|---|
| 17 | // the Free Software Foundation; version 2 of the License
 | 
|---|
| 18 | //
 | 
|---|
| 19 | // This program is distributed in the hope that it will be useful,
 | 
|---|
| 20 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
|---|
| 21 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
|---|
| 22 | // GNU General Public License for more details.
 | 
|---|
| 23 | //
 | 
|---|
| 24 | // You should have received a copy of the GNU General Public License along
 | 
|---|
| 25 | // with this program; if not, write to the Free Software Foundation, Inc.,
 | 
|---|
| 26 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 | 
|---|
| 27 | //
 | 
|---|
| 28 | // ===========================================================
 | 
|---|
| 29 | #include <iostream>
 | 
|---|
| 30 | #include <sstream>
 | 
|---|
| 31 | 
 | 
|---|
| 32 | #include "configuration.h"
 | 
|---|
| 33 | 
 | 
|---|
| 34 | 
 | 
|---|
| 35 | using namespace natfw;
 | 
|---|
| 36 | 
 | 
|---|
| 37 | 
 | 
|---|
| 38 | // only used internally
 | 
|---|
| 39 | class parse_error : public std::exception {
 | 
|---|
| 40 |   public:
 | 
|---|
| 41 |         parse_error(const std::string &msg) throw () : msg(msg) { }
 | 
|---|
| 42 |         virtual ~parse_error() throw () { }
 | 
|---|
| 43 | 
 | 
|---|
| 44 |         std::string get_msg() const throw () { return msg; }
 | 
|---|
| 45 | 
 | 
|---|
| 46 |   private:
 | 
|---|
| 47 |         std::string msg;
 | 
|---|
| 48 | };
 | 
|---|
| 49 | 
 | 
|---|
| 50 | 
 | 
|---|
| 51 | /**
 | 
|---|
| 52 |  * Constructor.
 | 
|---|
| 53 |  */
 | 
|---|
| 54 | configuration::configuration(config_entry rules[]) {
 | 
|---|
| 55 |         for (unsigned i=0; rules[i].type != config_entry::T_END; i++)
 | 
|---|
| 56 |                 values[ rules[i].key ] = rules[i];
 | 
|---|
| 57 | }
 | 
|---|
| 58 | 
 | 
|---|
| 59 | 
 | 
|---|
| 60 | /**
 | 
|---|
| 61 |  * Load a configuration file.
 | 
|---|
| 62 |  *
 | 
|---|
| 63 |  * If there's a parse error or the file can't be opened, a config_error
 | 
|---|
| 64 |  * exception is thrown.
 | 
|---|
| 65 |  *
 | 
|---|
| 66 |  * @param filename the file to load
 | 
|---|
| 67 |  */
 | 
|---|
| 68 | void configuration::load(const std::string &filename) throw (config_error) {
 | 
|---|
| 69 | 
 | 
|---|
| 70 |         std::ifstream in(filename.c_str());
 | 
|---|
| 71 | 
 | 
|---|
| 72 |         if ( ! in )
 | 
|---|
| 73 |                 throw config_error("cannot open file `" + filename + "'");
 | 
|---|
| 74 | 
 | 
|---|
| 75 |         try {
 | 
|---|
| 76 |                 this->load(in);
 | 
|---|
| 77 |         }
 | 
|---|
| 78 |         catch ( config_error &e ) {
 | 
|---|
| 79 |                 in.close();
 | 
|---|
| 80 |                 throw;
 | 
|---|
| 81 |         }
 | 
|---|
| 82 |         catch ( ... ) {
 | 
|---|
| 83 |                 in.close();
 | 
|---|
| 84 |                 throw config_error("unknown exception thrown");
 | 
|---|
| 85 |         }
 | 
|---|
| 86 | 
 | 
|---|
| 87 |         in.close();
 | 
|---|
| 88 | }
 | 
|---|
| 89 | 
 | 
|---|
| 90 | 
 | 
|---|
| 91 | /**
 | 
|---|
| 92 |  * Load configuration data from a stream.
 | 
|---|
| 93 |  *
 | 
|---|
| 94 |  * If there is a parse error, a config_error exception is thrown. This method
 | 
|---|
| 95 |  * will read until end of file. It is up to the caller to close the stream.
 | 
|---|
| 96 |  *
 | 
|---|
| 97 |  * @param in_stream the input stream to read data from
 | 
|---|
| 98 |  */
 | 
|---|
| 99 | void configuration::load(std::istream &in_stream) throw (config_error) {
 | 
|---|
| 100 |         using namespace std;
 | 
|---|
| 101 | 
 | 
|---|
| 102 |         for (int line = 1; in_stream; line++) {
 | 
|---|
| 103 |                 string buf;
 | 
|---|
| 104 |                 string key;
 | 
|---|
| 105 | 
 | 
|---|
| 106 |                 getline(in_stream, buf);
 | 
|---|
| 107 | 
 | 
|---|
| 108 |                 stringstream in(buf);
 | 
|---|
| 109 | 
 | 
|---|
| 110 |                 // skip leading whitespace
 | 
|---|
| 111 |                 strip_leading_space(in);
 | 
|---|
| 112 | 
 | 
|---|
| 113 |                 // skip empty lines and comments
 | 
|---|
| 114 |                 if ( in.peek() == -1 || in.peek() == '#' )
 | 
|---|
| 115 |                         continue;
 | 
|---|
| 116 | 
 | 
|---|
| 117 |                 // read the key
 | 
|---|
| 118 |                 in >> key;
 | 
|---|
| 119 |                 if ( key == "")
 | 
|---|
| 120 |                         throw config_error("parse error", line);
 | 
|---|
| 121 | 
 | 
|---|
| 122 |                 if ( values.find(key) == values.end() )
 | 
|---|
| 123 |                         throw config_error("invalid key `" + key + "'", line);
 | 
|---|
| 124 | 
 | 
|---|
| 125 |                 // skip space between key and '='
 | 
|---|
| 126 |                 strip_leading_space(in);
 | 
|---|
| 127 | 
 | 
|---|
| 128 |                 char c = in.get();
 | 
|---|
| 129 |                 if ( c != '=' )
 | 
|---|
| 130 |                         throw config_error("parse error", line);
 | 
|---|
| 131 | 
 | 
|---|
| 132 |                 // skip space between '=' and value
 | 
|---|
| 133 |                 strip_leading_space(in);
 | 
|---|
| 134 | 
 | 
|---|
| 135 |                 // no value for this key, we ignore it altogether
 | 
|---|
| 136 |                 if ( in.peek() == -1 )
 | 
|---|
| 137 |                         continue;
 | 
|---|
| 138 | 
 | 
|---|
| 139 |                 try {
 | 
|---|
| 140 |                         parse_and_assign(key, in);
 | 
|---|
| 141 |                 }
 | 
|---|
| 142 |                 catch ( parse_error &e ) {
 | 
|---|
| 143 |                         throw config_error(e.get_msg(), line);
 | 
|---|
| 144 |                 }
 | 
|---|
| 145 |         }
 | 
|---|
| 146 | 
 | 
|---|
| 147 |         if ( ! in_stream.eof() )
 | 
|---|
| 148 |                 throw config_error("stream error");
 | 
|---|
| 149 | 
 | 
|---|
| 150 |         // check if all required config settings are set.
 | 
|---|
| 151 |         for ( c_iter i = values.begin(); i != values.end(); i++ ) {
 | 
|---|
| 152 |                 const config_entry &e = i->second;
 | 
|---|
| 153 | 
 | 
|---|
| 154 |                 if ( e.required && ! e.defined )
 | 
|---|
| 155 |                         throw config_error(
 | 
|---|
| 156 |                                 "key `" + e.key + "' required but not set");
 | 
|---|
| 157 |         }
 | 
|---|
| 158 | }
 | 
|---|
| 159 | 
 | 
|---|
| 160 | 
 | 
|---|
| 161 | /**
 | 
|---|
| 162 |  * Write the configuration data to a stream.
 | 
|---|
| 163 |  *
 | 
|---|
| 164 |  * If there is a write error, a config_error exception is thrown. This method
 | 
|---|
| 165 |  * doesn't close the stream after writing. It is up to the caller to do that.
 | 
|---|
| 166 |  *
 | 
|---|
| 167 |  * @param out the output stream to read data from
 | 
|---|
| 168 |  */
 | 
|---|
| 169 | void configuration::dump(std::ostream &out) throw (config_error) {
 | 
|---|
| 170 |         using namespace std;
 | 
|---|
| 171 | 
 | 
|---|
| 172 |         out << "# Configuration dump" << endl;
 | 
|---|
| 173 | 
 | 
|---|
| 174 |         for ( c_iter i = values.begin(); i != values.end(); i++ ) {
 | 
|---|
| 175 |                 const config_entry &e = i->second;
 | 
|---|
| 176 | 
 | 
|---|
| 177 |                 out << e.key << " = ";
 | 
|---|
| 178 | 
 | 
|---|
| 179 |                 if ( ! e.defined ) {
 | 
|---|
| 180 |                         out << endl;
 | 
|---|
| 181 |                         continue;
 | 
|---|
| 182 |                 }
 | 
|---|
| 183 | 
 | 
|---|
| 184 |                 switch ( e.type ) {
 | 
|---|
| 185 |                         case config_entry::T_BOOL:
 | 
|---|
| 186 |                                 out << e.bool_value << endl;
 | 
|---|
| 187 |                                 break;
 | 
|---|
| 188 |                         case config_entry::T_INT:
 | 
|---|
| 189 |                                 out << e.int_value << endl;
 | 
|---|
| 190 |                                 break;
 | 
|---|
| 191 |                         case config_entry::T_FLOAT:
 | 
|---|
| 192 |                                 out << e.float_value << endl;
 | 
|---|
| 193 |                                 break;
 | 
|---|
| 194 |                         case config_entry::T_STR:
 | 
|---|
| 195 |                                 write_string(out, e.str_value);
 | 
|---|
| 196 |                                 out << endl;
 | 
|---|
| 197 |                                 break;
 | 
|---|
| 198 |                         case config_entry::T_IPv4:
 | 
|---|
| 199 |                                 out << e.ipv4_value << endl;
 | 
|---|
| 200 |                                 break;
 | 
|---|
| 201 |                         case config_entry::T_IPv6:
 | 
|---|
| 202 |                                 out << e.ipv6_value << endl;
 | 
|---|
| 203 |                                 break;
 | 
|---|
| 204 |                         case config_entry::T_IPv4_LIST: // fall-through
 | 
|---|
| 205 |                         case config_entry::T_IPv6_LIST:
 | 
|---|
| 206 |                                 dump_address_list(out, e.address_list);
 | 
|---|
| 207 |                                 out << endl;
 | 
|---|
| 208 |                                 break;
 | 
|---|
| 209 |                         default:
 | 
|---|
| 210 |                                 assert( false );
 | 
|---|
| 211 |                 }
 | 
|---|
| 212 |         }
 | 
|---|
| 213 | }
 | 
|---|
| 214 | 
 | 
|---|
| 215 | 
 | 
|---|
| 216 | void configuration::dump_address_list(std::ostream &out,
 | 
|---|
| 217 |                 const std::list<hostaddress> &addresses) const {
 | 
|---|
| 218 | 
 | 
|---|
| 219 |         typedef std::list<hostaddress>::const_iterator addr_iter;
 | 
|---|
| 220 | 
 | 
|---|
| 221 |         for ( addr_iter i = addresses.begin(); i != addresses.end(); i++ )
 | 
|---|
| 222 |                 out << *i << " ";
 | 
|---|
| 223 | }
 | 
|---|
| 224 | 
 | 
|---|
| 225 | 
 | 
|---|
| 226 | /**
 | 
|---|
| 227 |  * Test if the configuration contains the given value.
 | 
|---|
| 228 |  *
 | 
|---|
| 229 |  * @param key the name of the key
 | 
|---|
| 230 |  * @return true, if the configuraiton has that key
 | 
|---|
| 231 |  */
 | 
|---|
| 232 | bool configuration::is_defined(const std::string &key) const throw () {
 | 
|---|
| 233 |         c_iter i = values.find(key);
 | 
|---|
| 234 | 
 | 
|---|
| 235 |         if ( values.find(key) == values.end() )
 | 
|---|
| 236 |                 return false;
 | 
|---|
| 237 |         else
 | 
|---|
| 238 |                 return i->second.defined;
 | 
|---|
| 239 | }
 | 
|---|
| 240 | 
 | 
|---|
| 241 | 
 | 
|---|
| 242 | /**
 | 
|---|
| 243 |  * Get a string configuration value.
 | 
|---|
| 244 |  *
 | 
|---|
| 245 |  * @param key the name of the key
 | 
|---|
| 246 |  * @return the value from the configuration
 | 
|---|
| 247 |  */
 | 
|---|
| 248 | std::string configuration::get_string(const std::string &key) const throw () {
 | 
|---|
| 249 |         c_iter i = values.find(key);
 | 
|---|
| 250 |         assert( i != values.end() );
 | 
|---|
| 251 |         assert( i->second.type == config_entry::T_STR );
 | 
|---|
| 252 | 
 | 
|---|
| 253 |         return i->second.str_value;
 | 
|---|
| 254 | }
 | 
|---|
| 255 | 
 | 
|---|
| 256 | 
 | 
|---|
| 257 | /**
 | 
|---|
| 258 |  * Get a boolean configuration value.
 | 
|---|
| 259 |  *
 | 
|---|
| 260 |  * @param key the name of the key
 | 
|---|
| 261 |  * @return the value from the configuration
 | 
|---|
| 262 |  */
 | 
|---|
| 263 | bool configuration::get_bool(const std::string &key) const throw () {
 | 
|---|
| 264 |         c_iter i = values.find(key);
 | 
|---|
| 265 |         assert( i != values.end() );
 | 
|---|
| 266 |         assert( i->second.type == config_entry::T_BOOL );
 | 
|---|
| 267 | 
 | 
|---|
| 268 |         return i->second.bool_value;
 | 
|---|
| 269 | }
 | 
|---|
| 270 | 
 | 
|---|
| 271 | 
 | 
|---|
| 272 | /**
 | 
|---|
| 273 |  * Get an integer configuration value.
 | 
|---|
| 274 |  *
 | 
|---|
| 275 |  * @param key the name of the key
 | 
|---|
| 276 |  * @return the value from the configuration
 | 
|---|
| 277 |  */
 | 
|---|
| 278 | int configuration::get_int(const std::string &key) const throw () {
 | 
|---|
| 279 |         c_iter i = values.find(key);
 | 
|---|
| 280 |         assert( i != values.end() );
 | 
|---|
| 281 |         assert( i->second.type == config_entry::T_INT );
 | 
|---|
| 282 | 
 | 
|---|
| 283 |         return i->second.int_value;
 | 
|---|
| 284 | }
 | 
|---|
| 285 | 
 | 
|---|
| 286 | 
 | 
|---|
| 287 | /**
 | 
|---|
| 288 |  * Get a floating point configuration value.
 | 
|---|
| 289 |  *
 | 
|---|
| 290 |  * @param key the name of the key
 | 
|---|
| 291 |  * @return the value from the configuration
 | 
|---|
| 292 |  */
 | 
|---|
| 293 | float configuration::get_float(const std::string &key) const throw () {
 | 
|---|
| 294 |         c_iter i = values.find(key);
 | 
|---|
| 295 |         assert( i != values.end() );
 | 
|---|
| 296 |         assert( i->second.type == config_entry::T_FLOAT );
 | 
|---|
| 297 | 
 | 
|---|
| 298 |         return i->second.float_value;
 | 
|---|
| 299 | }
 | 
|---|
| 300 | 
 | 
|---|
| 301 | 
 | 
|---|
| 302 | /**
 | 
|---|
| 303 |  * Get an IPv4 hostaddress configuration value.
 | 
|---|
| 304 |  *
 | 
|---|
| 305 |  * @param key the name of the key
 | 
|---|
| 306 |  * @return the value from the configuration
 | 
|---|
| 307 |  */
 | 
|---|
| 308 | hostaddress configuration::get_ipv4_address(
 | 
|---|
| 309 |                 const std::string &key) const throw () {
 | 
|---|
| 310 | 
 | 
|---|
| 311 |         c_iter i = values.find(key);
 | 
|---|
| 312 |         assert( i != values.end() );
 | 
|---|
| 313 |         assert( i->second.type == config_entry::T_IPv4 );
 | 
|---|
| 314 | 
 | 
|---|
| 315 |         return i->second.ipv4_value;
 | 
|---|
| 316 | }
 | 
|---|
| 317 | 
 | 
|---|
| 318 | 
 | 
|---|
| 319 | /**
 | 
|---|
| 320 |  * Get an IPv6 hostaddress configuration value.
 | 
|---|
| 321 |  *
 | 
|---|
| 322 |  * @param key the name of the key
 | 
|---|
| 323 |  * @return the value from the configuration
 | 
|---|
| 324 |  */
 | 
|---|
| 325 | hostaddress configuration::get_ipv6_address(
 | 
|---|
| 326 |                 const std::string &key) const throw () {
 | 
|---|
| 327 | 
 | 
|---|
| 328 |         c_iter i = values.find(key);
 | 
|---|
| 329 |         assert( i != values.end() );
 | 
|---|
| 330 |         assert( i->second.type == config_entry::T_IPv6 );
 | 
|---|
| 331 | 
 | 
|---|
| 332 |         return i->second.ipv6_value;
 | 
|---|
| 333 | }
 | 
|---|
| 334 | 
 | 
|---|
| 335 | 
 | 
|---|
| 336 | /**
 | 
|---|
| 337 |  * Get a list of IPv4 hostaddress objects.
 | 
|---|
| 338 |  *
 | 
|---|
| 339 |  * @param key the name of the key
 | 
|---|
| 340 |  * @return the list of values from the configuration
 | 
|---|
| 341 |  */
 | 
|---|
| 342 | std::list<hostaddress> configuration::get_ipv4_address_list(
 | 
|---|
| 343 |                 const std::string &key) const throw () {
 | 
|---|
| 344 | 
 | 
|---|
| 345 |         c_iter i = values.find(key);
 | 
|---|
| 346 |         assert( i != values.end() );
 | 
|---|
| 347 |         assert( i->second.type == config_entry::T_IPv4_LIST );
 | 
|---|
| 348 | 
 | 
|---|
| 349 |         return i->second.address_list;
 | 
|---|
| 350 | }
 | 
|---|
| 351 | 
 | 
|---|
| 352 | 
 | 
|---|
| 353 | /**
 | 
|---|
| 354 |  * Get a list of IPv6 hostaddress objects.
 | 
|---|
| 355 |  *
 | 
|---|
| 356 |  * @param key the name of the key
 | 
|---|
| 357 |  * @return the list of values from the configuration
 | 
|---|
| 358 |  */
 | 
|---|
| 359 | std::list<hostaddress> configuration::get_ipv6_address_list(
 | 
|---|
| 360 |                 const std::string &key) const throw () {
 | 
|---|
| 361 | 
 | 
|---|
| 362 |         c_iter i = values.find(key);
 | 
|---|
| 363 |         assert( i != values.end() );
 | 
|---|
| 364 |         assert( i->second.type == config_entry::T_IPv6_LIST );
 | 
|---|
| 365 | 
 | 
|---|
| 366 |         return i->second.address_list;
 | 
|---|
| 367 | }
 | 
|---|
| 368 | 
 | 
|---|
| 369 | 
 | 
|---|
| 370 | void configuration::strip_leading_space(std::istream &in) const {
 | 
|---|
| 371 |         while ( in && ( in.peek() == ' ' || in.peek() == '\t' ) )
 | 
|---|
| 372 |                 in.get();
 | 
|---|
| 373 | }
 | 
|---|
| 374 | 
 | 
|---|
| 375 | 
 | 
|---|
| 376 | // Parse the given buffer and assign the value to the config entry
 | 
|---|
| 377 | void configuration::parse_and_assign(const std::string &key, std::istream &in) {
 | 
|---|
| 378 | 
 | 
|---|
| 379 |         switch ( values[key].type ) {
 | 
|---|
| 380 |                 case config_entry::T_BOOL:
 | 
|---|
| 381 |                         values[key].bool_value = parse_bool(in);
 | 
|---|
| 382 |                         break;
 | 
|---|
| 383 |                 case config_entry::T_INT:
 | 
|---|
| 384 |                         values[key].int_value = parse_int(in);
 | 
|---|
| 385 |                         break;
 | 
|---|
| 386 |                 case config_entry::T_FLOAT:
 | 
|---|
| 387 |                         values[key].float_value = parse_float(in);
 | 
|---|
| 388 |                         break;
 | 
|---|
| 389 |                 case config_entry::T_STR:
 | 
|---|
| 390 |                         values[key].str_value = parse_string(in);
 | 
|---|
| 391 |                         break;
 | 
|---|
| 392 |                 case config_entry::T_IPv4:
 | 
|---|
| 393 |                         values[key].ipv4_value = parse_ipv4_address(in);
 | 
|---|
| 394 |                         break;
 | 
|---|
| 395 |                 case config_entry::T_IPv6:
 | 
|---|
| 396 |                         values[key].ipv6_value = parse_ipv6_address(in);
 | 
|---|
| 397 |                         break;
 | 
|---|
| 398 |                 case config_entry::T_IPv4_LIST:
 | 
|---|
| 399 |                         values[key].address_list = parse_ipv4_address_list(in);
 | 
|---|
| 400 |                         break;
 | 
|---|
| 401 |                 case config_entry::T_IPv6_LIST:
 | 
|---|
| 402 |                         values[key].address_list = parse_ipv6_address_list(in);
 | 
|---|
| 403 |                         break;
 | 
|---|
| 404 |                 default:
 | 
|---|
| 405 |                         assert( false );
 | 
|---|
| 406 |                         throw parse_error("invalid value"); // not reached
 | 
|---|
| 407 |         }
 | 
|---|
| 408 | 
 | 
|---|
| 409 |         // no exception thrown until now, so parsing was successful
 | 
|---|
| 410 |         values[key].defined = true;
 | 
|---|
| 411 | }
 | 
|---|
| 412 | 
 | 
|---|
| 413 | 
 | 
|---|
| 414 | // Write the string to the stream, adding quotation marks and escape sequences
 | 
|---|
| 415 | void configuration::write_string(
 | 
|---|
| 416 |                 std::ostream &out, const std::string &str) const {
 | 
|---|
| 417 | 
 | 
|---|
| 418 |         std::stringstream stream(str);
 | 
|---|
| 419 | 
 | 
|---|
| 420 |         out << '"';
 | 
|---|
| 421 | 
 | 
|---|
| 422 |         char c;
 | 
|---|
| 423 |         while ( stream.get(c) ) {
 | 
|---|
| 424 |                 switch ( c ) {
 | 
|---|
| 425 |                         case '\\':      // fallthrough
 | 
|---|
| 426 |                         case '"':       out << '\\' << c; break;
 | 
|---|
| 427 |                         default:        out << c;
 | 
|---|
| 428 |                 }
 | 
|---|
| 429 |         }
 | 
|---|
| 430 | 
 | 
|---|
| 431 |         out << '"';
 | 
|---|
| 432 | }
 | 
|---|
| 433 | 
 | 
|---|
| 434 | 
 | 
|---|
| 435 | // Matches pattern "[^"]*"\s*
 | 
|---|
| 436 | std::string configuration::parse_string(std::istream &in) const {
 | 
|---|
| 437 | 
 | 
|---|
| 438 |         if ( in.get() != '"' )
 | 
|---|
| 439 |                 throw parse_error("string doesn't start with a quotation mark");
 | 
|---|
| 440 | 
 | 
|---|
| 441 |         bool escape = false;
 | 
|---|
| 442 |         std::string tmp;
 | 
|---|
| 443 |         char c;
 | 
|---|
| 444 | 
 | 
|---|
| 445 |         while ( in.get(c) ) {
 | 
|---|
| 446 |                 if ( escape ) {
 | 
|---|
| 447 |                         if ( c == '\\' || c == '"' )
 | 
|---|
| 448 |                                 tmp += c;
 | 
|---|
| 449 |                         else
 | 
|---|
| 450 |                                 throw parse_error("invalid escape sequence");
 | 
|---|
| 451 | 
 | 
|---|
| 452 |                         escape = false;
 | 
|---|
| 453 |                 }
 | 
|---|
| 454 |                 else {
 | 
|---|
| 455 |                         if ( c == '"' )
 | 
|---|
| 456 |                                 break;
 | 
|---|
| 457 |                         else if ( c == '\\' )
 | 
|---|
| 458 |                                 escape = true;
 | 
|---|
| 459 |                         else
 | 
|---|
| 460 |                                 tmp += c;
 | 
|---|
| 461 |                 }
 | 
|---|
| 462 |         }
 | 
|---|
| 463 | 
 | 
|---|
| 464 |         // we should be on the closing quotation mark
 | 
|---|
| 465 |         if ( c != '"' )
 | 
|---|
| 466 |                 throw parse_error("unterminated string");
 | 
|---|
| 467 | 
 | 
|---|
| 468 |         skip_rest_of_line(in);
 | 
|---|
| 469 | 
 | 
|---|
| 470 |         return tmp;
 | 
|---|
| 471 | }
 | 
|---|
| 472 | 
 | 
|---|
| 473 | 
 | 
|---|
| 474 | hostaddress configuration::parse_ipv4_address(std::istream &in) const {
 | 
|---|
| 475 |         std::string word;
 | 
|---|
| 476 |         in >> word;
 | 
|---|
| 477 | 
 | 
|---|
| 478 |         bool success;
 | 
|---|
| 479 |         hostaddress addr(word.c_str(), &success);
 | 
|---|
| 480 | 
 | 
|---|
| 481 |         if ( success || ! addr.is_ipv4() )
 | 
|---|
| 482 |                 return addr;
 | 
|---|
| 483 |         else
 | 
|---|
| 484 |                 throw parse_error("invalid IPv4 address");
 | 
|---|
| 485 | 
 | 
|---|
| 486 |         skip_rest_of_line(in);
 | 
|---|
| 487 | 
 | 
|---|
| 488 |         return addr;
 | 
|---|
| 489 | }
 | 
|---|
| 490 | 
 | 
|---|
| 491 | 
 | 
|---|
| 492 | hostaddress configuration::parse_ipv6_address(std::istream &in) const {
 | 
|---|
| 493 |         std::string word;
 | 
|---|
| 494 |         in >> word;
 | 
|---|
| 495 | 
 | 
|---|
| 496 |         bool success;
 | 
|---|
| 497 |         hostaddress addr(word.c_str(), &success);
 | 
|---|
| 498 | 
 | 
|---|
| 499 |         if ( success || ! addr.is_ipv6() )
 | 
|---|
| 500 |                 return addr;
 | 
|---|
| 501 |         else
 | 
|---|
| 502 |                 throw parse_error("invalid IPv6 address");
 | 
|---|
| 503 | 
 | 
|---|
| 504 |         skip_rest_of_line(in);
 | 
|---|
| 505 | 
 | 
|---|
| 506 |         return addr;
 | 
|---|
| 507 | }
 | 
|---|
| 508 | 
 | 
|---|
| 509 | std::list<hostaddress> configuration::parse_ipv4_address_list(
 | 
|---|
| 510 |                 std::istream &in) const {
 | 
|---|
| 511 | 
 | 
|---|
| 512 |         std::list<hostaddress> result;
 | 
|---|
| 513 | 
 | 
|---|
| 514 |         std::string tmp;
 | 
|---|
| 515 |         while ( in >> tmp ) {
 | 
|---|
| 516 |                 bool success;
 | 
|---|
| 517 |                 hostaddress addr(tmp.c_str(), &success);
 | 
|---|
| 518 | 
 | 
|---|
| 519 |                 if ( success && addr.is_ipv4() )
 | 
|---|
| 520 |                         result.push_back(addr);
 | 
|---|
| 521 |                 else
 | 
|---|
| 522 |                         throw parse_error("invalid IPv4 address `" + tmp + "'");
 | 
|---|
| 523 |         }
 | 
|---|
| 524 | 
 | 
|---|
| 525 |         return result;
 | 
|---|
| 526 | }
 | 
|---|
| 527 | 
 | 
|---|
| 528 | std::list<hostaddress> configuration::parse_ipv6_address_list(
 | 
|---|
| 529 |                 std::istream &in) const {
 | 
|---|
| 530 | 
 | 
|---|
| 531 |         std::list<hostaddress> result;
 | 
|---|
| 532 | 
 | 
|---|
| 533 |         std::string tmp;
 | 
|---|
| 534 |         while ( in >> tmp ) {
 | 
|---|
| 535 |                 bool success;
 | 
|---|
| 536 |                 hostaddress addr(tmp.c_str(), &success);
 | 
|---|
| 537 | 
 | 
|---|
| 538 |                 if ( success && addr.is_ipv6() )
 | 
|---|
| 539 |                         result.push_back(addr);
 | 
|---|
| 540 |                 else
 | 
|---|
| 541 |                         throw parse_error("invalid IPv6 address `" + tmp + "'");
 | 
|---|
| 542 |         }
 | 
|---|
| 543 | 
 | 
|---|
| 544 |         return result;
 | 
|---|
| 545 | }
 | 
|---|
| 546 | 
 | 
|---|
| 547 | int configuration::parse_int(std::istream &in) const {
 | 
|---|
| 548 |         int tmp = -1;
 | 
|---|
| 549 | 
 | 
|---|
| 550 |         in >> tmp;
 | 
|---|
| 551 | 
 | 
|---|
| 552 |         if ( ! in.good() && ! in.eof() )
 | 
|---|
| 553 |                 throw parse_error("parsing integer failed");
 | 
|---|
| 554 | 
 | 
|---|
| 555 |         skip_rest_of_line(in);
 | 
|---|
| 556 | 
 | 
|---|
| 557 |         return tmp;
 | 
|---|
| 558 | }
 | 
|---|
| 559 | 
 | 
|---|
| 560 | 
 | 
|---|
| 561 | float configuration::parse_float(std::istream &in) const {
 | 
|---|
| 562 |         float tmp = 0.0;
 | 
|---|
| 563 | 
 | 
|---|
| 564 |         in >> tmp;
 | 
|---|
| 565 | 
 | 
|---|
| 566 |         if ( ! in.good() && ! in.eof() )
 | 
|---|
| 567 |                 throw parse_error("parsing float failed");
 | 
|---|
| 568 | 
 | 
|---|
| 569 |         skip_rest_of_line(in);
 | 
|---|
| 570 | 
 | 
|---|
| 571 |         return tmp;
 | 
|---|
| 572 | }
 | 
|---|
| 573 | 
 | 
|---|
| 574 | 
 | 
|---|
| 575 | bool configuration::parse_bool(std::istream &in) const {
 | 
|---|
| 576 |         std::string tmp;
 | 
|---|
| 577 |         bool value;
 | 
|---|
| 578 | 
 | 
|---|
| 579 |         in >> tmp;
 | 
|---|
| 580 | 
 | 
|---|
| 581 |         if ( tmp == "true" )
 | 
|---|
| 582 |                 value = true;
 | 
|---|
| 583 |         else if ( tmp == "false" )
 | 
|---|
| 584 |                 value = false;
 | 
|---|
| 585 |         else
 | 
|---|
| 586 |                 throw parse_error("parsing boolean failed");
 | 
|---|
| 587 | 
 | 
|---|
| 588 |         skip_rest_of_line(in);
 | 
|---|
| 589 | 
 | 
|---|
| 590 |         return value;
 | 
|---|
| 591 | }
 | 
|---|
| 592 | 
 | 
|---|
| 593 | 
 | 
|---|
| 594 | // throw an exception if the rest of the line doesn't only contain whitespace
 | 
|---|
| 595 | void configuration::skip_rest_of_line(std::istream &in) const {
 | 
|---|
| 596 |         char c;
 | 
|---|
| 597 |         while ( in.get(c) ) {
 | 
|---|
| 598 |                 if ( c != ' ' && c != '\t' )
 | 
|---|
| 599 |                         throw parse_error("junk after value");
 | 
|---|
| 600 |         }
 | 
|---|
| 601 | }
 | 
|---|
| 602 | 
 | 
|---|
| 603 | 
 | 
|---|
| 604 | // EOF
 | 
|---|