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