configfile.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       configfile.cc
00003 ///             Barry configuraion class, for one device PIN
00004 ///
00005 
00006 /*
00007     Copyright (C) 2007-2010, Net Direct Inc. (http://www.netdirect.ca/)
00008 
00009     This program is free software; you can redistribute it and/or modify
00010     it under the terms of the GNU General Public License as published by
00011     the Free Software Foundation; either version 2 of the License, or
00012     (at your option) any later version.
00013 
00014     This program 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.
00017 
00018     See the GNU General Public License in the COPYING file at the
00019     root directory of this project for more details.
00020 */
00021 
00022 #include "configfile.h"
00023 #include "error.h"
00024 #include "r_message.h"
00025 #include <pwd.h>
00026 #include <string.h>
00027 #include <errno.h>
00028 #include <unistd.h>
00029 #include <fstream>
00030 #include <sstream>
00031 #include <sys/types.h>
00032 #include <sys/stat.h>
00033 
00034 namespace Barry {
00035 
00036 bool ConfigFile::DBListType::IsSelected(const std::string &dbname) const
00037 {
00038         const_iterator i = begin();
00039         for( ; i != end(); ++i ) {
00040                 if( *i == dbname ) {
00041                         return true;
00042                 }
00043         }
00044         return false;
00045 }
00046 
00047 
00048 //////////////////////////////////////////////////////////////////////////////
00049 // ConfigFile class members
00050 
00051 /// Loads config file for the given pin, and ends up in an
00052 /// unenlightened state.  Throws ConfigFileError on error,
00053 /// but it is not an error if the config does not exist.
00054 /// Never use this if you have a DatabaseDatabase object!
00055 /// This ctor is only for temporary loading of config data.
00056 ConfigFile::ConfigFile(Barry::Pin pin)
00057         : m_pin(pin)
00058         , m_loaded(false)
00059         , m_promptBackupLabel(false)
00060 {
00061         if( m_pin == 0 )
00062                 throw ConfigFileError("Configfile: empty pin");
00063 
00064         BuildFilename();
00065         BuildDefaultPath(); // this handles the situation that path is not set
00066         Load();
00067 }
00068 
00069 /// Opens and loads config file for given pin, and calls Enlighten
00070 /// Throws ConfigFileError on error.  Should never fail unless
00071 /// passed a bad pin.
00072 ConfigFile::ConfigFile(Barry::Pin pin,
00073                        const Barry::DatabaseDatabase &db)
00074         : m_pin(pin)
00075         , m_loaded(false)
00076         , m_promptBackupLabel(false)
00077 {
00078         if( m_pin == 0 )
00079                 throw ConfigFileError("Configfile: empty pin");
00080 
00081         BuildFilename();
00082         BuildDefaultPath();
00083         Load();
00084         Enlighten(db);
00085 }
00086 
00087 ConfigFile::~ConfigFile()
00088 {
00089 }
00090 
00091 void ConfigFile::BuildFilename()
00092 {
00093         struct passwd *pw = getpwuid(getuid());
00094         if( !pw )
00095                 throw ConfigFileError("BuildFilename: getpwuid failed", errno);
00096 
00097         m_filename = pw->pw_dir;
00098         m_filename += "/.barry/backup/";
00099         m_filename += m_pin.str();
00100         m_filename += "/config";
00101 }
00102 
00103 void ConfigFile::BuildDefaultPath()
00104 {
00105         struct passwd *pw = getpwuid(getuid());
00106         m_path = pw->pw_dir;
00107         m_path += "/.barry/backup/";
00108         m_path += m_pin.str();
00109 }
00110 
00111 void ConfigFile::Clear()
00112 {
00113         m_loaded = false;
00114         m_backupList.clear();
00115         m_restoreList.clear();
00116         m_deviceName.clear();
00117         m_promptBackupLabel = false;
00118 }
00119 
00120 /// Attempt to load the configuration file, but do not fail if not available
00121 void ConfigFile::Load()
00122 {
00123         // start fresh
00124         Clear();
00125 
00126         // open input file
00127         std::ifstream in(m_filename.c_str(), std::ios::in | std::ios::binary);
00128         if( !in )
00129                 return;
00130 
00131         std::string line;
00132         DBListType *pList = 0;
00133 
00134         while( std::getline(in, line) ) {
00135                 std::string keyword;
00136                 std::istringstream iss(line);
00137                 iss >> keyword;
00138 
00139                 if( keyword == "backup_list" ) {
00140                         pList = &m_backupList;
00141                 }
00142                 else if( keyword == "restore_list" ) {
00143                         pList = &m_restoreList;
00144                 }
00145                 else if( line[0] == ' ' && pList ) {
00146                         pList->push_back(line.c_str() + 1);
00147                 }
00148                 else {
00149                         pList = 0;
00150 
00151                         // add all remaining keyword checks here
00152                         if( keyword == "device_name" ) {
00153                                 iss >> std::ws;
00154                                 std::getline(iss, m_deviceName);
00155                                 if( m_deviceName.size() == 0 ) {
00156                                         // if there is a device_name setting,
00157                                         // then this value must hold something,
00158                                         // so that the user can ignore this
00159                                         // field, and not get pestered all
00160                                         // the time
00161                                         m_deviceName = " ";
00162                                 }
00163                         }
00164                         else if( keyword == "backup_path" ) {
00165                                 iss >> std::ws;
00166                                 std::getline(iss, m_path);
00167                                 if( (m_path.size() == 0) || !(CheckPath(m_path)))
00168                                         BuildDefaultPath();
00169                         }
00170                         else if( keyword == "prompt_backup_label" ) {
00171                                 int flag;
00172                                 iss >> flag;
00173                                 m_promptBackupLabel = flag;
00174                         }
00175                 }
00176         }
00177 
00178         m_loaded = true;
00179 }
00180 
00181 /// Saves current device's config, overwriting or creating a config file
00182 bool ConfigFile::Save()
00183 {
00184         if( !CheckPath(m_path, &m_last_error) )
00185                 return false;
00186 
00187         std::ofstream out(m_filename.c_str(), std::ios::out | std::ios::binary);
00188         if( !out ) {
00189                 m_last_error = "Unable to open " + m_filename + " for writing.";
00190                 return false;
00191         }
00192 
00193         out << "backup_list" << std::endl;
00194         for( DBListType::iterator i = m_backupList.begin(); i != m_backupList.end(); ++i ) {
00195                 out << " " << *i << std::endl;
00196         }
00197 
00198         out << "restore_list" << std::endl;
00199         for( DBListType::iterator i = m_restoreList.begin(); i != m_restoreList.end(); ++i ) {
00200                 out << " " << *i << std::endl;
00201         }
00202 
00203         if( m_deviceName.size() ) {
00204                 out << "device_name " << m_deviceName << std::endl;
00205         }
00206 
00207         if( m_path.size() ) {
00208                 out << "backup_path " << m_path << std::endl;
00209         }
00210 
00211         out << "prompt_backup_label " << (m_promptBackupLabel ? 1 : 0) << std::endl;
00212 
00213         if( !out ) {
00214                 m_last_error = "Error during write.  Config may be incomplete.";
00215                 return false;
00216         }
00217         return true;
00218 }
00219 
00220 /// Compares a given databasedatabase from a real device with the
00221 /// current config.  If not yet configured, initialize with valid
00222 /// defaults.
00223 void ConfigFile::Enlighten(const Barry::DatabaseDatabase &db)
00224 {
00225         if( !m_loaded ) {
00226                 // if not fully loaded, we use db as our default list
00227                 // our defaults are: backup everything, restore everything
00228                 // except email
00229 
00230                 m_backupList.clear();
00231                 m_restoreList.clear();
00232 
00233                 Barry::DatabaseDatabase::DatabaseArrayType::const_iterator i =
00234                         db.Databases.begin();
00235                 for( ; i != db.Databases.end(); ++i ) {
00236                         // backup everything
00237                         m_backupList.push_back(i->Name);
00238 
00239                         // restore everything except email (which could take ages)
00240                         // and Handheld Agent (which seems write protected)
00241                         if( i->Name != Barry::Message::GetDBName() &&
00242                             i->Name != "Handheld Agent" )
00243                         {
00244                                 m_restoreList.push_back(i->Name);
00245                         }
00246                 }
00247         }
00248 }
00249 
00250 /// Sets list with new config
00251 void ConfigFile::SetBackupList(const DBListType &list)
00252 {
00253         m_backupList = list;
00254         m_loaded = true;
00255 }
00256 
00257 void ConfigFile::SetRestoreList(const DBListType &list)
00258 {
00259         m_restoreList = list;
00260         m_loaded = true;
00261 }
00262 
00263 void ConfigFile::SetDeviceName(const std::string &name)
00264 {
00265         if( name.size() )
00266                 m_deviceName = name;
00267         else
00268                 m_deviceName = " ";
00269 }
00270 
00271 void ConfigFile::SetBackupPath(const std::string &path)
00272 {
00273         if( path.size() && CheckPath(path) )
00274                 m_path = path;
00275         else
00276                 BuildDefaultPath();
00277 }
00278 
00279 void ConfigFile::SetPromptBackupLabel(bool prompt)
00280 {
00281         m_promptBackupLabel = prompt;
00282 }
00283 
00284 /// Checks that the path in path exists, and if not, creates it.
00285 /// Returns false if unable to create path, true if ok.
00286 bool ConfigFile::CheckPath(const std::string &path, std::string *perr)
00287 {
00288         if( path.size() == 0 ) {
00289                 if( perr )
00290                         *perr = "path is empty!";
00291                 return false;
00292         }
00293 
00294         if( access(path.c_str(), F_OK) == 0 )
00295                 return true;
00296 
00297         std::string base;
00298         std::string::size_type slash = 0;
00299         while( (slash = path.find('/', slash + 1)) != std::string::npos ) {
00300                 base = path.substr(0, slash);
00301                 if( access(base.c_str(), F_OK) != 0 ) {
00302                         if( mkdir(base.c_str(), 0755) == -1 ) {
00303                                 if( perr ) {
00304                                         *perr = "mkdir(" + base + ") failed: ";
00305                                         *perr += strerror(errno);
00306                                 }
00307                                 return false;
00308                         }
00309                 }
00310         }
00311         if( mkdir(path.c_str(), 0755) == -1 ) {
00312                 if( perr ) {
00313                         *perr = "last mkdir(" + path + ") failed: ";
00314                         *perr += strerror(errno);
00315                 }
00316                 return false;
00317         }
00318         return true;
00319 }
00320 
00321 
00322 
00323 //////////////////////////////////////////////////////////////////////////////
00324 // GlobalConfigFile class members
00325 
00326 GlobalConfigFile::GlobalConfigFile()
00327         : m_loaded(false)
00328         , m_verboseLogging(false)
00329 {
00330         BuildFilename();
00331         Load();
00332 }
00333 
00334 GlobalConfigFile::GlobalConfigFile(const std::string &appname)
00335         : m_loaded(false)
00336         , m_appname(appname)
00337         , m_verboseLogging(false)
00338 {
00339         // there can be no spaces in the appname
00340         if( m_appname.find(' ') != std::string::npos )
00341                 throw std::logic_error("App name must have no spaces.");
00342 
00343         BuildFilename();
00344         Load();
00345 }
00346 
00347 GlobalConfigFile::~GlobalConfigFile()
00348 {
00349 }
00350 
00351 void GlobalConfigFile::BuildFilename()
00352 {
00353         struct passwd *pw = getpwuid(getuid());
00354         if( !pw )
00355                 throw ConfigFileError("BuildFilename: getpwuid failed", errno);
00356 
00357         m_filename = pw->pw_dir;
00358         m_filename += "/.barry/config";
00359 
00360         // build the global path too, since this never changes
00361         m_path = pw->pw_dir;
00362         m_path += "/.barry";
00363 }
00364 
00365 void GlobalConfigFile::Clear()
00366 {
00367         m_loaded = false;
00368         m_lastDevice = 0;
00369 }
00370 
00371 void GlobalConfigFile::Load()
00372 {
00373         // start fresh
00374         Clear();
00375 
00376         // open input file
00377         std::ifstream in(m_filename.c_str(), std::ios::in | std::ios::binary);
00378         if( !in )
00379                 return;
00380 
00381         std::string line;
00382 
00383         while( std::getline(in, line) ) {
00384                 std::string keyword;
00385                 std::istringstream iss(line);
00386                 iss >> keyword;
00387 
00388                 if( keyword == "last_device" ) {
00389                         iss >> std::ws;
00390                         iss >> m_lastDevice;
00391                 }
00392                 else if( keyword == "verbose_logging" ) {
00393                         int flag;
00394                         iss >> flag;
00395                         m_verboseLogging = flag;
00396                 }
00397                 else {
00398                         // store any other keys as app keys
00399                         if( keyword.substr(0, 2) == "X-" ) {
00400                                 iss >> std::ws;
00401                                 std::getline(iss, line);
00402                                 m_keymap[keyword] = line;
00403                         }
00404                 }
00405         }
00406 
00407         m_loaded = true;
00408 }
00409 
00410 /// Save the current global config, overwriting or creating as needed
00411 bool GlobalConfigFile::Save()
00412 {
00413         if( !ConfigFile::CheckPath(m_path, &m_last_error) )
00414                 return false;
00415 
00416         std::ofstream out(m_filename.c_str(), std::ios::out | std::ios::binary);
00417         if( !out ) {
00418                 m_last_error = "Unable to open " + m_filename + " for writing.";
00419                 return false;
00420         }
00421 
00422         if( !(m_lastDevice == 0) ) {
00423                 out << "last_device " << m_lastDevice.str() << std::endl;
00424         }
00425 
00426         out << "verbose_logging " << (m_verboseLogging ? 1 : 0) << std::endl;
00427 
00428         // store all app keys
00429         keymap_type::const_iterator ci = m_keymap.begin();
00430         for( ; ci != m_keymap.end(); ++ci ) {
00431                 out << ci->first << " " << ci->second << std::endl;
00432         }
00433 
00434         if( !out ) {
00435                 m_last_error = "Error during write.  Config may be incomplete.";
00436                 return false;
00437         }
00438         return true;
00439 }
00440 
00441 void GlobalConfigFile::SetKey(const std::string &key, const std::string &value)
00442 {
00443         if( !m_appname.size() )
00444                 throw std::logic_error("Cannot use SetKey() without specifying an appname in the constructor.");
00445 
00446         if( value.find_first_of("\n\r") != std::string::npos )
00447                 throw std::logic_error("SetKey values may not contain newline characters.");
00448 
00449         std::string fullkey = "X-" + m_appname + "-" + key;
00450         m_keymap[fullkey] = value;
00451 }
00452 
00453 std::string GlobalConfigFile::GetKey(const std::string &key) const
00454 {
00455         if( !m_appname.size() )
00456                 throw std::logic_error("Cannot use SetKey() without specifying an appname in the constructor.");
00457 
00458         std::string fullkey = "X-" + m_appname + "-" + key;
00459         keymap_type::const_iterator ci = m_keymap.find(fullkey);
00460         if( ci == m_keymap.end() )
00461                 return "";
00462         return ci->second;
00463 }
00464 
00465 void GlobalConfigFile::SetLastDevice(const Barry::Pin &pin)
00466 {
00467         m_lastDevice = pin;
00468 }
00469 
00470 void GlobalConfigFile::SetVerboseLogging(bool verbose)
00471 {
00472         m_verboseLogging = verbose;
00473 }
00474 
00475 
00476 } // namespace Barry
00477 

Generated on 29 Mar 2010 for Barry by  doxygen 1.6.1