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