2 /// \file configfile.cc
3 /// Barry configuraion class, for one device PIN
7 Copyright (C) 2007-2011, 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 size_t strsize
= 255 * 5;
94 char *strbuf
= new char[strsize
];
98 getpwuid_r(getuid(), &pwbuf
, strbuf
, strsize
, &pw
);
101 throw ConfigFileError("BuildFilename: getpwuid failed", errno
);
104 m_filename
= pw
->pw_dir
;
105 m_filename
+= "/.barry/backup/";
106 m_filename
+= m_pin
.Str();
107 m_filename
+= "/config";
112 void ConfigFile::BuildDefaultPath()
114 struct passwd
*pw
= getpwuid(getuid());
116 m_path
+= "/.barry/backup/";
117 m_path
+= m_pin
.Str();
120 void ConfigFile::Clear()
123 m_backupList
.clear();
124 m_restoreList
.clear();
125 m_deviceName
.clear();
126 m_promptBackupLabel
= false;
129 /// Attempt to load the configuration file, but do not fail if not available
130 void ConfigFile::Load()
136 std::ifstream
in(m_filename
.c_str(), std::ios::in
| std::ios::binary
);
141 DBListType
*pList
= 0;
143 while( std::getline(in
, line
) ) {
145 std::istringstream
iss(line
);
148 if( keyword
== "backup_list" ) {
149 pList
= &m_backupList
;
151 else if( keyword
== "restore_list" ) {
152 pList
= &m_restoreList
;
154 else if( line
[0] == ' ' && pList
) {
155 pList
->push_back(line
.c_str() + 1);
160 // add all remaining keyword checks here
161 if( keyword
== "device_name" ) {
163 std::getline(iss
, m_deviceName
);
164 if( m_deviceName
.size() == 0 ) {
165 // if there is a device_name setting,
166 // then this value must hold something,
167 // so that the user can ignore this
168 // field, and not get pestered all
173 else if( keyword
== "backup_path" ) {
175 std::getline(iss
, m_path
);
176 if( (m_path
.size() == 0) || !(CheckPath(m_path
)))
179 else if( keyword
== "prompt_backup_label" ) {
182 m_promptBackupLabel
= flag
;
190 /// Saves current device's config, overwriting or creating a config file
191 bool ConfigFile::Save()
193 if( !CheckPath(m_path
, &m_last_error
) )
196 std::ofstream
out(m_filename
.c_str(), std::ios::out
| std::ios::binary
);
198 m_last_error
= "Unable to open " + m_filename
+ " for writing.";
202 out
<< "backup_list" << std::endl
;
203 for( DBListType::iterator i
= m_backupList
.begin(); i
!= m_backupList
.end(); ++i
) {
204 out
<< " " << *i
<< std::endl
;
207 out
<< "restore_list" << std::endl
;
208 for( DBListType::iterator i
= m_restoreList
.begin(); i
!= m_restoreList
.end(); ++i
) {
209 out
<< " " << *i
<< std::endl
;
212 if( m_deviceName
.size() ) {
213 out
<< "device_name " << m_deviceName
<< std::endl
;
216 if( m_path
.size() ) {
217 out
<< "backup_path " << m_path
<< std::endl
;
220 out
<< "prompt_backup_label " << (m_promptBackupLabel
? 1 : 0) << std::endl
;
223 m_last_error
= "Error during write. Config may be incomplete.";
229 /// Compares a given databasedatabase from a real device with the
230 /// current config. If not yet configured, initialize with valid
232 void ConfigFile::Enlighten(const Barry::DatabaseDatabase
&db
)
235 // if not fully loaded, we use db as our default list
236 // our defaults are: backup everything, restore everything
239 m_backupList
.clear();
240 m_restoreList
.clear();
242 Barry::DatabaseDatabase::DatabaseArrayType::const_iterator i
=
243 db
.Databases
.begin();
244 for( ; i
!= db
.Databases
.end(); ++i
) {
246 m_backupList
.push_back(i
->Name
);
248 // restore everything except email (which could take ages)
249 // and Handheld Agent (which seems write protected)
250 if( i
->Name
!= Barry::Message::GetDBName() &&
251 i
->Name
!= "Handheld Agent" )
253 m_restoreList
.push_back(i
->Name
);
259 /// Sets list with new config
260 void ConfigFile::SetBackupList(const DBListType
&list
)
266 void ConfigFile::SetRestoreList(const DBListType
&list
)
268 m_restoreList
= list
;
272 void ConfigFile::SetDeviceName(const std::string
&name
)
280 void ConfigFile::SetBackupPath(const std::string
&path
)
282 if( path
.size() && CheckPath(path
) )
288 void ConfigFile::SetPromptBackupLabel(bool prompt
)
290 m_promptBackupLabel
= prompt
;
293 /// Checks that the path in path exists, and if not, creates it.
294 /// Returns false if unable to create path, true if ok.
295 bool ConfigFile::CheckPath(const std::string
&path
, std::string
*perr
)
297 if( path
.size() == 0 ) {
299 *perr
= "path is empty!";
303 if( access(path
.c_str(), F_OK
) == 0 )
307 std::string::size_type slash
= 0;
308 while( (slash
= path
.find('/', slash
+ 1)) != std::string::npos
) {
309 base
= path
.substr(0, slash
);
310 if( access(base
.c_str(), F_OK
) != 0 ) {
311 if( mkdir(base
.c_str(), 0755) == -1 ) {
313 *perr
= "mkdir(" + base
+ ") failed: ";
314 *perr
+= strerror(errno
);
320 if( mkdir(path
.c_str(), 0755) == -1 ) {
322 *perr
= "last mkdir(" + path
+ ") failed: ";
323 *perr
+= strerror(errno
);
332 //////////////////////////////////////////////////////////////////////////////
333 // GlobalConfigFile class members
335 GlobalConfigFile::GlobalConfigFile()
337 , m_verboseLogging(false)
343 GlobalConfigFile::GlobalConfigFile(const std::string
&appname
)
346 , m_verboseLogging(false)
348 // there can be no spaces in the appname
349 if( m_appname
.find(' ') != std::string::npos
)
350 throw std::logic_error("App name must have no spaces.");
356 GlobalConfigFile::~GlobalConfigFile()
360 void GlobalConfigFile::BuildFilename()
362 struct passwd
*pw
= getpwuid(getuid());
364 throw ConfigFileError("BuildFilename: getpwuid failed", errno
);
366 m_filename
= pw
->pw_dir
;
367 m_filename
+= "/.barry/config";
369 // build the global path too, since this never changes
374 void GlobalConfigFile::Clear()
380 void GlobalConfigFile::Load()
386 std::ifstream
in(m_filename
.c_str(), std::ios::in
| std::ios::binary
);
392 while( std::getline(in
, line
) ) {
394 std::istringstream
iss(line
);
397 if( keyword
== "last_device" ) {
399 m_lastDevice
.Clear();
402 else if( keyword
== "verbose_logging" ) {
405 m_verboseLogging
= flag
;
408 // store any other keys as app keys
409 if( keyword
.substr(0, 2) == "X-" ) {
412 std::getline(iss
, line
);
413 m_keymap
[keyword
] = line
;
421 /// Save the current global config, overwriting or creating as needed
422 bool GlobalConfigFile::Save()
424 if( !ConfigFile::CheckPath(m_path
, &m_last_error
) )
427 std::ofstream
out(m_filename
.c_str(), std::ios::out
| std::ios::binary
);
429 m_last_error
= "Unable to open " + m_filename
+ " for writing.";
433 if( !(m_lastDevice
== 0) ) {
434 out
<< "last_device " << m_lastDevice
.Str() << std::endl
;
437 out
<< "verbose_logging " << (m_verboseLogging
? 1 : 0) << std::endl
;
439 // store all app keys
440 keymap_type::const_iterator ci
= m_keymap
.begin();
441 for( ; ci
!= m_keymap
.end(); ++ci
) {
442 out
<< ci
->first
<< " " << ci
->second
<< std::endl
;
446 m_last_error
= "Error during write. Config may be incomplete.";
452 void GlobalConfigFile::SetKey(const std::string
&key
, const std::string
&value
)
454 if( !m_appname
.size() )
455 throw std::logic_error("Cannot use SetKey() without specifying an appname in the constructor.");
457 if( value
.find_first_of("\n\r") != std::string::npos
)
458 throw std::logic_error("SetKey values may not contain newline characters.");
460 std::string fullkey
= "X-" + m_appname
+ "-" + key
;
461 m_keymap
[fullkey
] = value
;
464 std::string
GlobalConfigFile::GetKey(const std::string
&key
,
465 const std::string
&default_value
) const
467 if( !m_appname
.size() )
468 throw std::logic_error("Cannot use SetKey() without specifying an appname in the constructor.");
470 std::string fullkey
= "X-" + m_appname
+ "-" + key
;
471 keymap_type::const_iterator ci
= m_keymap
.find(fullkey
);
472 if( ci
== m_keymap
.end() )
473 return default_value
;
477 void GlobalConfigFile::SetLastDevice(const Barry::Pin
&pin
)
482 void GlobalConfigFile::SetVerboseLogging(bool verbose
)
484 m_verboseLogging
= verbose
;