RCReader.cc

Go to the documentation of this file.
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 #include "config.h"
00038 
00039 #include <cstring>
00040 
00041 #include <unistd.h>  // for stat
00042 #include <sys/types.h>
00043 #include <sys/stat.h>
00044 
00045 #ifdef WIN32
00046 #define FALSE 0
00047 // Win32 does not define F_OK. 08/21/02 jhrg
00048 #define F_OK 0
00049 #define DIR_SEP_STRING "\\"
00050 #define DIR_SEP_CHAR   '\\'
00051 #include <direct.h>
00052 #else
00053 #define DIR_SEP_STRING "/"
00054 #define DIR_SEP_CHAR   '/'
00055 #endif
00056 
00057 #include <pthread.h>
00058 
00059 #include <fstream>
00060 
00061 #include "debug.h"
00062 #include "RCReader.h"
00063 #include "Error.h"
00064 
00065 using namespace std;
00066 
00067 namespace libdap {
00068 
00069 RCReader* RCReader::_instance = 0;
00070 
00071 // This variable (instance_control) is used to ensure that in a MT
00072 // environment _instance is correctly initialized. See the get_instance
00073 // method. 08/07/02 jhrg
00074 static pthread_once_t instance_control = PTHREAD_ONCE_INIT;
00075 
00080 bool
00081 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 configuation 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)."
00096         << endl;
00097         fpo << "MAX_CACHE_SIZE=" << _dods_cache_max  << endl;
00098         fpo << "MAX_CACHED_OBJ=" << _dods_cached_obj << endl;
00099         fpo << "IGNORE_EXPIRES=" << _dods_ign_expires  << endl;
00100         fpo << "CACHE_ROOT=" << d_cache_root << endl;
00101         fpo << "DEFAULT_EXPIRES=" << _dods_default_expires << endl;
00102         fpo << "ALWAYS_VALIDATE=" << _dods_always_validate << endl;
00103         fpo << "# Request servers compress responses if possible?" << endl;
00104         fpo << "# 1 (yes) or 0 (false)." << endl;
00105         fpo << "DEFLATE=" << _dods_deflate << endl;
00106 
00107         fpo << "# Should SSL certificates and hosts be validated? SSL" << endl;
00108         fpo << "# will only work with signed certificates." << endl;
00109         fpo << "VALIDATE_SSL=" << d_validate_ssl << endl;
00110 
00111         fpo << "# Proxy configuration:" << endl;
00112         fpo << "# PROXY_SERVER=<protocol>,<[username:password@]host[:port]>"
00113         << 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()
00117                 ? ""
00118                 : d_dods_proxy_server_userpw + "@")
00119             + d_dods_proxy_server_host
00120             + ":" + long_to_string(d_dods_proxy_server_port) << endl;
00121         }
00122 
00123         fpo << "# NO_PROXY_FOR=<protocol>,<host|domain>" << endl;
00124         if (!d_dods_no_proxy_for_host.empty()) {
00125             fpo << "NO_PROXY_FOR=" << d_dods_no_proxy_for_protocol << ","
00126             << d_dods_no_proxy_for_host << endl;
00127         }
00128 
00129         fpo << "# AIS_DATABASE=<file or url>" << endl;
00130         fpo.close();
00131 
00132         return true;
00133     }
00134 
00135     return false;
00136 }
00137 
00138 bool
00139 RCReader::read_rc_file(const string &pathname)
00140 {
00141     DBG(cerr << "Reading the RC file from " << pathname << endl);
00142 
00143     ifstream fpi(pathname.c_str());
00144     if (fpi) {
00145         // The file exists and we may now begin to parse it.
00146         // Defaults are already stored in the variables, if the correct
00147         // tokens are found in the file then those defaults will be
00148         // overwritten.
00149         char *value;
00150         char *tempstr = new char[1024];;
00151         int tokenlength;
00152         while (true) {
00153             fpi.getline(tempstr, 1023);
00154             if (!fpi.good())
00155                 break;
00156 
00157             value = strchr(tempstr, '=');
00158             if (!value)
00159                 continue;
00160             tokenlength = value - tempstr;
00161             value++;
00162 
00163             if ((strncmp(tempstr, "USE_CACHE", 9) == 0)
00164                 && tokenlength == 9) {
00165                 _dods_use_cache = atoi(value) ? true : false;
00166             }
00167             else if ((strncmp(tempstr, "MAX_CACHE_SIZE", 14) == 0)
00168                      && tokenlength == 14) {
00169                 _dods_cache_max = atoi(value);
00170             }
00171             else if ((strncmp(tempstr, "MAX_CACHED_OBJ", 14) == 0)
00172                      && tokenlength == 14) {
00173                 _dods_cached_obj = atoi(value);
00174             }
00175             else if ((strncmp(tempstr, "IGNORE_EXPIRES", 14) == 0)
00176                      && tokenlength == 14) {
00177                 _dods_ign_expires = atoi(value);
00178             }
00179             else if ((strncmp(tempstr, "DEFLATE", 7) == 0)
00180                      && tokenlength == 7) {
00181                 _dods_deflate = atoi(value) ? true : false;
00182             }
00183             else if ((strncmp(tempstr, "CACHE_ROOT", 10) == 0)
00184                      && tokenlength == 10) {
00185                 d_cache_root = value;
00186                 if (d_cache_root[d_cache_root.length() - 1] != DIR_SEP_CHAR)
00187                     d_cache_root += string(DIR_SEP_STRING);
00188             }
00189             else if ((strncmp(tempstr, "DEFAULT_EXPIRES", 15) == 0)
00190                      && tokenlength == 15) {
00191                 _dods_default_expires = atoi(value);
00192             }
00193             else if ((strncmp(tempstr, "ALWAYS_VALIDATE", 15) == 0)
00194                      && tokenlength == 15) {
00195                 _dods_always_validate = atoi(value);
00196             }
00197             else if ((strncmp(tempstr, "VALIDATE_SSL", 12) == 0)
00198                      && tokenlength == 12) {
00199                 d_validate_ssl = atoi(value);
00200             }
00201             else if (strncmp(tempstr, "AIS_DATABASE", 12) == 0
00202                      && tokenlength == 12) {
00203                 d_ais_database = value;
00204             }
00205             else if ((strncmp(tempstr, "PROXY_SERVER", 12) == 0)
00206                      && tokenlength == 12) {
00207                 // Setup a proxy server for all requests.
00208                 string proxy = value;
00209                 string::size_type comma = proxy.find(',');
00210 
00211                 // Since the protocol is required, the comma *must* be
00212                 // present. We could throw an Error on the malformed line...
00213                 if (comma == string::npos)
00214                     continue;
00215                 d_dods_proxy_server_protocol = proxy.substr(0, comma);
00216                 proxy = proxy.substr(comma + 1);
00217 
00218                 // Break apart into userpw, host and port.
00219                 string::size_type at_sign = proxy.find('@');
00220                 if (at_sign != string::npos) { // has userpw
00221                     d_dods_proxy_server_userpw = proxy.substr(0, at_sign);
00222                     proxy = proxy.substr(at_sign + 1);
00223                 }
00224                 else
00225                     d_dods_proxy_server_userpw = "";
00226 
00227                 // Get host and look for a port number
00228                 string::size_type colon = proxy.find(':');
00229                 if (colon != string::npos) {
00230                     d_dods_proxy_server_host = proxy.substr(0, colon);
00231                     d_dods_proxy_server_port
00232                     = strtol(proxy.substr(colon + 1).c_str(), 0, 0);
00233                 }
00234                 else {
00235                     d_dods_proxy_server_host = proxy;
00236                     // No port given, look at protocol or throw
00237                     if (d_dods_proxy_server_protocol == "http")
00238                         d_dods_proxy_server_port = 80;
00239                     else if (d_dods_proxy_server_protocol == "https")
00240                         d_dods_proxy_server_port = 443;
00241                     else if (d_dods_proxy_server_protocol == "ftp")
00242                         d_dods_proxy_server_port = 21;
00243                     else throw Error("Could not determine the port to use for proxy '"
00244                                          + d_dods_proxy_server_host
00245                                          + ".' Please check your .dodsrc file.");
00246                 }
00247             }
00248             else if ((strncmp(tempstr, "NO_PROXY_FOR", 12) == 0)
00249                      && tokenlength == 12) {
00250                 // Setup a proxy server for all requests.
00251                 string no_proxy = value;
00252                 string::size_type comma = no_proxy.find(',');
00253 
00254                 // Since the protocol is required, the comma *must* be
00255                 // present. We could throw an Error on the malformed line...
00256                 if (comma == string::npos)
00257                     continue;
00258                 d_dods_no_proxy_for_protocol = no_proxy.substr(0, comma);
00259                 d_dods_no_proxy_for_host = no_proxy.substr(comma + 1);
00260                 d_dods_no_proxy_for = true;
00261             }
00262         }
00263 
00264         delete [] tempstr; tempstr = 0;
00265 
00266         fpi.close(); // Close the .dodsrc file. 12/14/99 jhrg
00267 
00268         return true;
00269     }  // End of cache file parsing.
00270 
00271     return false;
00272 }
00273 
00274 // Helper for check_env_var(). This is its main logic, separated out for the
00275 // cases under WIN32 where we don't use an environment variable.  09/19/03
00276 // jhrg
00277 string
00278 RCReader::check_string(string env_var)
00279 {
00280     struct stat stat_info;
00281 
00282     if (stat(env_var.c_str(), &stat_info) != 0)
00283         return "";  // ENV VAR not a file or dir, bail
00284 
00285     if (S_ISREG(stat_info.st_mode))
00286         return env_var;  // ENV VAR is a file, use it
00287 
00288     // ENV VAR is a directory, does it contain .dodsrc? Can we create
00289     // .dodsrc if it's not there?
00290     if (S_ISDIR(stat_info.st_mode)) {
00291         if (*env_var.rbegin() != DIR_SEP_CHAR) // Add trailing / if missing
00292             env_var += DIR_SEP_STRING;
00293         // Trick: set d_cache_root here in case we're going to create the
00294         // .dodsrc later on. If the .dodsrc file exists, its value will
00295         // overwrite this value, if not write_rc_file() will use the correct
00296         // value. 09/19/03 jhrg
00297         d_cache_root = env_var + string(".dods_cache") + DIR_SEP_STRING;
00298         env_var += ".dodsrc";
00299         if (stat(env_var.c_str(), &stat_info) == 0 &&
00300             S_ISREG(stat_info.st_mode))
00301             return env_var; // Found .dodsrc in ENV VAR
00302 
00303         // Didn't find .dodsrc in ENV VAR and ENV VAR is a directory; try to
00304         // create it. Note write_rc_file uses d_cache_root (set above) when
00305         // it creates the RC file's contents.
00306         if (write_rc_file(env_var))
00307             return env_var;
00308     }
00309 
00310     // If we're here, then we've not found or created the RC file.
00311     return "";
00312 }
00313 
00323 string
00324 RCReader::check_env_var(const string &variable_name)
00325 {
00326     char *ev = getenv(variable_name.c_str());
00327     if (!ev || strlen(ev) == 0)
00328         return "";
00329 
00330     return check_string(ev);
00331 }
00332 
00333 RCReader::RCReader() throw(Error)
00334 {
00335     d_rc_file_path = "";
00336     d_cache_root = "";
00337 
00338     // ** Set default values **
00339     // Users must explicitly turn caching on.
00340     _dods_use_cache = false;
00341     _dods_cache_max = 20;
00342     _dods_cached_obj = 5;
00343     _dods_ign_expires = 0;
00344     _dods_default_expires = 86400;
00345     _dods_always_validate = 0;
00346 
00347     _dods_deflate = 0;
00348     d_validate_ssl = 1;
00349 
00350     //flags for PROXY_SERVER=<protocol>,<host url>
00351     d_dods_proxy_server_protocol = "";
00352     d_dods_proxy_server_host = "";
00353     d_dods_proxy_server_port = 0;
00354     d_dods_proxy_server_userpw = "";
00355 
00356     _dods_proxy_server_host_url = ""; // deprecated
00357 
00358     // flags for PROXY_FOR=<regex>,<proxy host url>,<flags>
00359     _dods_proxy_for = false; // true if proxy_for is used.
00360     _dods_proxy_for_regexp = "";
00361     _dods_proxy_for_proxy_host_url = "";
00362     _dods_proxy_for_regexp_flags = 0;
00363 
00364     //flags for NO_PROXY_FOR=<protocol>,<host>,<port>
00365     d_dods_no_proxy_for = false;
00366     d_dods_no_proxy_for_protocol = "";
00367     d_dods_no_proxy_for_host = "";
00368     // default to port 0 if not specified. This means all ports. Using 80
00369     // will fail when the URL does not contain the port number. That's
00370     // probably a bug in libwww. 10/23/2000 jhrg
00371     _dods_no_proxy_for_port = 0;
00372 
00373 #ifdef WIN32
00374     string homedir = string("C:") + string(DIR_SEP_STRING) + string("Dods");
00375     d_rc_file_path = check_string(homedir);
00376     //  Normally, I'd prefer this for WinNT-based systems.
00377     if (d_rc_file_path.empty())
00378         d_rc_file_path = check_env_var("APPDATA");
00379     if (d_rc_file_path.empty())
00380         d_rc_file_path = check_env_var("TEMP");
00381     if (d_rc_file_path.empty())
00382         d_rc_file_path = check_env_var("TMP");
00383 #else
00384     d_rc_file_path = check_env_var("DODS_CONF");
00385     if (d_rc_file_path.empty())
00386         d_rc_file_path = check_env_var("HOME");
00387 #endif
00388 
00389     if (!d_rc_file_path.empty())
00390         read_rc_file(d_rc_file_path);
00391 }
00392 
00393 RCReader::~RCReader()
00394 {}
00395 
00397 void
00398 RCReader::delete_instance()
00399 {
00400     if (RCReader::_instance) {
00401         delete RCReader::_instance;
00402         RCReader::_instance = 0;
00403     }
00404 }
00405 
00407 void
00408 RCReader::initialize_instance()
00409 {
00410     DBG(cerr << "RCReader::initialize_instance() ... ");
00411 
00412     RCReader::_instance = new RCReader;
00413     atexit(RCReader::delete_instance);
00414 
00415     DBGN(cerr << "exiting." << endl);
00416 }
00417 
00418 RCReader*
00419 RCReader::instance()
00420 {
00421     // The instance_control variable is defined at the top of this file.
00422     // 08/07/02 jhrg
00423     pthread_once(&instance_control, initialize_instance);
00424 
00425     return _instance;
00426 }
00427 
00428 } // namespace libdap

Generated on Wed Mar 5 15:27:11 2008 for libdap++ by  doxygen 1.5.4