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