2 /// \file configfile.cc
3 /// Barry configuraion class, for one device PIN
7 Copyright (C) 2007-2010, Net Direct Inc. (http://www.netdirect.ca/)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
22 #include "configfile.h"
24 #include "r_message.h"
31 #include <sys/types.h>
36 bool ConfigFile::DBListType::IsSelected(const std::string
&dbname
) const
38 const_iterator i
= begin();
39 for( ; i
!= end(); ++i
) {
48 //////////////////////////////////////////////////////////////////////////////
49 // ConfigFile class members
51 /// Loads config file for the given pin, and ends up in an
52 /// unenlightened state. Throws ConfigFileError on error,
53 /// but it is not an error if the config does not exist.
54 /// Never use this if you have a DatabaseDatabase object!
55 /// This ctor is only for temporary loading of config data.
56 ConfigFile::ConfigFile(Barry::Pin pin
)
59 , m_promptBackupLabel(false)
62 throw ConfigFileError("Configfile: empty pin");
65 BuildDefaultPath(); // this handles the situation that path is not set
69 /// Opens and loads config file for given pin, and calls Enlighten
70 /// Throws ConfigFileError on error. Should never fail unless
72 ConfigFile::ConfigFile(Barry::Pin pin
,
73 const Barry::DatabaseDatabase
&db
)
76 , m_promptBackupLabel(false)
79 throw ConfigFileError("Configfile: empty pin");
87 ConfigFile::~ConfigFile()
91 void ConfigFile::BuildFilename()
93 struct passwd
*pw
= getpwuid(getuid());
95 throw ConfigFileError("BuildFilename: getpwuid failed", errno
);
97 m_filename
= pw
->pw_dir
;
98 m_filename
+= "/.barry/backup/";
99 m_filename
+= m_pin
.str();
100 m_filename
+= "/config";
103 void ConfigFile::BuildDefaultPath()
105 struct passwd
*pw
= getpwuid(getuid());
107 m_path
+= "/.barry/backup/";
108 m_path
+= m_pin
.str();
111 void ConfigFile::Clear()
114 m_backupList
.clear();
115 m_restoreList
.clear();
116 m_deviceName
.clear();
117 m_promptBackupLabel
= false;
120 /// Attempt to load the configuration file, but do not fail if not available
121 void ConfigFile::Load()
127 std::ifstream
in(m_filename
.c_str(), std::ios::in
| std::ios::binary
);
132 DBListType
*pList
= 0;
134 while( std::getline(in
, line
) ) {
136 std::istringstream
iss(line
);
139 if( keyword
== "backup_list" ) {
140 pList
= &m_backupList
;
142 else if( keyword
== "restore_list" ) {
143 pList
= &m_restoreList
;
145 else if( line
[0] == ' ' && pList
) {
146 pList
->push_back(line
.c_str() + 1);
151 // add all remaining keyword checks here
152 if( keyword
== "device_name" ) {
154 std::getline(iss
, m_deviceName
);
155 if( m_deviceName
.size() == 0 ) {
156 // if there is a device_name setting,
157 // then this value must hold something,
158 // so that the user can ignore this
159 // field, and not get pestered all
164 else if( keyword
== "backup_path" ) {
166 std::getline(iss
, m_path
);
167 if( (m_path
.size() == 0) || !(CheckPath(m_path
)))
170 else if( keyword
== "prompt_backup_label" ) {
173 m_promptBackupLabel
= flag
;
181 /// Saves current device's config, overwriting or creating a config file
182 bool ConfigFile::Save()
184 if( !CheckPath(m_path
, &m_last_error
) )
187 std::ofstream
out(m_filename
.c_str(), std::ios::out
| std::ios::binary
);
189 m_last_error
= "Unable to open " + m_filename
+ " for writing.";
193 out
<< "backup_list" << std::endl
;
194 for( DBListType::iterator i
= m_backupList
.begin(); i
!= m_backupList
.end(); ++i
) {
195 out
<< " " << *i
<< std::endl
;
198 out
<< "restore_list" << std::endl
;
199 for( DBListType::iterator i
= m_restoreList
.begin(); i
!= m_restoreList
.end(); ++i
) {
200 out
<< " " << *i
<< std::endl
;
203 if( m_deviceName
.size() ) {
204 out
<< "device_name " << m_deviceName
<< std::endl
;
207 if( m_path
.size() ) {
208 out
<< "backup_path " << m_path
<< std::endl
;
211 out
<< "prompt_backup_label " << (m_promptBackupLabel
? 1 : 0) << std::endl
;
214 m_last_error
= "Error during write. Config may be incomplete.";
220 /// Compares a given databasedatabase from a real device with the
221 /// current config. If not yet configured, initialize with valid
223 void ConfigFile::Enlighten(const Barry::DatabaseDatabase
&db
)
226 // if not fully loaded, we use db as our default list
227 // our defaults are: backup everything, restore everything
230 m_backupList
.clear();
231 m_restoreList
.clear();
233 Barry::DatabaseDatabase::DatabaseArrayType::const_iterator i
=
234 db
.Databases
.begin();
235 for( ; i
!= db
.Databases
.end(); ++i
) {
237 m_backupList
.push_back(i
->Name
);
239 // restore everything except email (which could take ages)
240 // and Handheld Agent (which seems write protected)
241 if( i
->Name
!= Barry::Message::GetDBName() &&
242 i
->Name
!= "Handheld Agent" )
244 m_restoreList
.push_back(i
->Name
);
250 /// Sets list with new config
251 void ConfigFile::SetBackupList(const DBListType
&list
)
257 void ConfigFile::SetRestoreList(const DBListType
&list
)
259 m_restoreList
= list
;
263 void ConfigFile::SetDeviceName(const std::string
&name
)
271 void ConfigFile::SetBackupPath(const std::string
&path
)
273 if( path
.size() && CheckPath(path
) )
279 void ConfigFile::SetPromptBackupLabel(bool prompt
)
281 m_promptBackupLabel
= prompt
;
284 /// Checks that the path in path exists, and if not, creates it.
285 /// Returns false if unable to create path, true if ok.
286 bool ConfigFile::CheckPath(const std::string
&path
, std::string
*perr
)
288 if( path
.size() == 0 ) {
290 *perr
= "path is empty!";
294 if( access(path
.c_str(), F_OK
) == 0 )
298 std::string::size_type slash
= 0;
299 while( (slash
= path
.find('/', slash
+ 1)) != std::string::npos
) {
300 base
= path
.substr(0, slash
);
301 if( access(base
.c_str(), F_OK
) != 0 ) {
302 if( mkdir(base
.c_str(), 0755) == -1 ) {
304 *perr
= "mkdir(" + base
+ ") failed: ";
305 *perr
+= strerror(errno
);
311 if( mkdir(path
.c_str(), 0755) == -1 ) {
313 *perr
= "last mkdir(" + path
+ ") failed: ";
314 *perr
+= strerror(errno
);
323 //////////////////////////////////////////////////////////////////////////////
324 // GlobalConfigFile class members
326 GlobalConfigFile::GlobalConfigFile()
328 , m_verboseLogging(false)
334 GlobalConfigFile::GlobalConfigFile(const std::string
&appname
)
337 , m_verboseLogging(false)
339 // there can be no spaces in the appname
340 if( m_appname
.find(' ') != std::string::npos
)
341 throw std::logic_error("App name must have no spaces.");
347 GlobalConfigFile::~GlobalConfigFile()
351 void GlobalConfigFile::BuildFilename()
353 struct passwd
*pw
= getpwuid(getuid());
355 throw ConfigFileError("BuildFilename: getpwuid failed", errno
);
357 m_filename
= pw
->pw_dir
;
358 m_filename
+= "/.barry/config";
360 // build the global path too, since this never changes
365 void GlobalConfigFile::Clear()
371 void GlobalConfigFile::Load()
377 std::ifstream
in(m_filename
.c_str(), std::ios::in
| std::ios::binary
);
383 while( std::getline(in
, line
) ) {
385 std::istringstream
iss(line
);
388 if( keyword
== "last_device" ) {
390 m_lastDevice
.clear();
393 else if( keyword
== "verbose_logging" ) {
396 m_verboseLogging
= flag
;
399 // store any other keys as app keys
400 if( keyword
.substr(0, 2) == "X-" ) {
403 std::getline(iss
, line
);
404 m_keymap
[keyword
] = line
;
412 /// Save the current global config, overwriting or creating as needed
413 bool GlobalConfigFile::Save()
415 if( !ConfigFile::CheckPath(m_path
, &m_last_error
) )
418 std::ofstream
out(m_filename
.c_str(), std::ios::out
| std::ios::binary
);
420 m_last_error
= "Unable to open " + m_filename
+ " for writing.";
424 if( !(m_lastDevice
== 0) ) {
425 out
<< "last_device " << m_lastDevice
.str() << std::endl
;
428 out
<< "verbose_logging " << (m_verboseLogging
? 1 : 0) << std::endl
;
430 // store all app keys
431 keymap_type::const_iterator ci
= m_keymap
.begin();
432 for( ; ci
!= m_keymap
.end(); ++ci
) {
433 out
<< ci
->first
<< " " << ci
->second
<< std::endl
;
437 m_last_error
= "Error during write. Config may be incomplete.";
443 void GlobalConfigFile::SetKey(const std::string
&key
, const std::string
&value
)
445 if( !m_appname
.size() )
446 throw std::logic_error("Cannot use SetKey() without specifying an appname in the constructor.");
448 if( value
.find_first_of("\n\r") != std::string::npos
)
449 throw std::logic_error("SetKey values may not contain newline characters.");
451 std::string fullkey
= "X-" + m_appname
+ "-" + key
;
452 m_keymap
[fullkey
] = value
;
455 std::string
GlobalConfigFile::GetKey(const std::string
&key
) const
457 if( !m_appname
.size() )
458 throw std::logic_error("Cannot use SetKey() without specifying an appname in the constructor.");
460 std::string fullkey
= "X-" + m_appname
+ "-" + key
;
461 keymap_type::const_iterator ci
= m_keymap
.find(fullkey
);
462 if( ci
== m_keymap
.end() )
467 void GlobalConfigFile::SetLastDevice(const Barry::Pin
&pin
)
472 void GlobalConfigFile::SetVerboseLogging(bool verbose
)
474 m_verboseLogging
= verbose
;