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 {
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  if (_the_keys && _own_keys)
143  {
144  delete _the_keys;
145  }
146 }
147 
148 /* @brief Determine if the specified key file has been loaded yet
149  *
150  * Given the name of the key file, determine if it has already been
151  * loaded. More specifically, if started to load the file.
152  *
153  * @returns true if already started to load, false otherwise
154  */
155 bool BESKeys::LoadedKeys(const string &key_file)
156 {
157  vector<string>::const_iterator i = BESKeys::KeyList.begin();
158  vector<string>::const_iterator e = BESKeys::KeyList.end();
159  for (; i != e; i++)
160  {
161  if ((*i) == key_file)
162  {
163  return true;
164  }
165  }
166  return false;
167 }
168 
169 void BESKeys::load_keys()
170 {
171  char buffer[255];
172  string key, value;
173  while (!(*_keys_file).eof())
174  {
175  if ((*_keys_file).getline(buffer, 255))
176  {
177  bool addto = false;
178  if (break_pair(buffer, key, value, addto))
179  {
180  if (key == BES_INCLUDE_KEY)
181  {
182  // I added this call to set_key() because we need access
183  // to the child config files for the admin interface.
184  // jhrg 5/27/11
185  // Force 'addto' to true; we need all of the include values.
186  // jhrg 6/21/11
187  set_key(key, value, true);
188  load_include_files(value);
189  }
190  else
191  {
192  set_key(key, value, addto);
193  }
194  }
195  }
196  }
197 }
198 
199 // The string contained in the character buffer b should be of the
200 // format key=value or key+=value. The pair is broken apart, storing the
201 // key in the key parameter and the value of the key in the value
202 // parameter. If += is used, then the value should be added to the value
203 // of key, not replacing.
204 //
205 // It used to be that we would validate the key=value line. Instead,
206 // anything after the equal sign is considered the value of the key.
207 inline bool BESKeys::break_pair(const char* b, string& key, string &value, bool &addto)
208 {
209  addto = false;
210  // Ignore comments and lines with only spaces
211  if (b && (b[0] != '#') && (!only_blanks(b)))
212  {
213  register size_t l = strlen(b);
214  if (l > 1)
215  {
216  int pos = 0;
217  bool done = false;
218  for (register size_t j = 0; j < l && !done; j++)
219  {
220  if (b[j] == '=')
221  {
222  if (!addto)
223  pos = j;
224  else
225  {
226  if (pos != static_cast<int> (j - 1))
227  {
228  string s = string("BES: Invalid entry ") + b + " in configuration file " + _keys_file_name
229  + " '+' character found in variable name" + " or attempting '+=' with space" + " between the characters.\n";
230  throw BESInternalFatalError(s, __FILE__, __LINE__);
231  }
232  }
233  done = true;
234  }
235  else if (b[j] == '+')
236  {
237  addto = true;
238  pos = j;
239  }
240  }
241  if (!done)
242  {
243  string s = string("BES: Invalid entry ") + b + " in configuration file " + _keys_file_name + ": " + " '=' character not found.\n";
244  throw BESInternalFatalError(s, __FILE__, __LINE__);
245  }
246 
247  string s = b;
248  key = s.substr(0, pos);
250  if (addto)
251  value = s.substr(pos + 2, s.size());
252  else
253  value = s.substr(pos + 1, s.size());
255 
256  return true;
257  }
258 
259  return false;
260  }
261 
262  return false;
263 }
264 
274 void BESKeys::load_include_files(const string &files)
275 {
276  string newdir;
277  BESFSFile allfiles(files);
278 
279  // If the files specified begin with a /, then use that directory
280  // instead of the current keys file directory.
281  if (!files.empty() && files[0] == '/')
282  {
283  newdir = allfiles.getDirName();
284  }
285  else
286  {
287  // determine the directory of the current keys file. All included
288  // files will be relative to this file.
289  BESFSFile currfile(_keys_file_name);
290  string currdir = currfile.getDirName();
291 
292  string alldir = allfiles.getDirName();
293 
294  if ((currdir == "./" || currdir == ".") && (alldir == "./" || alldir == "."))
295  {
296  newdir = "./";
297  }
298  else
299  {
300  if (alldir == "./" || alldir == ".")
301  {
302  newdir = currdir;
303  }
304  else
305  {
306  newdir = currdir + "/" + alldir;
307  }
308  }
309  }
310 
311  // load the files one at a time. If the directory doesn't exist,
312  // then don't load any configuration files
313  BESFSDir fsd(newdir, allfiles.getFileName());
314  BESFSDir::fileIterator i = fsd.beginOfFileList();
315  BESFSDir::fileIterator e = fsd.endOfFileList();
316  for (; i != e; i++)
317  {
318  load_include_file((*i).getFullPath());
319  }
320 }
321 
328 void BESKeys::load_include_file(const string &file)
329 {
330  // make sure the file exists and is readable
331  // throws exception if unable to read
332  // not loaded if has already be started to be loaded
333  if (!BESKeys::LoadedKeys(file))
334  {
335  BESKeys::KeyList.push_back(file);
336  BESKeys tmp(file, _the_keys);
337  }
338 }
339 
340 bool BESKeys::only_blanks(const char *line)
341 {
342  string my_line = line;
343  if (my_line.find_first_not_of(" ") != string::npos)
344  return false;
345  else
346  return true;
347 
348 #if 0
349  int val;
350  regex_t rx;
351  string expr = "[^[:space:]]";
352  val = regcomp(&rx, expr.c_str(), REG_ICASE);
353 
354  if (val != 0)
355  {
356  string s = (string) "Regular expression " + expr + " did not compile correctly " + " in configuration file " + _keys_file_name;
357  throw BESInternalFatalError(s, __FILE__, __LINE__);
358  }
359  val = regexec(&rx, line, 0, 0, REG_NOTBOL);
360  if (val == 0)
361  {
362  regfree(&rx);
363  return false;
364  }
365  else
366  {
367  if (val == REG_NOMATCH)
368  {
369  regfree(&rx);
370  return true;
371  }
372  else if (val == REG_ESPACE)
373  {
374  string s = (string) "Execution of regular expression out of space" + " in configuration file " + _keys_file_name;
375  throw BESInternalFatalError(s, __FILE__, __LINE__);
376  }
377  else
378  {
379  string s = (string) "Execution of regular expression has unknown " + " problem in configuration file " + _keys_file_name;
380  throw BESInternalFatalError(s, __FILE__, __LINE__);
381  }
382  }
383 #endif
384 }
385 
402 void BESKeys::set_key(const string &key, const string &val, bool addto)
403 {
404  map<string, vector<string> >::iterator i;
405  i = _the_keys->find(key);
406  if (i == _the_keys->end())
407  {
408  vector<string> vals;
409  (*_the_keys)[key] = vals;
410  }
411  if (!addto)
412  (*_the_keys)[key].clear();
413  if (!val.empty())
414  {
415  (*_the_keys)[key].push_back(val);
416  }
417 }
418 
430 void BESKeys::set_key(const string &pair)
431 {
432  string key;
433  string val;
434  bool addto = false;
435  break_pair(pair.c_str(), key, val, addto);
436  set_key(key, val, addto);
437 }
438 
453 void BESKeys::get_value(const string& s, string &val, bool &found)
454 {
455  found = false;
456  map<string, vector<string> >::iterator i;
457  i = _the_keys->find(s);
458  if (i != _the_keys->end())
459  {
460  found = true;
461  if ((*i).second.size() > 1)
462  {
463  string err = string("Multiple values for the key ") + s + " found, should only be one.";
464  throw BESSyntaxUserError(err, __FILE__, __LINE__);
465  }
466  if ((*i).second.size() == 1)
467  {
468  val = (*i).second[0];
469  }
470  else
471  {
472  val = "";
473  }
474  }
475 }
476 
488 void BESKeys::get_values(const string& s, vector<string> &vals, bool &found)
489 {
490  found = false;
491  map<string, vector<string> >::iterator i;
492  i = _the_keys->find(s);
493  if (i != _the_keys->end())
494  {
495  found = true;
496  vals = (*i).second;
497  }
498 }
499 
506 void BESKeys::dump(ostream &strm) const
507 {
508  strm << BESIndent::LMarg << "BESKeys::dump - (" << (void *) this << ")" << endl;
510  strm << BESIndent::LMarg << "key file:" << _keys_file_name << endl;
511  if (_keys_file && *_keys_file)
512  {
513  strm << BESIndent::LMarg << "key file is valid" << endl;
514  }
515  else
516  {
517  strm << BESIndent::LMarg << "key file is NOT valid" << endl;
518  }
519  if (_the_keys && _the_keys->size())
520  {
521  strm << BESIndent::LMarg << " keys:" << endl;
523  Keys_citer i = _the_keys->begin();
524  Keys_citer ie = _the_keys->end();
525  for (; i != ie; i++)
526  {
527  strm << BESIndent::LMarg << (*i).first << ":" << endl;
529  vector<string>::const_iterator v = (*i).second.begin();
530  vector<string>::const_iterator ve = (*i).second.end();
531  for (; v != ve; v++)
532  {
533  strm << (*v) << endl;
534  }
536  }
538  }
539  else
540  {
541  strm << BESIndent::LMarg << "keys: none" << endl;
542  }
544 }
545