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