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 char *tempstr = new char[1024];; 00169 int tokenlength; 00170 while (true) { 00171 fpi.getline(tempstr, 1023); 00172 if (!fpi.good()) 00173 break; 00174 00175 value = strchr(tempstr, '='); 00176 if (!value) 00177 continue; 00178 tokenlength = value - tempstr; 00179 value++; 00180 00181 if ((strncmp(tempstr, "USE_CACHE", 9) == 0) 00182 && tokenlength == 9) { 00183 _dods_use_cache = atoi(value) ? true : false; 00184 } 00185 else if ((strncmp(tempstr, "MAX_CACHE_SIZE", 14) == 0) 00186 && tokenlength == 14) { 00187 _dods_cache_max = atoi(value); 00188 } 00189 else if ((strncmp(tempstr, "MAX_CACHED_OBJ", 14) == 0) 00190 && tokenlength == 14) { 00191 _dods_cached_obj = atoi(value); 00192 } 00193 else if ((strncmp(tempstr, "IGNORE_EXPIRES", 14) == 0) 00194 && tokenlength == 14) { 00195 _dods_ign_expires = atoi(value); 00196 } 00197 else if ((strncmp(tempstr, "DEFLATE", 7) == 0) 00198 && tokenlength == 7) { 00199 _dods_deflate = atoi(value) ? true : false; 00200 } 00201 else if ((strncmp(tempstr, "CACHE_ROOT", 10) == 0) 00202 && tokenlength == 10) { 00203 d_cache_root = value; 00204 if (d_cache_root[d_cache_root.length() - 1] != DIR_SEP_CHAR) 00205 d_cache_root += string(DIR_SEP_STRING); 00206 } 00207 else if ((strncmp(tempstr, "DEFAULT_EXPIRES", 15) == 0) 00208 && tokenlength == 15) { 00209 _dods_default_expires = atoi(value); 00210 } 00211 else if ((strncmp(tempstr, "ALWAYS_VALIDATE", 15) == 0) 00212 && tokenlength == 15) { 00213 _dods_always_validate = atoi(value); 00214 } 00215 else if ((strncmp(tempstr, "VALIDATE_SSL", 12) == 0) 00216 && tokenlength == 12) { 00217 d_validate_ssl = atoi(value); 00218 } 00219 else if (strncmp(tempstr, "AIS_DATABASE", 12) == 0 00220 && tokenlength == 12) { 00221 d_ais_database = value; 00222 } 00223 else if (strncmp(tempstr, "COOKIE_JAR", 10) == 0 00224 && tokenlength == 10) { 00225 // if the value of COOKIE_JAR starts with a slash, use it as 00226 // is. However, if it does not start with a slash, prefix it 00227 // with the directory that contains the .dodsrc file. 00228 if (value[0] == '/') { 00229 d_cookie_jar = value; 00230 } 00231 else { 00232 d_cookie_jar = d_rc_file_path.substr(0, d_rc_file_path.find(".dodsrc")) + string(value); 00233 } 00234 DBG(cerr << "set cookie jar to: " << d_cookie_jar << endl); 00235 } 00236 else if ((strncmp(tempstr, "PROXY_SERVER", 12) == 0) 00237 && tokenlength == 12) { 00238 // Setup a proxy server for all requests. 00239 // The original syntax was <protocol>,<machine> where the 00240 // machine could also contain the user/pass and port info. 00241 // Support that but also support machine prefixed by 00242 // 'http://' with and without the '<protocol>,' prefix. jhrg 00243 // 4/21/08 (see bug 1095). 00244 string proxy = value; 00245 string::size_type comma = proxy.find(','); 00246 00247 // Since the <protocol> is now optional, the comma might be 00248 // here. If it is, check that the protocol given is http. 00249 if (comma != string::npos) { 00250 d_dods_proxy_server_protocol = proxy.substr(0, comma); 00251 downcase(d_dods_proxy_server_protocol); 00252 if (d_dods_proxy_server_protocol != "http") 00253 throw Error("The only supported protocol for a proxy server is \"HTTP\". Correct your \".dodsrc\" file."); 00254 proxy = proxy.substr(comma + 1); 00255 } 00256 else { 00257 d_dods_proxy_server_protocol = "http"; 00258 } 00259 00260 // Look for a 'protocol://' prefix; skip if found 00261 string::size_type protocol = proxy.find("://"); 00262 if (protocol != string::npos) { 00263 proxy = proxy.substr(protocol + 3); 00264 } 00265 00266 // Break apart into userpw, host and port. 00267 string::size_type at_sign = proxy.find('@'); 00268 if (at_sign != string::npos) { // has userpw 00269 d_dods_proxy_server_userpw = proxy.substr(0, at_sign); 00270 proxy = proxy.substr(at_sign + 1); 00271 } 00272 else 00273 d_dods_proxy_server_userpw = ""; 00274 00275 // Get host and look for a port number 00276 string::size_type colon = proxy.find(':'); 00277 if (colon != string::npos) { 00278 d_dods_proxy_server_host = proxy.substr(0, colon); 00279 d_dods_proxy_server_port 00280 = strtol(proxy.substr(colon + 1).c_str(), 0, 0); 00281 } 00282 else { 00283 d_dods_proxy_server_host = proxy; 00284 d_dods_proxy_server_port = 80; 00285 } 00286 } 00287 else if ((strncmp(tempstr, "NO_PROXY_FOR", 12) == 0) 00288 && tokenlength == 12) { 00289 // Setup a proxy server for all requests. 00290 string no_proxy = value; 00291 string::size_type comma = no_proxy.find(','); 00292 00293 // Since the protocol is required, the comma *must* be 00294 // present. We could throw an Error on the malformed line... 00295 if (comma == string::npos) { 00296 d_dods_no_proxy_for_protocol = "http"; 00297 d_dods_no_proxy_for_host = no_proxy; 00298 d_dods_no_proxy_for = true; 00299 } 00300 else { 00301 d_dods_no_proxy_for_protocol = no_proxy.substr(0, comma); 00302 d_dods_no_proxy_for_host = no_proxy.substr(comma + 1); 00303 d_dods_no_proxy_for = true; 00304 } 00305 } 00306 } 00307 00308 delete [] tempstr; tempstr = 0; 00309 00310 fpi.close(); // Close the .dodsrc file. 12/14/99 jhrg 00311 00312 return true; 00313 } // End of cache file parsing. 00314 00315 return false; 00316 } 00317 00318 // Helper for check_env_var(). This is its main logic, separated out for the 00319 // cases under WIN32 where we don't use an environment variable. 09/19/03 00320 // jhrg 00321 string 00322 RCReader::check_string(string env_var) 00323 { 00324 DBG(cerr << "Entering check_string... (" << env_var << ")" << endl); 00325 struct stat stat_info; 00326 00327 if (stat(env_var.c_str(), &stat_info) != 0) { 00328 DBG(cerr << "stat returned non-zero" << endl); 00329 return ""; // ENV VAR not a file or dir, bail 00330 } 00331 00332 if (S_ISREG(stat_info.st_mode)) { 00333 DBG(cerr << "S_ISREG: " << S_ISREG(stat_info.st_mode) << endl); 00334 return env_var; // ENV VAR is a file, use it 00335 } 00336 00337 // ENV VAR is a directory, does it contain .dodsrc? Can we create 00338 // .dodsrc if it's not there? 00339 if (S_ISDIR(stat_info.st_mode)) { 00340 DBG(cerr << "S_ISDIR: " << S_ISDIR(stat_info.st_mode) << endl); 00341 if (*env_var.rbegin() != DIR_SEP_CHAR) // Add trailing / if missing 00342 env_var += DIR_SEP_STRING; 00343 // Trick: set d_cache_root here in case we're going to create the 00344 // .dodsrc later on. If the .dodsrc file exists, its value will 00345 // overwrite this value, if not write_rc_file() will use the correct 00346 // value. 09/19/03 jhrg 00347 d_cache_root = env_var + string(".dods_cache") + DIR_SEP_STRING; 00348 env_var += ".dodsrc"; 00349 if (stat(env_var.c_str(), &stat_info) == 0 && 00350 S_ISREG(stat_info.st_mode)) { 00351 DBG(cerr << "Found .dodsrc in \"" << env_var << "\"" << endl); 00352 return env_var; // Found .dodsrc in ENV VAR 00353 } 00354 00355 // Didn't find .dodsrc in ENV VAR and ENV VAR is a directory; try to 00356 // create it. Note write_rc_file uses d_cache_root (set above) when 00357 // it creates the RC file's contents. 00358 if (write_rc_file(env_var)) { 00359 DBG(cerr << "Wrote .dodsrc in \"" << env_var << "\"" << endl); 00360 return env_var; 00361 } 00362 } 00363 00364 // If we're here, then we've neither found nor created the RC file. 00365 DBG(cerr << "could neither find nor create a .dodsrc file" << endl); 00366 return ""; 00367 } 00368 00378 string 00379 RCReader::check_env_var(const string &variable_name) 00380 { 00381 char *ev = getenv(variable_name.c_str()); 00382 if (!ev || strlen(ev) == 0) 00383 return ""; 00384 00385 return check_string(ev); 00386 } 00387 00388 RCReader::RCReader() throw(Error) 00389 { 00390 d_rc_file_path = ""; 00391 d_cache_root = ""; 00392 00393 // ** Set default values ** 00394 // Users must explicitly turn caching on. 00395 _dods_use_cache = false; 00396 _dods_cache_max = 20; 00397 _dods_cached_obj = 5; 00398 _dods_ign_expires = 0; 00399 _dods_default_expires = 86400; 00400 _dods_always_validate = 0; 00401 00402 _dods_deflate = 0; 00403 d_validate_ssl = 1; 00404 00405 //flags for PROXY_SERVER=<protocol>,<host url> 00406 // New syntax PROXY_SERVER=[http://][user:pw@]host[:port] 00407 d_dods_proxy_server_protocol = ""; 00408 d_dods_proxy_server_host = ""; 00409 d_dods_proxy_server_port = 0; 00410 d_dods_proxy_server_userpw = ""; 00411 00412 _dods_proxy_server_host_url = ""; // deprecated 00413 00414 // PROXY_FOR is deprecated. 00415 // flags for PROXY_FOR=<regex>,<proxy host url>,<flags> 00416 _dods_proxy_for = false; // true if proxy_for is used. 00417 _dods_proxy_for_regexp = ""; 00418 _dods_proxy_for_proxy_host_url = ""; 00419 _dods_proxy_for_regexp_flags = 0; 00420 00421 //flags for NO_PROXY_FOR=<protocol>,<host>,<port> 00422 // New syntax NO_PROXY_FOR=<host|domain> 00423 d_dods_no_proxy_for = false; 00424 d_dods_no_proxy_for_protocol = ""; // deprecated 00425 d_dods_no_proxy_for_host = ""; 00426 // default to port 0 if not specified. This means all ports. Using 80 00427 // will fail when the URL does not contain the port number. That's 00428 // probably a bug in libwww. 10/23/2000 jhrg 00429 _dods_no_proxy_for_port = 0; // deprecated 00430 00431 d_cookie_jar = ""; 00432 00433 #ifdef WIN32 00434 string homedir = string("C:") + string(DIR_SEP_STRING) + string("Dods"); 00435 d_rc_file_path = check_string(homedir); 00436 if (d_rc_file_path.empty()) { 00437 homedir = string("C:") + string(DIR_SEP_STRING) + string("opendap"); 00438 d_rc_file_path = check_string(homedir); 00439 } 00440 // Normally, I'd prefer this for WinNT-based systems. 00441 if (d_rc_file_path.empty()) 00442 d_rc_file_path = check_env_var("APPDATA"); 00443 if (d_rc_file_path.empty()) 00444 d_rc_file_path = check_env_var("TEMP"); 00445 if (d_rc_file_path.empty()) 00446 d_rc_file_path = check_env_var("TMP"); 00447 #else 00448 d_rc_file_path = check_env_var("DODS_CONF"); 00449 if (d_rc_file_path.empty()) 00450 d_rc_file_path = check_env_var("HOME"); 00451 #endif 00452 DBG(cerr << "Looking for .dodsrc in: " << d_rc_file_path << endl); 00453 00454 if (!d_rc_file_path.empty()) 00455 read_rc_file(d_rc_file_path); 00456 } 00457 00458 RCReader::~RCReader() 00459 {} 00460 00462 void 00463 RCReader::delete_instance() 00464 { 00465 if (RCReader::_instance) { 00466 delete RCReader::_instance; 00467 RCReader::_instance = 0; 00468 } 00469 } 00470 00472 void 00473 RCReader::initialize_instance() 00474 { 00475 DBGN(cerr << "RCReader::initialize_instance() ... "); 00476 00477 RCReader::_instance = new RCReader; 00478 atexit(RCReader::delete_instance); 00479 00480 DBG(cerr << "exiting." << endl); 00481 } 00482 00483 RCReader* 00484 RCReader::instance() 00485 { 00486 DBG(cerr << "Entring RCReader::instance" << endl); 00487 // The instance_control variable is defined at the top of this file. 00488 // 08/07/02 jhrg 00489 pthread_once(&instance_control, initialize_instance); 00490 00491 DBG(cerr << "Instance value: " << hex << _instance << dec << endl); 00492 00493 return _instance; 00494 } 00495 00496 } // namespace libdap