OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
BESKeys.cc
Go to the documentation of this file.
1 // BESKeys.cc
2 
3 // This file is part of bes, A C++ back-end server implementation framework
4 // for the OPeNDAP Data Access Protocol.
5 
6 // Copyright (c) 2004-2009 University Corporation for Atmospheric Research
7 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 //
23 // You can contact University Corporation for Atmospheric Research at
24 // 3080 Center Green Drive, Boulder, CO 80301
25 
26 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
27 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
28 //
29 // Authors:
30 // pwest Patrick West <pwest@ucar.edu>
31 // jgarcia Jose Garcia <jgarcia@ucar.edu>
32 
33 #include "config.h"
34 
35 #ifdef __cplusplus
36 extern "C" {
37 #include <sys/types.h>
38 #include "regex.h"
39 }
40 #endif
41 
42 #include <cerrno>
43 #include <cstring>
44 
45 #if HAVE_UNISTD_H
46 #include <unistd.h>
47 #endif
48 
49 #include "BESKeys.h"
50 #include "BESUtil.h"
51 #include "BESFSDir.h"
52 #include "BESFSFile.h"
53 #include "BESInternalFatalError.h"
54 #include "BESSyntaxUserError.h"
55 
56 #define BES_INCLUDE_KEY "BES.Include"
57 
58 vector<string> BESKeys::KeyList ;
59 
76 BESKeys::BESKeys( const string &keys_file_name )
77  : _keys_file( 0 ),
78  _keys_file_name( keys_file_name ),
79  _the_keys( 0 ),
80  _own_keys( true )
81 {
82  _the_keys = new map<string,vector<string> >;
83  initialize_keys( ) ;
84 }
85 
86 BESKeys::BESKeys( const string &keys_file_name, map<string,vector<string> > *keys)
87  : _keys_file( 0 ),
88  _keys_file_name( keys_file_name ),
89  _the_keys( keys ),
90  _own_keys( false )
91 {
92  initialize_keys( ) ;
93 }
94 
98 {
99  clean() ;
100 }
101 
102 void
103 BESKeys::initialize_keys( )
104 {
105  _keys_file = new ifstream( _keys_file_name.c_str() ) ;
106  int myerrno = errno ;
107  if( !(*_keys_file) )
108  {
109  char path[500] ;
110  getcwd( path, sizeof( path ) ) ;
111  string s = string("BES: fatal, cannot open BES configuration file ")
112  + _keys_file_name + ": " ;
113  char *err = strerror( myerrno ) ;
114  if( err )
115  s += err ;
116  else
117  s += "Unknown error" ;
118 
119  s += (string)".\n" + "The current working directory is " + path + "\n" ;
120  throw BESInternalFatalError( s, __FILE__, __LINE__ ) ;
121  }
122 
123  try
124  {
125  load_keys() ;
126  }
127  catch( BESError &e )
128  {
129  // be sure we're throwing a fatal error, since the BES can't run
130  // within the configuration file
131  clean() ;
133  e.get_file(), e.get_line() ) ;
134  }
135  catch(...)
136  {
137  clean() ;
138  string s = (string)"Undefined exception while trying to load keys "
139  + "from bes configuration file " + _keys_file_name ;
140  throw BESInternalFatalError( s, __FILE__, __LINE__ ) ;
141  }
142 }
143 
144 void
145 BESKeys::clean()
146 {
147  if( _keys_file )
148  {
149  _keys_file->close() ;
150  delete _keys_file ;
151  }
152  if( _the_keys && _own_keys )
153  {
154  delete _the_keys ;
155  }
156 }
157 
158 /* @brief Determine if the specified key file has been loaded yet
159  *
160  * Given the name of the key file, determine if it has already been
161  * loaded. More specifically, if started to load the file.
162  *
163  * @returns true if already started to load, false otherwise
164  */
165 bool
166 BESKeys::LoadedKeys( const string &key_file )
167 {
168  vector<string>::const_iterator i = BESKeys::KeyList.begin() ;
169  vector<string>::const_iterator e = BESKeys::KeyList.end() ;
170  for( ; i != e; i++ )
171  {
172  if( (*i) == key_file )
173  {
174  return true ;
175  }
176  }
177  return false ;
178 }
179 
180 void
181 BESKeys::load_keys()
182 {
183  char buffer[255];
184  string key,value;
185  while(!(*_keys_file).eof())
186  {
187  if((*_keys_file).getline(buffer,255))
188  {
189  bool addto = false ;
190  if( break_pair( buffer, key, value, addto ) )
191  {
192  if( key == BES_INCLUDE_KEY )
193  {
194  load_include_files( value ) ;
195  }
196  else
197  {
198  set_key( key, value, addto ) ;
199  }
200  }
201  }
202  }
203 }
204 
205 // The string contained in the character buffer b should be of the
206 // format key=value or key+=value. The pair is broken apart, storing the
207 // key in the key parameter and the value of the key in the value
208 // parameter. If += is used, then the value should be added to the value
209 // of key, not replacing.
210 //
211 // It used to be that we would validate the key=value line. Instead,
212 // anything after the equal sign is considered the value of the key.
213 inline bool
214 BESKeys::break_pair( const char* b, string& key, string &value, bool &addto )
215 {
216  addto = false ;
217  // Ignore comments and lines with only spaces
218  if( b && ( b[0] != '#' ) && ( !only_blanks( b ) ) )
219  {
220  register size_t l = strlen( b ) ;
221  if( l > 1 )
222  {
223  int pos = 0 ;
224  bool done = false ;
225  for( register size_t j = 0; j < l && !done ; j++ )
226  {
227  if( b[j] == '=' )
228  {
229  if( !addto ) pos = j ;
230  else
231  {
232  if( pos != static_cast<int>(j-1) )
233  {
234  string s = string("BES: Invalid entry ") + b
235  + " in configuration file "
236  + _keys_file_name
237  + " '+' character found in variable name"
238  + " or attempting '+=' with space"
239  + " between the characters.\n" ;
240  throw BESInternalFatalError( s, __FILE__, __LINE__);
241  }
242  }
243  done = true ;
244  }
245  else if( b[j] == '+' )
246  {
247  addto = true ;
248  pos = j ;
249  }
250  }
251  if( !done )
252  {
253  string s = string("BES: Invalid entry ") + b
254  + " in configuration file "
255  + _keys_file_name + ": "
256  + " '=' character not found.\n" ;
257  throw BESInternalFatalError( s, __FILE__, __LINE__);
258  }
259 
260  string s = b ;
261  key = s.substr( 0, pos ) ;
263  if( addto ) value = s.substr( pos+2, s.size() ) ;
264  else value = s.substr( pos+1, s.size() ) ;
266 
267  return true;
268  }
269 
270  return false;
271  }
272 
273  return false;
274 }
275 
285 void
286 BESKeys::load_include_files( const string &files )
287 {
288  string newdir ;
289  BESFSFile allfiles( files ) ;
290 
291  // If the files specified begin with a /, then use that directory
292  // instead of the current keys file directory.
293  if( !files.empty() && files[0] == '/' )
294  {
295  newdir = allfiles.getDirName() ;
296  }
297  else
298  {
299  // determine the directory of the current keys file. All included
300  // files will be relative to this file.
301  BESFSFile currfile( _keys_file_name ) ;
302  string currdir = currfile.getDirName() ;
303 
304  string alldir = allfiles.getDirName() ;
305 
306  if( ( currdir == "./" || currdir == "." )
307  && ( alldir == "./" || alldir == "." ) )
308  {
309  newdir = "./" ;
310  }
311  else
312  {
313  if( alldir == "./" || alldir == "." )
314  {
315  newdir = currdir ;
316  }
317  else
318  {
319  newdir = currdir + "/" + alldir ;
320  }
321  }
322  }
323 
324  // load the files one at a time. If the directory doesn't exist,
325  // then don't load any configuration files
326  BESFSDir fsd( newdir, allfiles.getFileName() ) ;
327  BESFSDir::fileIterator i = fsd.beginOfFileList() ;
328  BESFSDir::fileIterator e = fsd.endOfFileList() ;
329  for( ; i != e; i++ )
330  {
331  load_include_file( (*i).getFullPath() ) ;
332  }
333 }
334 
341 void
342 BESKeys::load_include_file( const string &file )
343 {
344  // make sure the file exists and is readable
345  // throws exception if unable to read
346  // not loaded if has already be started to be loaded
347  if( !BESKeys::LoadedKeys( file ) )
348  {
349  BESKeys::KeyList.push_back( file ) ;
350  BESKeys tmp( file, _the_keys ) ;
351  }
352 }
353 
354 bool
355 BESKeys::only_blanks(const char *line)
356 {
357  int val;
358  regex_t rx;
359  string expr = "[^[:space:]]" ;
360  val = regcomp( &rx, expr.c_str(), REG_ICASE ) ;
361 
362  if( val != 0 )
363  {
364  string s = (string)"Regular expression " + expr
365  + " did not compile correctly "
366  + " in configuration file " + _keys_file_name ;
367  throw BESInternalFatalError( s, __FILE__, __LINE__ ) ;
368  }
369  val = regexec( &rx, line, 0, 0, REG_NOTBOL ) ;
370  if( val == 0 )
371  {
372  regfree( &rx ) ;
373  return false ;
374  }
375  else
376  {
377  if( val == REG_NOMATCH )
378  {
379  regfree( &rx ) ;
380  return true ;
381  }
382  else if( val == REG_ESPACE )
383  {
384  string s = (string)"Execution of regular expression out of space"
385  + " in configuration file " + _keys_file_name ;
386  throw BESInternalFatalError( s, __FILE__, __LINE__ ) ;
387  }
388  else
389  {
390  string s = (string)"Execution of regular expression has unknown "
391  + " problem in configuration file " + _keys_file_name ;
392  throw BESInternalFatalError( s, __FILE__, __LINE__ ) ;
393  }
394  }
395 }
396 
413 void
414 BESKeys::set_key( const string &key, const string &val, bool addto )
415 {
416  map<string,vector<string> >::iterator i ;
417  i = _the_keys->find( key ) ;
418  if( i == _the_keys->end() )
419  {
420  vector<string> vals ;
421  (*_the_keys)[key] = vals ;
422  }
423  if( !addto ) (*_the_keys)[key].clear() ;
424  if( !val.empty() )
425  {
426  (*_the_keys)[key].push_back( val ) ;
427  }
428 }
429 
441 void
442 BESKeys::set_key( const string &pair )
443 {
444  string key ;
445  string val ;
446  bool addto = false ;
447  break_pair( pair.c_str(), key, val, addto ) ;
448  set_key( key, val, addto ) ;
449 }
450 
465 void
466 BESKeys::get_value( const string& s, string &val, bool &found )
467 {
468  found = false ;
469  map<string,vector<string> >::iterator i ;
470  i = _the_keys->find( s ) ;
471  if( i != _the_keys->end() )
472  {
473  found = true ;
474  if( (*i).second.size() > 1 )
475  {
476  string err = string("Multiple values for the key ") + s
477  + " found, should only be one." ;
478  throw BESSyntaxUserError( err, __FILE__, __LINE__ ) ;
479  }
480  if( (*i).second.size() == 1 )
481  {
482  val = (*i).second[0] ;
483  }
484  else
485  {
486  val = "" ;
487  }
488  }
489 }
490 
502 void
503 BESKeys::get_values( const string& s, vector<string> &vals, bool &found )
504 {
505  found = false ;
506  map<string,vector<string> >::iterator i ;
507  i = _the_keys->find( s ) ;
508  if( i != _the_keys->end() )
509  {
510  found = true ;
511  vals = (*i).second ;
512  }
513 }
514 
521 void
522 BESKeys::dump( ostream &strm ) const
523 {
524  strm << BESIndent::LMarg << "BESKeys::dump - ("
525  << (void *)this << ")" << endl ;
527  strm << BESIndent::LMarg << "key file:" << _keys_file_name << endl ;
528  if( _keys_file && *_keys_file )
529  {
530  strm << BESIndent::LMarg << "key file is valid" << endl ;
531  }
532  else
533  {
534  strm << BESIndent::LMarg << "key file is NOT valid" << endl ;
535  }
536  if( _the_keys && _the_keys->size() )
537  {
538  strm << BESIndent::LMarg << " keys:" << endl ;
540  Keys_citer i = _the_keys->begin() ;
541  Keys_citer ie = _the_keys->end() ;
542  for( ; i != ie; i++ )
543  {
544  strm << BESIndent::LMarg << (*i).first << ":" << endl ;
546  vector<string>::const_iterator v = (*i).second.begin() ;
547  vector<string>::const_iterator ve = (*i).second.end() ;
548  for( ; v != ve; v++ )
549  {
550  strm << (*v) << endl ;
551  }
553  }
555  }
556  else
557  {
558  strm << BESIndent::LMarg << "keys: none" << endl ;
559  }
561 }
562