libdap++ Updated for version 3.8.2
|
00001 00002 // -*- mode: c++; c-basic-offset:4 -*- 00003 00004 // This file is part of libdap, A C++ implementation of the OPeNDAP Data 00005 // Access Protocol. 00006 00007 // Copyright (c) 2002,2003 OPeNDAP, Inc. 00008 // Author: Jose Garcia <jgarcia@ucar.edu> 00009 // 00010 // This library is free software; you can redistribute it and/or 00011 // modify it under the terms of the GNU Lesser General Public 00012 // License as published by the Free Software Foundation; either 00013 // version 2.1 of the License, or (at your option) any later version. 00014 // 00015 // This library is distributed in the hope that it will be useful, 00016 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00018 // Lesser General Public License for more details. 00019 // 00020 // You should have received a copy of the GNU Lesser General Public 00021 // License along with this library; if not, write to the Free Software 00022 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00023 // 00024 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. 00025 00026 // (c) COPYRIGHT URI/MIT 2001,2002 00027 // Please read the full copyright statement in the file COPYRIGHT_URI. 00028 // 00029 // Authors: 00030 // jose Jose Garcia <jgarcia@ucar.edu> 00031 00037 // #define DODS_DEBUG 00038 #include "config.h" 00039 00040 #include <cstring> 00041 00042 #include <unistd.h> // for stat 00043 #include <sys/types.h> 00044 #include <sys/stat.h> 00045 00046 #ifdef WIN32 00047 #define FALSE 0 00048 // Win32 does not define F_OK. 08/21/02 jhrg 00049 #define F_OK 0 00050 #define DIR_SEP_STRING "\\" 00051 #define DIR_SEP_CHAR '\\' 00052 #include <direct.h> 00053 #else 00054 #define DIR_SEP_STRING "/" 00055 #define DIR_SEP_CHAR '/' 00056 #endif 00057 00058 #include <pthread.h> 00059 00060 #include <fstream> 00061 00062 #include "debug.h" 00063 #include "RCReader.h" 00064 #include "Error.h" 00065 00066 using namespace std; 00067 00068 namespace libdap { 00069 00070 RCReader* RCReader::_instance = 0; 00071 00072 // This variable (instance_control) is used to ensure that in a MT 00073 // environment _instance is correctly initialized. See the get_instance 00074 // method. 08/07/02 jhrg 00075 static pthread_once_t instance_control = PTHREAD_ONCE_INIT; 00076 00081 bool 00082 RCReader::write_rc_file(const string &pathname) 00083 { 00084 DBG(cerr << "Writing the RC file to " << pathname << endl); 00085 ofstream fpo(pathname.c_str()); 00086 00087 // If the file couldn't be created. Nothing needs to be done here, 00088 // the program will simply use the defaults. 00089 00090 if (fpo) { 00091 // This means we just created the file. We will now save 00092 // the defaults in it for future use. 00093 fpo << "# OPeNDAP client configuration file. See the OPeNDAP" << endl; 00094 fpo << "# users guide for information." << endl; 00095 fpo << "USE_CACHE=" << _dods_use_cache << endl; 00096 fpo << "# Cache and object size are given in megabytes (20 ==> 20Mb)." 00097 << endl; 00098 fpo << "MAX_CACHE_SIZE=" << _dods_cache_max << endl; 00099 fpo << "MAX_CACHED_OBJ=" << _dods_cached_obj << endl; 00100 fpo << "IGNORE_EXPIRES=" << _dods_ign_expires << endl; 00101 fpo << "CACHE_ROOT=" << d_cache_root << endl; 00102 fpo << "DEFAULT_EXPIRES=" << _dods_default_expires << endl; 00103 fpo << "ALWAYS_VALIDATE=" << _dods_always_validate << endl; 00104 fpo << "# Request servers compress responses if possible?" << endl; 00105 fpo << "# 1 (yes) or 0 (false)." << endl; 00106 fpo << "DEFLATE=" << _dods_deflate << endl; 00107 00108 fpo << "# Should SSL certificates and hosts be validated? SSL" << endl; 00109 fpo << "# will only work with signed certificates." << endl; 00110 fpo << "VALIDATE_SSL=" << d_validate_ssl << endl; 00111 00112 fpo << "# Proxy configuration (optional parts in []s)." << endl; 00113 fpo << "# You may also use the 'http_proxy' environment variable" 00114 << endl; 00115 fpo << "# but a value in this file will override that env variable." 00116 << endl; 00117 fpo << "# PROXY_SERVER=[http://][username:password@]host[:port]" 00118 << endl; 00119 if (!d_dods_proxy_server_host.empty()) { 00120 fpo << "PROXY_SERVER=" << d_dods_proxy_server_protocol << "://" 00121 << (d_dods_proxy_server_userpw.empty() 00122 ? "" 00123 : d_dods_proxy_server_userpw + "@") 00124 + d_dods_proxy_server_host 00125 + ":" + long_to_string(d_dods_proxy_server_port) << endl; 00126 } 00127 00128 fpo << "# NO_PROXY_FOR=<host|domain>" << endl; 00129 if (!d_dods_no_proxy_for_host.empty()) { 00130 fpo << "NO_PROXY_FOR=" << d_dods_no_proxy_for_host << endl; 00131 } 00132 00133 fpo << "# AIS_DATABASE=<file or url>" << endl; 00134 00135 fpo << "# COOKIE_JAR=.dods_cookies" << endl; 00136 fpo << "# The cookie jar is a file that holds cookies sent from" 00137 << endl; 00138 fpo << "# servers such as single signon systems. Uncomment this" 00139 << endl; 00140 fpo << "# option and provide a file name to activate this feature." 00141 << endl; 00142 fpo << "# If the value is a filename, it will be created in this" 00143 << endl; 00144 fpo << "# directory; a full pathname can be used to force a specific" 00145 << endl; 00146 fpo << "# location." << endl; 00147 00148 fpo.close(); 00149 return true; 00150 } 00151 00152 return false; 00153 } 00154 00155 bool 00156 RCReader::read_rc_file(const string &pathname) 00157 { 00158 DBG(cerr << "Reading the RC file from " << pathname << endl); 00159 00160 ifstream fpi(pathname.c_str()); 00161 if (fpi) { 00162 // The file exists and we may now begin to parse it. 00163 // Defaults are already stored in the variables, if the correct 00164 // tokens are found in the file then those defaults will be 00165 // overwritten. 00166 char *value; 00167 char *tempstr = new char[1024];; 00168 int tokenlength; 00169 while (true) { 00170 fpi.getline(tempstr, 1023); 00171 if (!fpi.good()) 00172 break; 00173 00174 value = strchr(tempstr, '='); 00175 if (!value) 00176 continue; 00177 tokenlength = value - tempstr; 00178 value++; 00179 00180 if ((strncmp(tempstr, "USE_CACHE", 9) == 0) 00181 && tokenlength == 9) { 00182 _dods_use_cache = atoi(value) ? true : false; 00183 } 00184 else if ((strncmp(tempstr, "MAX_CACHE_SIZE", 14) == 0) 00185 && tokenlength == 14) { 00186 _dods_cache_max = atoi(value); 00187 } 00188 else if ((strncmp(tempstr, "MAX_CACHED_OBJ", 14) == 0) 00189 && tokenlength == 14) { 00190 _dods_cached_obj = atoi(value); 00191 } 00192 else if ((strncmp(tempstr, "IGNORE_EXPIRES", 14) == 0) 00193 && tokenlength == 14) { 00194 _dods_ign_expires = atoi(value); 00195 } 00196 else if ((strncmp(tempstr, "DEFLATE", 7) == 0) 00197 && tokenlength == 7) { 00198 _dods_deflate = atoi(value) ? true : false; 00199 } 00200 else if ((strncmp(tempstr, "CACHE_ROOT", 10) == 0) 00201 && tokenlength == 10) { 00202 d_cache_root = value; 00203 if (d_cache_root[d_cache_root.length() - 1] != DIR_SEP_CHAR) 00204 d_cache_root += string(DIR_SEP_STRING); 00205 } 00206 else if ((strncmp(tempstr, "DEFAULT_EXPIRES", 15) == 0) 00207 && tokenlength == 15) { 00208 _dods_default_expires = atoi(value); 00209 } 00210 else if ((strncmp(tempstr, "ALWAYS_VALIDATE", 15) == 0) 00211 && tokenlength == 15) { 00212 _dods_always_validate = atoi(value); 00213 } 00214 else if ((strncmp(tempstr, "VALIDATE_SSL", 12) == 0) 00215 && tokenlength == 12) { 00216 d_validate_ssl = atoi(value); 00217 } 00218 else if (strncmp(tempstr, "AIS_DATABASE", 12) == 0 00219 && tokenlength == 12) { 00220 d_ais_database = value; 00221 } 00222 else if (strncmp(tempstr, "COOKIE_JAR", 10) == 0 00223 && tokenlength == 10) { 00224 // if the value of COOKIE_JAR starts with a slash, use it as 00225 // is. However, if it does not start with a slash, prefix it 00226 // with the directory that contains the .dodsrc file. 00227 if (value[0] == '/') { 00228 d_cookie_jar = value; 00229 } 00230 else { 00231 d_cookie_jar = d_rc_file_path.substr(0, d_rc_file_path.find(".dodsrc")) + string(value); 00232 } 00233 DBG(cerr << "set cookie jar to: " << d_cookie_jar << endl); 00234 } 00235 else if ((strncmp(tempstr, "PROXY_SERVER", 12) == 0) 00236 && tokenlength == 12) { 00237 // Setup a proxy server for all requests. 00238 // The original syntax was <protocol>,<machine> where the 00239 // machine could also contain the user/pass and port info. 00240 // Support that but also support machine prefixed by 00241 // 'http://' with and without the '<protocol>,' prefix. jhrg 00242 // 4/21/08 (see bug 1095). 00243 string proxy = value; 00244 string::size_type comma = proxy.find(','); 00245 00246 // Since the <protocol> is now optional, the comma might be 00247 // here. If it is, check that the protocol given is http. 00248 if (comma != string::npos) { 00249 d_dods_proxy_server_protocol = proxy.substr(0, comma); 00250 downcase(d_dods_proxy_server_protocol); 00251 if (d_dods_proxy_server_protocol != "http") 00252 throw Error("The only supported protocol for a proxy server is \"HTTP\". Correct your \".dodsrc\" file."); 00253 proxy = proxy.substr(comma + 1); 00254 } 00255 else { 00256 d_dods_proxy_server_protocol = "http"; 00257 } 00258 00259 // Look for a 'protocol://' prefix; skip if found 00260 string::size_type protocol = proxy.find("://"); 00261 if (protocol != string::npos) { 00262 proxy = proxy.substr(protocol + 3); 00263 } 00264 00265 // Break apart into userpw, host and port. 00266 string::size_type at_sign = proxy.find('@'); 00267 if (at_sign != string::npos) { // has userpw 00268 d_dods_proxy_server_userpw = proxy.substr(0, at_sign); 00269 proxy = proxy.substr(at_sign + 1); 00270 } 00271 else 00272 d_dods_proxy_server_userpw = ""; 00273 00274 // Get host and look for a port number 00275 string::size_type colon = proxy.find(':'); 00276 if (colon != string::npos) { 00277 d_dods_proxy_server_host = proxy.substr(0, colon); 00278 d_dods_proxy_server_port 00279 = strtol(proxy.substr(colon + 1).c_str(), 0, 0); 00280 } 00281 else { 00282 d_dods_proxy_server_host = proxy; 00283 d_dods_proxy_server_port = 80; 00284 } 00285 } 00286 else if ((strncmp(tempstr, "NO_PROXY_FOR", 12) == 0) 00287 && tokenlength == 12) { 00288 // Setup a proxy server for all requests. 00289 string no_proxy = value; 00290 string::size_type comma = no_proxy.find(','); 00291 00292 // Since the protocol is required, the comma *must* be 00293 // present. We could throw an Error on the malformed line... 00294 if (comma == string::npos) { 00295 d_dods_no_proxy_for_protocol = "http"; 00296 d_dods_no_proxy_for_host = no_proxy; 00297 d_dods_no_proxy_for = true; 00298 } 00299 else { 00300 d_dods_no_proxy_for_protocol = no_proxy.substr(0, comma); 00301 d_dods_no_proxy_for_host = no_proxy.substr(comma + 1); 00302 d_dods_no_proxy_for = true; 00303 } 00304 } 00305 } 00306 00307 delete [] tempstr; tempstr = 0; 00308 00309 fpi.close(); // Close the .dodsrc file. 12/14/99 jhrg 00310 00311 return true; 00312 } // End of cache file parsing. 00313 00314 return false; 00315 } 00316 00317 // Helper for check_env_var(). This is its main logic, separated out for the 00318 // cases under WIN32 where we don't use an environment variable. 09/19/03 00319 // jhrg 00320 string 00321 RCReader::check_string(string env_var) 00322 { 00323 DBG(cerr << "Entering check_string... (" << env_var << ")" << endl); 00324 struct stat stat_info; 00325 00326 if (stat(env_var.c_str(), &stat_info) != 0) { 00327 DBG(cerr << "stat returned non-zero" << endl); 00328 return ""; // ENV VAR not a file or dir, bail 00329 } 00330 00331 if (S_ISREG(stat_info.st_mode)) { 00332 DBG(cerr << "S_ISREG: " << S_ISREG(stat_info.st_mode) << endl); 00333 return env_var; // ENV VAR is a file, use it 00334 } 00335 00336 // ENV VAR is a directory, does it contain .dodsrc? Can we create 00337 // .dodsrc if it's not there? 00338 if (S_ISDIR(stat_info.st_mode)) { 00339 DBG(cerr << "S_ISDIR: " << S_ISDIR(stat_info.st_mode) << endl); 00340 if (*env_var.rbegin() != DIR_SEP_CHAR) // Add trailing / if missing 00341 env_var += DIR_SEP_STRING; 00342 // Trick: set d_cache_root here in case we're going to create the 00343 // .dodsrc later on. If the .dodsrc file exists, its value will 00344 // overwrite this value, if not write_rc_file() will use the correct 00345 // value. 09/19/03 jhrg 00346 d_cache_root = env_var + string(".dods_cache") + DIR_SEP_STRING; 00347 env_var += ".dodsrc"; 00348 if (stat(env_var.c_str(), &stat_info) == 0 && 00349 S_ISREG(stat_info.st_mode)) { 00350 DBG(cerr << "Found .dodsrc in \"" << env_var << "\"" << endl); 00351 return env_var; // Found .dodsrc in ENV VAR 00352 } 00353 00354 // Didn't find .dodsrc in ENV VAR and ENV VAR is a directory; try to 00355 // create it. Note write_rc_file uses d_cache_root (set above) when 00356 // it creates the RC file's contents. 00357 if (write_rc_file(env_var)) { 00358 DBG(cerr << "Wrote .dodsrc in \"" << env_var << "\"" << endl); 00359 return env_var; 00360 } 00361 } 00362 00363 // If we're here, then we've neither found nor created the RC file. 00364 DBG(cerr << "could neither find nor create a .dodsrc file" << endl); 00365 return ""; 00366 } 00367 00377 string 00378 RCReader::check_env_var(const string &variable_name) 00379 { 00380 char *ev = getenv(variable_name.c_str()); 00381 if (!ev || strlen(ev) == 0) 00382 return ""; 00383 00384 return check_string(ev); 00385 } 00386 00387 RCReader::RCReader() throw(Error) 00388 { 00389 d_rc_file_path = ""; 00390 d_cache_root = ""; 00391 00392 // ** Set default values ** 00393 // Users must explicitly turn caching on. 00394 _dods_use_cache = false; 00395 _dods_cache_max = 20; 00396 _dods_cached_obj = 5; 00397 _dods_ign_expires = 0; 00398 _dods_default_expires = 86400; 00399 _dods_always_validate = 0; 00400 00401 _dods_deflate = 0; 00402 d_validate_ssl = 1; 00403 00404 //flags for PROXY_SERVER=<protocol>,<host url> 00405 // New syntax PROXY_SERVER=[http://][user:pw@]host[:port] 00406 d_dods_proxy_server_protocol = ""; 00407 d_dods_proxy_server_host = ""; 00408 d_dods_proxy_server_port = 0; 00409 d_dods_proxy_server_userpw = ""; 00410 00411 _dods_proxy_server_host_url = ""; // deprecated 00412 00413 // PROXY_FOR is deprecated. 00414 // flags for PROXY_FOR=<regex>,<proxy host url>,<flags> 00415 _dods_proxy_for = false; // true if proxy_for is used. 00416 _dods_proxy_for_regexp = ""; 00417 _dods_proxy_for_proxy_host_url = ""; 00418 _dods_proxy_for_regexp_flags = 0; 00419 00420 //flags for NO_PROXY_FOR=<protocol>,<host>,<port> 00421 // New syntax NO_PROXY_FOR=<host|domain> 00422 d_dods_no_proxy_for = false; 00423 d_dods_no_proxy_for_protocol = ""; // deprecated 00424 d_dods_no_proxy_for_host = ""; 00425 // default to port 0 if not specified. This means all ports. Using 80 00426 // will fail when the URL does not contain the port number. That's 00427 // probably a bug in libwww. 10/23/2000 jhrg 00428 _dods_no_proxy_for_port = 0; // deprecated 00429 00430 d_cookie_jar = ""; 00431 00432 #ifdef WIN32 00433 string homedir = string("C:") + string(DIR_SEP_STRING) + string("Dods"); 00434 d_rc_file_path = check_string(homedir); 00435 if d_rc_file_path.empty()) { 00436 homedir = string("C:") + string(DIR_SEP_STRING) + string("opendap"); 00437 d_rc_file_path = check_string(homedir); 00438 } 00439 // Normally, I'd prefer this for WinNT-based systems. 00440 if (d_rc_file_path.empty()) 00441 d_rc_file_path = check_env_var("APPDATA"); 00442 if (d_rc_file_path.empty()) 00443 d_rc_file_path = check_env_var("TEMP"); 00444 if (d_rc_file_path.empty()) 00445 d_rc_file_path = check_env_var("TMP"); 00446 #else 00447 d_rc_file_path = check_env_var("DODS_CONF"); 00448 if (d_rc_file_path.empty()) 00449 d_rc_file_path = check_env_var("HOME"); 00450 #endif 00451 DBG(cerr << "Looking for .dodsrc in: " << d_rc_file_path << endl); 00452 00453 if (!d_rc_file_path.empty()) 00454 read_rc_file(d_rc_file_path); 00455 } 00456 00457 RCReader::~RCReader() 00458 {} 00459 00461 void 00462 RCReader::delete_instance() 00463 { 00464 if (RCReader::_instance) { 00465 delete RCReader::_instance; 00466 RCReader::_instance = 0; 00467 } 00468 } 00469 00471 void 00472 RCReader::initialize_instance() 00473 { 00474 DBGN(cerr << "RCReader::initialize_instance() ... "); 00475 00476 RCReader::_instance = new RCReader; 00477 atexit(RCReader::delete_instance); 00478 00479 DBG(cerr << "exiting." << endl); 00480 } 00481 00482 RCReader* 00483 RCReader::instance() 00484 { 00485 DBG(cerr << "Entring RCReader::instance" << endl); 00486 // The instance_control variable is defined at the top of this file. 00487 // 08/07/02 jhrg 00488 pthread_once(&instance_control, initialize_instance); 00489 00490 DBG(cerr << "Instance value: " << hex << _instance << dec << endl); 00491 00492 return _instance; 00493 } 00494 00495 } // namespace libdap