bes  Updated for version 3.17.4
BESKeys.cc
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 #if 0
36 #ifdef __cplusplus
37 extern "C"
38 {
39 #include <sys/types.h>
40 //#include "regex.h"
41 }
42 #endif
43 #endif
44 
45 #include <cerrno>
46 #include <cstring>
47 
48 #if HAVE_UNISTD_H
49 #include <unistd.h>
50 #endif
51 
52 #include "BESKeys.h"
53 #include "BESUtil.h"
54 #include "BESFSDir.h"
55 #include "BESFSFile.h"
56 #include "BESInternalFatalError.h"
57 #include "BESSyntaxUserError.h"
58 
59 #define BES_INCLUDE_KEY "BES.Include"
60 
61 vector<string> BESKeys::KeyList;
62 
79 BESKeys::BESKeys(const string &keys_file_name) :
80  _keys_file(0), _keys_file_name(keys_file_name), _the_keys(0), _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), _keys_file_name(keys_file_name), _the_keys(keys), _own_keys(false)
88 {
89  initialize_keys();
90 }
91 
95 {
96  clean();
97 }
98 
99 void BESKeys::initialize_keys()
100 {
101  _keys_file = new ifstream(_keys_file_name.c_str());
102  //int myerrno = errno;
103  if (!(*_keys_file))
104  {
105  char path[500];
106  getcwd(path, sizeof(path));
107  string s = string("BES: fatal, cannot open BES configuration file ") + _keys_file_name + ": ";
108  char *err = strerror(errno);
109  if (err)
110  s += err;
111  else
112  s += "Unknown error";
113 
114  s += (string) ".\n" + "The current working directory is " + path + "\n";
115  throw BESInternalFatalError(s, __FILE__, __LINE__);
116  }
117 
118  try
119  {
120  load_keys();
121  }
122  catch (BESError &e)
123  {
124  // be sure we're throwing a fatal error, since the BES can't run
125  // within the configuration file
126  clean();
127  throw BESInternalFatalError(e.get_message(), e.get_file(), e.get_line());
128  }
129  catch (...)
130  {
131  clean();
132  string s = (string) "Undefined exception while trying to load keys " + "from bes configuration file " + _keys_file_name;
133  throw BESInternalFatalError(s, __FILE__, __LINE__);
134  }
135 }
136 
137 void BESKeys::clean()
138 {
139  if (_keys_file)
140  {
141  _keys_file->close();
142  delete _keys_file;
143  }
144 
145  if (_the_keys && _own_keys)
146  {
147  delete _the_keys;
148  }
149 }
150 
151 /* @brief Determine if the specified key file has been loaded yet
152  *
153  * Given the name of the key file, determine if it has already been
154  * loaded. More specifically, if started to load the file.
155  *
156  * @returns true if already started to load, false otherwise
157  */
158 bool BESKeys::LoadedKeys(const string &key_file)
159 {
160  vector<string>::const_iterator i = BESKeys::KeyList.begin();
161  vector<string>::const_iterator e = BESKeys::KeyList.end();
162  for (; i != e; i++)
163  {
164  if ((*i) == key_file)
165  {
166  return true;
167  }
168  }
169  return false;
170 }
171 
172 void BESKeys::load_keys()
173 {
174 #if 0 // Replaced this use of character buffer code with the string based version below. ndp 09/09/2015
175  char buffer[255];
176  string key, value;
177  while (!(*_keys_file).eof())
178  {
179  if ((*_keys_file).getline(buffer, 255))
180  {
181  bool addto = false;
182  if (break_pair(buffer, key, value, addto))
183  {
184  if (key == BES_INCLUDE_KEY)
185  {
186  // I added this call to set_key() because we need access
187  // to the child config files for the admin interface.
188  // jhrg 5/27/11
189  // Force 'addto' to true; we need all of the include values.
190  // jhrg 6/21/11
191  set_key(key, value, true);
192  load_include_files(value);
193  }
194  else
195  {
196  set_key(key, value, addto);
197  }
198  }
199  }
200  }
201 #endif
202 
203 
204  string key, value, line;
205  while(!_keys_file->eof() )
206  {
207  bool addto = false;
208  getline( *_keys_file, line );
209  if (break_pair(line.c_str(), key, value, addto))
210  {
211  if (key == BES_INCLUDE_KEY)
212  {
213  // We make this call to set_key() and force 'addto' to
214  // be true because we need access to the child configuration
215  // files and their values for the admin interface.
216  set_key(key, value, true);
217  load_include_files(value);
218  }
219  else
220  {
221  set_key(key, value, addto);
222  }
223  }
224 
225  }
226 
227 }
228 
229 // The string contained in the character buffer b should be of the
230 // format key=value or key+=value. The pair is broken apart, storing the
231 // key in the key parameter and the value of the key in the value
232 // parameter. If += is used, then the value should be added to the value
233 // of key, not replacing.
234 //
235 // It used to be that we would validate the key=value line. Instead,
236 // anything after the equal sign is considered the value of the key.
237 inline bool BESKeys::break_pair(const char* b, string& key, string &value, bool &addto)
238 {
239  addto = false;
240  // Ignore comments and lines with only spaces
241  if (b && (b[0] != '#') && (!only_blanks(b)))
242  {
243  register size_t l = strlen(b);
244  if (l > 1)
245  {
246  int pos = 0;
247  bool done = false;
248  for (register size_t j = 0; j < l && !done; j++)
249  {
250  if (b[j] == '=')
251  {
252  if (!addto)
253  pos = j;
254  else
255  {
256  if (pos != static_cast<int> (j - 1))
257  {
258  string s = string("BES: Invalid entry ") + b + " in configuration file " + _keys_file_name
259  + " '+' character found in variable name" + " or attempting '+=' with space" + " between the characters.\n";
260  throw BESInternalFatalError(s, __FILE__, __LINE__);
261  }
262  }
263  done = true;
264  }
265  else if (b[j] == '+')
266  {
267  addto = true;
268  pos = j;
269  }
270  }
271  if (!done)
272  {
273  string s = string("BES: Invalid entry ") + b + " in configuration file " + _keys_file_name + ": " + " '=' character not found.\n";
274  throw BESInternalFatalError(s, __FILE__, __LINE__);
275  }
276 
277  string s = b;
278  key = s.substr(0, pos);
280  if (addto)
281  value = s.substr(pos + 2, s.size());
282  else
283  value = s.substr(pos + 1, s.size());
285 
286  return true;
287  }
288 
289  return false;
290  }
291 
292  return false;
293 }
294 
304 void BESKeys::load_include_files(const string &files)
305 {
306  string newdir;
307  BESFSFile allfiles(files);
308 
309  // If the files specified begin with a /, then use that directory
310  // instead of the current keys file directory.
311  if (!files.empty() && files[0] == '/')
312  {
313  newdir = allfiles.getDirName();
314  }
315  else
316  {
317  // determine the directory of the current keys file. All included
318  // files will be relative to this file.
319  BESFSFile currfile(_keys_file_name);
320  string currdir = currfile.getDirName();
321 
322  string alldir = allfiles.getDirName();
323 
324  if ((currdir == "./" || currdir == ".") && (alldir == "./" || alldir == "."))
325  {
326  newdir = "./";
327  }
328  else
329  {
330  if (alldir == "./" || alldir == ".")
331  {
332  newdir = currdir;
333  }
334  else
335  {
336  newdir = currdir + "/" + alldir;
337  }
338  }
339  }
340 
341  // load the files one at a time. If the directory doesn't exist,
342  // then don't load any configuration files
343  BESFSDir fsd(newdir, allfiles.getFileName());
344  BESFSDir::fileIterator i = fsd.beginOfFileList();
345  BESFSDir::fileIterator e = fsd.endOfFileList();
346  for (; i != e; i++)
347  {
348  load_include_file((*i).getFullPath());
349  }
350 }
351 
358 void BESKeys::load_include_file(const string &file)
359 {
360  // make sure the file exists and is readable
361  // throws exception if unable to read
362  // not loaded if has already be started to be loaded
363  if (!BESKeys::LoadedKeys(file))
364  {
365  BESKeys::KeyList.push_back(file);
366  BESKeys tmp(file, _the_keys);
367  }
368 }
369 
370 bool BESKeys::only_blanks(const char *line)
371 {
372  string my_line = line;
373  if (my_line.find_first_not_of(" ") != string::npos)
374  return false;
375  else
376  return true;
377 
378 #if 0
379  int val;
380  regex_t rx;
381  string expr = "[^[:space:]]";
382  val = regcomp(&rx, expr.c_str(), REG_ICASE);
383 
384  if (val != 0)
385  {
386  string s = (string) "Regular expression " + expr + " did not compile correctly " + " in configuration file " + _keys_file_name;
387  throw BESInternalFatalError(s, __FILE__, __LINE__);
388  }
389  val = regexec(&rx, line, 0, 0, REG_NOTBOL);
390  if (val == 0)
391  {
392  regfree(&rx);
393  return false;
394  }
395  else
396  {
397  if (val == REG_NOMATCH)
398  {
399  regfree(&rx);
400  return true;
401  }
402  else if (val == REG_ESPACE)
403  {
404  string s = (string) "Execution of regular expression out of space" + " in configuration file " + _keys_file_name;
405  throw BESInternalFatalError(s, __FILE__, __LINE__);
406  }
407  else
408  {
409  string s = (string) "Execution of regular expression has unknown " + " problem in configuration file " + _keys_file_name;
410  throw BESInternalFatalError(s, __FILE__, __LINE__);
411  }
412  }
413 #endif
414 }
415 
432 void BESKeys::set_key(const string &key, const string &val, bool addto)
433 {
434  map<string, vector<string> >::iterator i;
435  i = _the_keys->find(key);
436  if (i == _the_keys->end())
437  {
438  vector<string> vals;
439  (*_the_keys)[key] = vals;
440  }
441  if (!addto)
442  (*_the_keys)[key].clear();
443  if (!val.empty())
444  {
445  (*_the_keys)[key].push_back(val);
446  }
447 }
448 
460 void BESKeys::set_key(const string &pair)
461 {
462  string key;
463  string val;
464  bool addto = false;
465  break_pair(pair.c_str(), key, val, addto);
466  set_key(key, val, addto);
467 }
468 
483 void BESKeys::get_value(const string& s, string &val, bool &found)
484 {
485  found = false;
486  map<string, vector<string> >::iterator i;
487  i = _the_keys->find(s);
488  if (i != _the_keys->end())
489  {
490  found = true;
491  if ((*i).second.size() > 1)
492  {
493  string err = string("Multiple values for the key ") + s + " found, should only be one.";
494  throw BESSyntaxUserError(err, __FILE__, __LINE__);
495  }
496  if ((*i).second.size() == 1)
497  {
498  val = (*i).second[0];
499  }
500  else
501  {
502  val = "";
503  }
504  }
505 }
506 
518 void BESKeys::get_values(const string& s, vector<string> &vals, bool &found)
519 {
520  found = false;
521  map<string, vector<string> >::iterator i;
522  i = _the_keys->find(s);
523  if (i != _the_keys->end())
524  {
525  found = true;
526  vals = (*i).second;
527  }
528 }
529 
536 void BESKeys::dump(ostream &strm) const
537 {
538  strm << BESIndent::LMarg << "BESKeys::dump - (" << (void *) this << ")" << endl;
539  BESIndent::Indent();
540  strm << BESIndent::LMarg << "key file:" << _keys_file_name << endl;
541  if (_keys_file && *_keys_file)
542  {
543  strm << BESIndent::LMarg << "key file is valid" << endl;
544  }
545  else
546  {
547  strm << BESIndent::LMarg << "key file is NOT valid" << endl;
548  }
549  if (_the_keys && _the_keys->size())
550  {
551  strm << BESIndent::LMarg << " keys:" << endl;
552  BESIndent::Indent();
553  Keys_citer i = _the_keys->begin();
554  Keys_citer ie = _the_keys->end();
555  for (; i != ie; i++)
556  {
557  strm << BESIndent::LMarg << (*i).first << ":" << endl;
558  BESIndent::Indent();
559  vector<string>::const_iterator v = (*i).second.begin();
560  vector<string>::const_iterator ve = (*i).second.end();
561  for (; v != ve; v++)
562  {
563  strm << (*v) << endl;
564  }
565  BESIndent::UnIndent();
566  }
567  BESIndent::UnIndent();
568  }
569  else
570  {
571  strm << BESIndent::LMarg << "keys: none" << endl;
572  }
573  BESIndent::UnIndent();
574 }
575 
exception thrown if an internal error is found and is fatal to the BES
virtual ~BESKeys()
cleans up the key/value pair mapping
Definition: BESKeys.cc:94
virtual void dump(ostream &strm) const
dumps information about this object
Definition: BESKeys.cc:536
virtual std::string get_message()
get the error message for this exception
Definition: BESError.h:97
static void removeLeadingAndTrailingBlanks(string &key)
Definition: BESUtil.cc:447
error thrown if there is a user syntax error in the request or any other user error ...
void set_key(const string &key, const string &val, bool addto=false)
allows the user to set key/value pairs from within the application.
Definition: BESKeys.cc:432
mapping of key/value pairs defining different behaviors of an application.
Definition: BESKeys.h:84
Abstract exception class for the BES with basic string message.
Definition: BESError.h:56
void get_value(const string &s, string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: BESKeys.cc:483
virtual std::string get_file()
get the file name where the exception was thrown
Definition: BESError.h:105
void get_values(const string &s, vector< string > &vals, bool &found)
Retrieve the values of a given key, if set.
Definition: BESKeys.cc:518
virtual int get_line()
get the line number where the exception was thrown
Definition: BESError.h:113