libdap++  Updated for version 3.13.3
RCReader.cc
Go to the documentation of this file.
1 
2 // -*- mode: c++; c-basic-offset:4 -*-
3 
4 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
5 // Access Protocol.
6 
7 // Copyright (c) 2002,2003 OPeNDAP, Inc.
8 // Author: Jose Garcia <jgarcia@ucar.edu>
9 //
10 // This library is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU Lesser General Public
12 // License as published by the Free Software Foundation; either
13 // version 2.1 of the License, or (at your option) any later version.
14 //
15 // This library is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 // Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public
21 // License along with this library; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 //
24 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
25 
26 // (c) COPYRIGHT URI/MIT 2001,2002
27 // Please read the full copyright statement in the file COPYRIGHT_URI.
28 //
29 // Authors:
30 // jose Jose Garcia <jgarcia@ucar.edu>
31 
37 // #define DODS_DEBUG
38 #include "config.h"
39 
40 #include <cstring>
41 #include <cstdlib>
42 
43 #include <unistd.h> // for stat
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 
47 #ifdef WIN32
48 #define FALSE 0
49 // Win32 does not define F_OK. 08/21/02 jhrg
50 #define F_OK 0
51 #define DIR_SEP_STRING "\\"
52 #define DIR_SEP_CHAR '\\'
53 #include <direct.h>
54 #else
55 #define DIR_SEP_STRING "/"
56 #define DIR_SEP_CHAR '/'
57 #endif
58 
59 #include <pthread.h>
60 
61 #include <fstream>
62 
63 #include "debug.h"
64 #include "RCReader.h"
65 #include "Error.h"
66 
67 using namespace std;
68 
69 namespace libdap {
70 
71 RCReader* RCReader::_instance = 0;
72 
73 // This variable (instance_control) is used to ensure that in a MT
74 // environment _instance is correctly initialized. See the get_instance
75 // method. 08/07/02 jhrg
76 static pthread_once_t instance_control = PTHREAD_ONCE_INIT;
77 
82 bool
83 RCReader::write_rc_file(const string &pathname)
84 {
85  DBG(cerr << "Writing the RC file to " << pathname << endl);
86  ofstream fpo(pathname.c_str());
87 
88  // If the file couldn't be created. Nothing needs to be done here,
89  // the program will simply use the defaults.
90 
91  if (fpo) {
92  // This means we just created the file. We will now save
93  // the defaults in it for future use.
94  fpo << "# OPeNDAP client configuration file. See the OPeNDAP" << endl;
95  fpo << "# users guide for information." << endl;
96  fpo << "USE_CACHE=" << _dods_use_cache << endl;
97  fpo << "# Cache and object size are given in megabytes (20 ==> 20Mb)."
98  << endl;
99  fpo << "MAX_CACHE_SIZE=" << _dods_cache_max << endl;
100  fpo << "MAX_CACHED_OBJ=" << _dods_cached_obj << endl;
101  fpo << "IGNORE_EXPIRES=" << _dods_ign_expires << endl;
102  fpo << "CACHE_ROOT=" << d_cache_root << endl;
103  fpo << "DEFAULT_EXPIRES=" << _dods_default_expires << endl;
104  fpo << "ALWAYS_VALIDATE=" << _dods_always_validate << endl;
105  fpo << "# Request servers compress responses if possible?" << endl;
106  fpo << "# 1 (yes) or 0 (false)." << endl;
107  fpo << "DEFLATE=" << _dods_deflate << endl;
108 
109  fpo << "# Should SSL certificates and hosts be validated? SSL" << endl;
110  fpo << "# will only work with signed certificates." << endl;
111  fpo << "VALIDATE_SSL=" << d_validate_ssl << endl;
112 
113  fpo << "# Proxy configuration (optional parts in []s)." << endl;
114  fpo << "# You may also use the 'http_proxy' environment variable"
115  << endl;
116  fpo << "# but a value in this file will override that env variable."
117  << endl;
118  fpo << "# PROXY_SERVER=[http://][username:password@]host[:port]"
119  << endl;
120  if (!d_dods_proxy_server_host.empty()) {
121  fpo << "PROXY_SERVER=" << d_dods_proxy_server_protocol << "://"
122  << (d_dods_proxy_server_userpw.empty()
123  ? ""
124  : d_dods_proxy_server_userpw + "@")
125  + d_dods_proxy_server_host
126  + ":" + long_to_string(d_dods_proxy_server_port) << endl;
127  }
128 
129  fpo << "# NO_PROXY_FOR=<host|domain>" << endl;
130  if (!d_dods_no_proxy_for_host.empty()) {
131  fpo << "NO_PROXY_FOR=" << d_dods_no_proxy_for_host << endl;
132  }
133 
134  fpo << "# AIS_DATABASE=<file or url>" << endl;
135 
136  fpo << "# COOKIE_JAR=.dods_cookies" << endl;
137  fpo << "# The cookie jar is a file that holds cookies sent from"
138  << endl;
139  fpo << "# servers such as single signon systems. Uncomment this"
140  << endl;
141  fpo << "# option and provide a file name to activate this feature."
142  << endl;
143  fpo << "# If the value is a filename, it will be created in this"
144  << endl;
145  fpo << "# directory; a full pathname can be used to force a specific"
146  << endl;
147  fpo << "# location." << endl;
148 
149  fpo.close();
150  return true;
151  }
152 
153  return false;
154 }
155 
156 bool
157 RCReader::read_rc_file(const string &pathname)
158 {
159  DBG(cerr << "Reading the RC file from " << pathname << endl);
160 
161  ifstream fpi(pathname.c_str());
162  if (fpi) {
163  // The file exists and we may now begin to parse it.
164  // Defaults are already stored in the variables, if the correct
165  // tokens are found in the file then those defaults will be
166  // overwritten.
167  char *value;
168  // TODO Replace with a vector<char>
169  //char *tempstr = new char[1024];
170  vector<char> tempstr(1024);
171  int tokenlength;
172  while (true) {
173  fpi.getline(&tempstr[0], 1023);
174  if (!fpi.good())
175  break;
176 
177  value = strchr(&tempstr[0], '=');
178  if (!value)
179  continue;
180  tokenlength = value - &tempstr[0];
181  value++;
182 
183  if ((strncmp(&tempstr[0], "USE_CACHE", 9) == 0)
184  && tokenlength == 9) {
185  _dods_use_cache = atoi(value) ? true : false;
186  }
187  else if ((strncmp(&tempstr[0], "MAX_CACHE_SIZE", 14) == 0)
188  && tokenlength == 14) {
189  _dods_cache_max = atoi(value);
190  }
191  else if ((strncmp(&tempstr[0], "MAX_CACHED_OBJ", 14) == 0)
192  && tokenlength == 14) {
193  _dods_cached_obj = atoi(value);
194  }
195  else if ((strncmp(&tempstr[0], "IGNORE_EXPIRES", 14) == 0)
196  && tokenlength == 14) {
197  _dods_ign_expires = atoi(value);
198  }
199  else if ((strncmp(&tempstr[0], "DEFLATE", 7) == 0)
200  && tokenlength == 7) {
201  _dods_deflate = atoi(value) ? true : false;
202  }
203  else if ((strncmp(&tempstr[0], "CACHE_ROOT", 10) == 0)
204  && tokenlength == 10) {
205  d_cache_root = value;
206  if (d_cache_root[d_cache_root.length() - 1] != DIR_SEP_CHAR)
207  d_cache_root += string(DIR_SEP_STRING);
208  }
209  else if ((strncmp(&tempstr[0], "DEFAULT_EXPIRES", 15) == 0)
210  && tokenlength == 15) {
211  _dods_default_expires = atoi(value);
212  }
213  else if ((strncmp(&tempstr[0], "ALWAYS_VALIDATE", 15) == 0)
214  && tokenlength == 15) {
215  _dods_always_validate = atoi(value);
216  }
217  else if ((strncmp(&tempstr[0], "VALIDATE_SSL", 12) == 0)
218  && tokenlength == 12) {
219  d_validate_ssl = atoi(value);
220  }
221  else if (strncmp(&tempstr[0], "AIS_DATABASE", 12) == 0
222  && tokenlength == 12) {
223  d_ais_database = value;
224  }
225  else if (strncmp(&tempstr[0], "COOKIE_JAR", 10) == 0
226  && tokenlength == 10) {
227  // if the value of COOKIE_JAR starts with a slash, use it as
228  // is. However, if it does not start with a slash, prefix it
229  // with the directory that contains the .dodsrc file.
230  if (value[0] == '/') {
231  d_cookie_jar = value;
232  }
233  else {
234  d_cookie_jar = d_rc_file_path.substr(0, d_rc_file_path.find(".dodsrc")) + string(value);
235  }
236  DBG(cerr << "set cookie jar to: " << d_cookie_jar << endl);
237  }
238  else if ((strncmp(&tempstr[0], "PROXY_SERVER", 12) == 0)
239  && tokenlength == 12) {
240  // Setup a proxy server for all requests.
241  // The original syntax was <protocol>,<machine> where the
242  // machine could also contain the user/pass and port info.
243  // Support that but also support machine prefixed by
244  // 'http://' with and without the '<protocol>,' prefix. jhrg
245  // 4/21/08 (see bug 1095).
246  string proxy = value;
247  string::size_type comma = proxy.find(',');
248 
249  // Since the <protocol> is now optional, the comma might be
250  // here. If it is, check that the protocol given is http.
251  if (comma != string::npos) {
252  d_dods_proxy_server_protocol = proxy.substr(0, comma);
253  downcase(d_dods_proxy_server_protocol);
254  if (d_dods_proxy_server_protocol != "http")
255  throw Error("The only supported protocol for a proxy server is \"HTTP\". Correct your \".dodsrc\" file.");
256  proxy = proxy.substr(comma + 1);
257  }
258  else {
259  d_dods_proxy_server_protocol = "http";
260  }
261 
262  // Look for a 'protocol://' prefix; skip if found
263  string::size_type protocol = proxy.find("://");
264  if (protocol != string::npos) {
265  proxy = proxy.substr(protocol + 3);
266  }
267 
268  // Break apart into userpw, host and port.
269  string::size_type at_sign = proxy.find('@');
270  if (at_sign != string::npos) { // has userpw
271  d_dods_proxy_server_userpw = proxy.substr(0, at_sign);
272  proxy = proxy.substr(at_sign + 1);
273  }
274  else
275  d_dods_proxy_server_userpw = "";
276 
277  // Get host and look for a port number
278  string::size_type colon = proxy.find(':');
279  if (colon != string::npos) {
280  d_dods_proxy_server_host = proxy.substr(0, colon);
281  d_dods_proxy_server_port
282  = strtol(proxy.substr(colon + 1).c_str(), 0, 0);
283  }
284  else {
285  d_dods_proxy_server_host = proxy;
286  d_dods_proxy_server_port = 80;
287  }
288  }
289  else if ((strncmp(&tempstr[0], "NO_PROXY_FOR", 12) == 0)
290  && tokenlength == 12) {
291  // Setup a proxy server for all requests.
292  string no_proxy = value;
293  string::size_type comma = no_proxy.find(',');
294 
295  // Since the protocol is required, the comma *must* be
296  // present. We could throw an Error on the malformed line...
297  if (comma == string::npos) {
298  d_dods_no_proxy_for_protocol = "http";
299  d_dods_no_proxy_for_host = no_proxy;
300  d_dods_no_proxy_for = true;
301  }
302  else {
303  d_dods_no_proxy_for_protocol = no_proxy.substr(0, comma);
304  d_dods_no_proxy_for_host = no_proxy.substr(comma + 1);
305  d_dods_no_proxy_for = true;
306  }
307  }
308  }
309 
310  //delete [] tempstr; tempstr = 0;
311 
312  fpi.close(); // Close the .dodsrc file. 12/14/99 jhrg
313 
314  return true;
315  } // End of cache file parsing.
316 
317  return false;
318 }
319 
320 // Helper for check_env_var(). This is its main logic, separated out for the
321 // cases under WIN32 where we don't use an environment variable. 09/19/03
322 // jhrg
323 string
324 RCReader::check_string(string env_var)
325 {
326  DBG(cerr << "Entering check_string... (" << env_var << ")" << endl);
327  struct stat stat_info;
328 
329  if (stat(env_var.c_str(), &stat_info) != 0) {
330  DBG(cerr << "stat returned non-zero" << endl);
331  return ""; // ENV VAR not a file or dir, bail
332  }
333 
334  if (S_ISREG(stat_info.st_mode)) {
335  DBG(cerr << "S_ISREG: " << S_ISREG(stat_info.st_mode) << endl);
336  return env_var; // ENV VAR is a file, use it
337  }
338 
339  // ENV VAR is a directory, does it contain .dodsrc? Can we create
340  // .dodsrc if it's not there?
341  if (S_ISDIR(stat_info.st_mode)) {
342  DBG(cerr << "S_ISDIR: " << S_ISDIR(stat_info.st_mode) << endl);
343  if (*env_var.rbegin() != DIR_SEP_CHAR) // Add trailing / if missing
344  env_var += DIR_SEP_STRING;
345  // Trick: set d_cache_root here in case we're going to create the
346  // .dodsrc later on. If the .dodsrc file exists, its value will
347  // overwrite this value, if not write_rc_file() will use the correct
348  // value. 09/19/03 jhrg
349  d_cache_root = env_var + string(".dods_cache") + DIR_SEP_STRING;
350  env_var += ".dodsrc";
351  if (stat(env_var.c_str(), &stat_info) == 0 &&
352  S_ISREG(stat_info.st_mode)) {
353  DBG(cerr << "Found .dodsrc in \"" << env_var << "\"" << endl);
354  return env_var; // Found .dodsrc in ENV VAR
355  }
356 
357  // Didn't find .dodsrc in ENV VAR and ENV VAR is a directory; try to
358  // create it. Note write_rc_file uses d_cache_root (set above) when
359  // it creates the RC file's contents.
360  if (write_rc_file(env_var)) {
361  DBG(cerr << "Wrote .dodsrc in \"" << env_var << "\"" << endl);
362  return env_var;
363  }
364  }
365 
366  // If we're here, then we've neither found nor created the RC file.
367  DBG(cerr << "could neither find nor create a .dodsrc file" << endl);
368  return "";
369 }
370 
380 string
381 RCReader::check_env_var(const string &variable_name)
382 {
383  char *ev = getenv(variable_name.c_str());
384  if (!ev || strlen(ev) == 0)
385  return "";
386 
387  return check_string(ev);
388 }
389 
390 RCReader::RCReader() throw(Error)
391 {
392  d_rc_file_path = "";
393  d_cache_root = "";
394 
395  // ** Set default values **
396  // Users must explicitly turn caching on.
397  _dods_use_cache = false;
398  _dods_cache_max = 20;
399  _dods_cached_obj = 5;
400  _dods_ign_expires = 0;
401  _dods_default_expires = 86400;
402  _dods_always_validate = 0;
403 
404  _dods_deflate = 0;
405  d_validate_ssl = 1;
406 
407  //flags for PROXY_SERVER=<protocol>,<host url>
408  // New syntax PROXY_SERVER=[http://][user:pw@]host[:port]
409  d_dods_proxy_server_protocol = "";
410  d_dods_proxy_server_host = "";
411  d_dods_proxy_server_port = 0;
412  d_dods_proxy_server_userpw = "";
413 
414  _dods_proxy_server_host_url = ""; // deprecated
415 
416  // PROXY_FOR is deprecated.
417  // flags for PROXY_FOR=<regex>,<proxy host url>,<flags>
418  _dods_proxy_for = false; // true if proxy_for is used.
419  _dods_proxy_for_regexp = "";
420  _dods_proxy_for_proxy_host_url = "";
421  _dods_proxy_for_regexp_flags = 0;
422 
423  //flags for NO_PROXY_FOR=<protocol>,<host>,<port>
424  // New syntax NO_PROXY_FOR=<host|domain>
425  d_dods_no_proxy_for = false;
426  d_dods_no_proxy_for_protocol = ""; // deprecated
427  d_dods_no_proxy_for_host = "";
428  // default to port 0 if not specified. This means all ports. Using 80
429  // will fail when the URL does not contain the port number. That's
430  // probably a bug in libwww. 10/23/2000 jhrg
431  _dods_no_proxy_for_port = 0; // deprecated
432 
433  d_cookie_jar = "";
434 
435 #ifdef WIN32
436  string homedir = string("C:") + string(DIR_SEP_STRING) + string("Dods");
437  d_rc_file_path = check_string(homedir);
438  if (d_rc_file_path.empty()) {
439  homedir = string("C:") + string(DIR_SEP_STRING) + string("opendap");
440  d_rc_file_path = check_string(homedir);
441  }
442  // Normally, I'd prefer this for WinNT-based systems.
443  if (d_rc_file_path.empty())
444  d_rc_file_path = check_env_var("APPDATA");
445  if (d_rc_file_path.empty())
446  d_rc_file_path = check_env_var("TEMP");
447  if (d_rc_file_path.empty())
448  d_rc_file_path = check_env_var("TMP");
449 #else
450  d_rc_file_path = check_env_var("DODS_CONF");
451  if (d_rc_file_path.empty())
452  d_rc_file_path = check_env_var("HOME");
453 #endif
454  DBG(cerr << "Looking for .dodsrc in: " << d_rc_file_path << endl);
455 
456  if (!d_rc_file_path.empty())
457  read_rc_file(d_rc_file_path);
458 }
459 
460 RCReader::~RCReader()
461 {}
462 
464 void
465 RCReader::delete_instance()
466 {
467  if (RCReader::_instance) {
468  delete RCReader::_instance;
469  RCReader::_instance = 0;
470  }
471 }
472 
474 void
475 RCReader::initialize_instance()
476 {
477  DBGN(cerr << "RCReader::initialize_instance() ... ");
478 
479  RCReader::_instance = new RCReader;
480  atexit(RCReader::delete_instance);
481 
482  DBG(cerr << "exiting." << endl);
483 }
484 
485 RCReader*
486 RCReader::instance()
487 {
488  DBG(cerr << "Entring RCReader::instance" << endl);
489  // The instance_control variable is defined at the top of this file.
490  // 08/07/02 jhrg
491  pthread_once(&instance_control, initialize_instance);
492 
493  DBG(cerr << "Instance value: " << hex << _instance << dec << endl);
494 
495  return _instance;
496 }
497 
498 } // namespace libdap
#define DIR_SEP_CHAR
Definition: RCReader.cc:56
void downcase(string &s)
Definition: util.cc:428
#define DBGN(x)
Definition: debug.h:59
#define DBG(x)
Definition: debug.h:58
#define DIR_SEP_STRING
Definition: RCReader.cc:55
string long_to_string(long val, int base)
Definition: util.cc:773