OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
BESKeys.cc
Go to the documentation of this file.
00001 // BESKeys.cc
00002 
00003 // This file is part of bes, A C++ back-end server implementation framework
00004 // for the OPeNDAP Data Access Protocol.
00005 
00006 // Copyright (c) 2004-2009 University Corporation for Atmospheric Research
00007 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
00008 //
00009 // This library is free software; you can redistribute it and/or
00010 // modify it under the terms of the GNU Lesser General Public
00011 // License as published by the Free Software Foundation; either
00012 // version 2.1 of the License, or (at your option) any later version.
00013 // 
00014 // This library is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017 // Lesser General Public License for more details.
00018 // 
00019 // You should have received a copy of the GNU Lesser General Public
00020 // License along with this library; if not, write to the Free Software
00021 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 //
00023 // You can contact University Corporation for Atmospheric Research at
00024 // 3080 Center Green Drive, Boulder, CO 80301
00025  
00026 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
00027 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
00028 //
00029 // Authors:
00030 //      pwest       Patrick West <pwest@ucar.edu>
00031 //      jgarcia     Jose Garcia <jgarcia@ucar.edu>
00032 
00033 #include "config.h"
00034 
00035 #ifdef __cplusplus
00036 extern "C" {
00037 #include <sys/types.h>
00038 #include "regex.h"
00039 }
00040 #endif
00041 
00042 #include <cerrno>
00043 #include <cstring>
00044 
00045 #if HAVE_UNISTD_H
00046 #include <unistd.h>
00047 #endif
00048 
00049 #include "BESKeys.h"
00050 #include "BESUtil.h"
00051 #include "BESFSDir.h"
00052 #include "BESFSFile.h"
00053 #include "BESInternalFatalError.h"
00054 #include "BESSyntaxUserError.h"
00055 
00056 #define BES_INCLUDE_KEY "BES.Include"
00057 
00058 vector<string> BESKeys::KeyList ;
00059 
00076 BESKeys::BESKeys( const string &keys_file_name )
00077     : _keys_file( 0 ),
00078       _keys_file_name( keys_file_name ),
00079       _the_keys( 0 ),
00080       _own_keys( true )
00081 {
00082     _the_keys = new map<string,vector<string> >;
00083     initialize_keys( ) ;
00084 }
00085 
00086 BESKeys::BESKeys( const string &keys_file_name, map<string,vector<string> > *keys)
00087     : _keys_file( 0 ),
00088       _keys_file_name( keys_file_name ),
00089       _the_keys( keys ),
00090       _own_keys( false )
00091 {
00092     initialize_keys( ) ;
00093 }
00094 
00097 BESKeys::~BESKeys()
00098 {
00099     clean() ;
00100 }
00101 
00102 void
00103 BESKeys::initialize_keys( )
00104 {
00105     _keys_file = new ifstream( _keys_file_name.c_str() ) ;
00106     int myerrno = errno ;
00107     if( !(*_keys_file) )
00108     {
00109         char path[500] ;
00110         getcwd( path, sizeof( path ) ) ;
00111         string s = string("BES: fatal, cannot open BES configuration file ")
00112                    + _keys_file_name + ": " ;
00113         char *err = strerror( myerrno ) ;
00114         if( err )
00115             s += err ;
00116         else
00117             s += "Unknown error" ;
00118 
00119         s += (string)".\n" + "The current working directory is " + path + "\n" ;
00120         throw BESInternalFatalError( s, __FILE__, __LINE__ ) ;
00121     }
00122 
00123     try
00124     {
00125         load_keys() ;
00126     }
00127     catch( BESError &e )
00128     {
00129         // be sure we're throwing a fatal error, since the BES can't run
00130         // within the configuration file
00131         clean() ;
00132         throw BESInternalFatalError( e.get_message(),
00133                                      e.get_file(), e.get_line() ) ;
00134     }
00135     catch(...)
00136     {
00137         clean() ;
00138         string s = (string)"Undefined exception while trying to load keys "
00139                    + "from bes configuration file " + _keys_file_name ;
00140         throw BESInternalFatalError( s, __FILE__, __LINE__ ) ;
00141     }
00142 }
00143 
00144 void
00145 BESKeys::clean()
00146 {
00147     if( _keys_file )
00148     {
00149         _keys_file->close() ;
00150         delete _keys_file ;
00151     }
00152     if( _the_keys && _own_keys )
00153     {
00154         delete _the_keys ;
00155     }
00156 }
00157 
00158 /* @brief Determine if the specified key file has been loaded yet
00159  *
00160  * Given the name of the key file, determine if it has already been
00161  * loaded. More specifically, if started to load the file.
00162  *
00163  * @returns true if already started to load, false otherwise
00164  */
00165 bool
00166 BESKeys::LoadedKeys( const string &key_file )
00167 {
00168     vector<string>::const_iterator i = BESKeys::KeyList.begin() ;
00169     vector<string>::const_iterator e = BESKeys::KeyList.end() ;
00170     for( ; i != e; i++ )
00171     {
00172         if( (*i) == key_file )
00173         {
00174             return true ;
00175         }
00176     }
00177     return false ;
00178 }
00179 
00180 void
00181 BESKeys::load_keys()
00182 {
00183     char buffer[255];
00184     string key,value;
00185     while(!(*_keys_file).eof())
00186     {
00187         if((*_keys_file).getline(buffer,255))
00188         {
00189             bool addto = false ;
00190             if( break_pair( buffer, key, value, addto ) )
00191             {
00192                 if( key == BES_INCLUDE_KEY )
00193                 {
00194                     load_include_files( value ) ;
00195                 }
00196                 else
00197                 {
00198                     set_key( key, value, addto ) ;
00199                 }
00200             }
00201         }
00202     }
00203 }
00204 
00205 // The string contained in the character buffer b should be of the
00206 // format key=value or key+=value. The pair is broken apart, storing the
00207 // key in the key parameter and the value of the key in the value
00208 // parameter. If += is used, then the value should be added to the value
00209 // of key, not replacing.
00210 //
00211 // It used to be that we would validate the key=value line. Instead,
00212 // anything after the equal sign is considered the value of the key.
00213 inline bool
00214 BESKeys::break_pair( const char* b, string& key, string &value, bool &addto )
00215 {
00216     addto = false ;
00217     // Ignore comments and lines with only spaces
00218     if( b && ( b[0] != '#' ) && ( !only_blanks( b ) ) )
00219     {
00220         register size_t l = strlen( b ) ;
00221         if( l > 1 )
00222         {
00223             int pos = 0 ;
00224             bool done = false ;
00225             for( register size_t j = 0; j < l && !done ; j++ )
00226             {
00227                 if( b[j] == '=' )
00228                 {
00229                     if( !addto ) pos = j ;
00230                     else
00231                     {
00232                         if( pos != static_cast<int>(j-1) )
00233                         {
00234                             string s = string("BES: Invalid entry ") + b
00235                                        + " in configuration file "
00236                                        + _keys_file_name
00237                                        + " '+' character found in variable name"
00238                                        + " or attempting '+=' with space"
00239                                        + " between the characters.\n" ;
00240                             throw BESInternalFatalError( s, __FILE__, __LINE__);
00241                         }
00242                     }
00243                     done = true ;
00244                 }
00245                 else if( b[j] == '+' )
00246                 {
00247                     addto = true ;
00248                     pos = j ;
00249                 }
00250             }
00251             if( !done )
00252             {
00253                 string s = string("BES: Invalid entry ") + b
00254                            + " in configuration file "
00255                            + _keys_file_name + ": "
00256                            + " '=' character not found.\n" ;
00257                 throw BESInternalFatalError( s, __FILE__, __LINE__);
00258             }
00259 
00260             string s = b ;
00261             key = s.substr( 0, pos ) ;
00262             BESUtil::removeLeadingAndTrailingBlanks( key ) ;
00263             if( addto ) value = s.substr( pos+2, s.size() ) ;
00264             else value = s.substr( pos+1, s.size() ) ;
00265             BESUtil::removeLeadingAndTrailingBlanks( value ) ;
00266 
00267             return true;
00268         }
00269 
00270         return false;
00271     }
00272 
00273     return false;
00274 }
00275 
00285 void
00286 BESKeys::load_include_files( const string &files )
00287 {
00288     string newdir ;
00289     BESFSFile allfiles( files ) ;
00290 
00291     // If the files specified begin with a /, then use that directory
00292     // instead of the current keys file directory.
00293     if( !files.empty() && files[0] == '/' )
00294     {
00295         newdir = allfiles.getDirName() ;
00296     }
00297     else
00298     {
00299         // determine the directory of the current keys file. All included
00300         // files will be relative to this file.
00301         BESFSFile currfile( _keys_file_name ) ;
00302         string currdir = currfile.getDirName() ;
00303 
00304         string alldir = allfiles.getDirName() ;
00305 
00306         if( ( currdir == "./" || currdir == "." )
00307             && ( alldir == "./" || alldir == "." ) )
00308         {
00309             newdir = "./" ;
00310         }
00311         else
00312         {
00313             if( alldir == "./" || alldir == "." )
00314             {
00315                 newdir = currdir ;
00316             }
00317             else
00318             {
00319                 newdir = currdir + "/" + alldir ;
00320             }
00321         }
00322     }
00323 
00324     // load the files one at a time. If the directory doesn't exist,
00325     // then don't load any configuration files
00326     BESFSDir fsd( newdir, allfiles.getFileName() ) ;
00327     BESFSDir::fileIterator i = fsd.beginOfFileList() ;
00328     BESFSDir::fileIterator e = fsd.endOfFileList() ;
00329     for( ; i != e; i++ )
00330     {
00331         load_include_file( (*i).getFullPath() ) ;
00332     }
00333 }
00334 
00341 void
00342 BESKeys::load_include_file( const string &file )
00343 {
00344     // make sure the file exists and is readable
00345     // throws exception if unable to read
00346     // not loaded if has already be started to be loaded
00347     if( !BESKeys::LoadedKeys( file ) )
00348     {
00349         BESKeys::KeyList.push_back( file ) ;
00350         BESKeys tmp( file, _the_keys ) ;
00351     }
00352 }
00353 
00354 bool
00355 BESKeys::only_blanks(const char *line)
00356 {
00357     int val;
00358     regex_t rx;
00359     string expr = "[^[:space:]]" ;
00360     val = regcomp( &rx, expr.c_str(), REG_ICASE ) ;
00361 
00362     if( val != 0 )
00363     {
00364         string s = (string)"Regular expression " + expr
00365                    + " did not compile correctly "
00366                    + " in configuration file " + _keys_file_name ;
00367         throw BESInternalFatalError( s, __FILE__, __LINE__ ) ;
00368     }
00369     val = regexec( &rx, line, 0, 0, REG_NOTBOL ) ;
00370     if( val == 0 )
00371     {
00372         regfree( &rx ) ;
00373         return false ;
00374     }
00375     else
00376     {
00377         if( val == REG_NOMATCH )
00378         {
00379             regfree( &rx ) ;
00380             return true ;
00381         }
00382         else if( val == REG_ESPACE )
00383         {
00384             string s = (string)"Execution of regular expression out of space"
00385                        + " in configuration file " + _keys_file_name ;
00386             throw BESInternalFatalError( s, __FILE__, __LINE__ ) ;
00387         }
00388         else
00389         {
00390             string s = (string)"Execution of regular expression has unknown "
00391                        + " problem in configuration file " + _keys_file_name ;
00392             throw BESInternalFatalError( s, __FILE__, __LINE__ ) ;
00393         }
00394     }
00395 }
00396 
00413 void
00414 BESKeys::set_key( const string &key, const string &val, bool addto )
00415 {
00416     map<string,vector<string> >::iterator i ;
00417     i = _the_keys->find( key ) ;
00418     if( i == _the_keys->end() )
00419     {
00420         vector<string> vals ;
00421         (*_the_keys)[key] = vals ;
00422     }
00423     if( !addto ) (*_the_keys)[key].clear() ;
00424     if( !val.empty() )
00425     {
00426         (*_the_keys)[key].push_back( val ) ;
00427     }
00428 }
00429 
00441 void
00442 BESKeys::set_key( const string &pair )
00443 {
00444     string key ;
00445     string val ;
00446     bool addto = false ;
00447     break_pair( pair.c_str(), key, val, addto ) ;
00448     set_key( key, val, addto ) ;
00449 }
00450 
00465 void
00466 BESKeys::get_value( const string& s, string &val, bool &found ) 
00467 {
00468     found = false ;
00469     map<string,vector<string> >::iterator i ;
00470     i = _the_keys->find( s ) ;
00471     if( i != _the_keys->end() )
00472     {
00473         found = true ;
00474         if( (*i).second.size() > 1 )
00475         {
00476             string err = string("Multiple values for the key ") + s
00477                          + " found, should only be one." ;
00478             throw BESSyntaxUserError( err, __FILE__, __LINE__ ) ;
00479         }
00480         if( (*i).second.size() == 1 )
00481         {
00482             val = (*i).second[0] ;
00483         }
00484         else
00485         {
00486             val = "" ;
00487         }
00488     }
00489 }
00490 
00502 void
00503 BESKeys::get_values( const string& s, vector<string> &vals, bool &found ) 
00504 {
00505     found = false ;
00506     map<string,vector<string> >::iterator i ;
00507     i = _the_keys->find( s ) ;
00508     if( i != _the_keys->end() )
00509     {
00510         found = true ;
00511         vals = (*i).second ;
00512     }
00513 }
00514 
00521 void
00522 BESKeys::dump( ostream &strm ) const
00523 {
00524     strm << BESIndent::LMarg << "BESKeys::dump - ("
00525                              << (void *)this << ")" << endl ;
00526     BESIndent::Indent() ;
00527     strm << BESIndent::LMarg << "key file:" << _keys_file_name << endl ;
00528     if( _keys_file && *_keys_file )
00529     {
00530         strm << BESIndent::LMarg << "key file is valid" << endl ;
00531     }
00532     else
00533     {
00534         strm << BESIndent::LMarg << "key file is NOT valid" << endl ;
00535     }
00536     if( _the_keys && _the_keys->size() )
00537     {
00538         strm << BESIndent::LMarg << "    keys:" << endl ;
00539         BESIndent::Indent() ;
00540         Keys_citer i = _the_keys->begin() ;
00541         Keys_citer ie = _the_keys->end() ;
00542         for( ; i != ie; i++ )
00543         {
00544             strm << BESIndent::LMarg << (*i).first << ":" << endl ;
00545             BESIndent::Indent() ;
00546             vector<string>::const_iterator v = (*i).second.begin() ;
00547             vector<string>::const_iterator ve = (*i).second.end() ;
00548             for( ; v != ve; v++ )
00549             {
00550                 strm << (*v) << endl ;
00551             }
00552             BESIndent::UnIndent() ;
00553         }
00554         BESIndent::UnIndent() ;
00555     }
00556     else
00557     {
00558         strm << BESIndent::LMarg << "keys: none" << endl ;
00559     }
00560     BESIndent::UnIndent() ;
00561 }
00562