2 /// \file configfile.cc
3 /// Barry configuraion class, for one device PIN
7 Copyright (C) 2007-2012, 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"
33 #include <sys/types.h>
38 /// Creates a tar.gz filename using PIN + date + time + label.
39 /// Does not include any path, just returns a new filename.
40 std::string
MakeBackupFilename(const Barry::Pin
&pin
,
41 const std::string
&label
)
45 time_t t
= time(NULL
);
46 struct tm
*lt
= localtime(&t
);
48 std::string fileLabel
= label
;
49 if( fileLabel
.size() ) {
51 fileLabel
.insert(fileLabel
.begin(), '-');
53 // translate all spaces and slashes
54 for( size_t i
= 0; i
< fileLabel
.size(); i
++ ) {
55 if( fileLabel
[i
] == ' ' )
57 else if( fileLabel
[i
] == '/' )
59 else if( fileLabel
[i
] == '\\' )
64 ostringstream tarfilename
;
65 tarfilename
<< pin
.Str() << "-"
66 << setw(4) << setfill('0') << (lt
->tm_year
+ 1900)
67 << setw(2) << setfill('0') << (lt
->tm_mon
+ 1)
68 << setw(2) << setfill('0') << lt
->tm_mday
70 << setw(2) << setfill('0') << lt
->tm_hour
71 << setw(2) << setfill('0') << lt
->tm_min
72 << setw(2) << setfill('0') << lt
->tm_sec
75 return tarfilename
.str();
78 bool ConfigFile::DBListType::IsSelected(const std::string
&dbname
) const
80 const_iterator i
= begin();
81 for( ; i
!= end(); ++i
) {
89 std::ostream
& operator<< (std::ostream
&os
, const ConfigFile::DBListType
&list
)
91 os
<< "DBListType dump:\n";
93 for( ConfigFile::DBListType::const_iterator i
= list
.begin();
97 os
<< " " << *i
<< "\n";
103 //////////////////////////////////////////////////////////////////////////////
104 // ConfigFile class members
106 /// Loads config file for the given pin, and ends up in an
107 /// unenlightened state. Throws ConfigFileError on error,
108 /// but it is not an error if the config does not exist.
109 /// Never use this if you have a DatabaseDatabase object!
110 /// This ctor is only for temporary loading of config data.
111 ConfigFile::ConfigFile(Barry::Pin pin
)
114 , m_promptBackupLabel(false)
115 , m_autoSelectAll(false)
118 throw ConfigFileError("Configfile: empty pin");
121 BuildDefaultPath(); // this handles the situation that path is not set
125 /// Opens and loads config file for given pin, and calls Enlighten
126 /// Throws ConfigFileError on error. Should never fail unless
127 /// passed a bad pin.
128 ConfigFile::ConfigFile(Barry::Pin pin
,
129 const Barry::DatabaseDatabase
&db
)
132 , m_promptBackupLabel(false)
133 , m_autoSelectAll(false)
136 throw ConfigFileError("Configfile: empty pin");
144 ConfigFile::~ConfigFile()
148 void ConfigFile::BuildFilename()
150 size_t strsize
= 255 * 5;
151 char *strbuf
= new char[strsize
];
155 getpwuid_r(getuid(), &pwbuf
, strbuf
, strsize
, &pw
);
158 throw ConfigFileError("BuildFilename: getpwuid failed", errno
);
161 m_filename
= pw
->pw_dir
;
162 m_filename
+= "/.barry/backup/";
163 m_filename
+= m_pin
.Str();
164 m_filename
+= "/config";
169 void ConfigFile::BuildDefaultPath()
171 struct passwd
*pw
= getpwuid(getuid());
173 m_path
+= "/.barry/backup/";
174 m_path
+= m_pin
.Str();
177 void ConfigFile::Clear()
180 m_backupList
.clear();
181 m_restoreList
.clear();
182 m_deviceName
.clear();
183 m_promptBackupLabel
= false;
184 m_autoSelectAll
= false;
187 /// Attempt to load the configuration file, but do not fail if not available
188 void ConfigFile::Load()
194 std::ifstream
in(m_filename
.c_str(), std::ios::in
| std::ios::binary
);
199 DBListType
*pList
= 0;
201 while( std::getline(in
, line
) ) {
203 std::istringstream
iss(line
);
206 if( keyword
== "backup_list" ) {
207 pList
= &m_backupList
;
209 else if( keyword
== "restore_list" ) {
210 pList
= &m_restoreList
;
212 else if( line
[0] == ' ' && pList
) {
213 pList
->push_back(line
.c_str() + 1);
218 // add all remaining keyword checks here
219 if( keyword
== "device_name" ) {
221 std::getline(iss
, m_deviceName
);
222 if( m_deviceName
.size() == 0 ) {
223 // if there is a device_name setting,
224 // then this value must hold something,
225 // so that the user can ignore this
226 // field, and not get pestered all
231 else if( keyword
== "backup_path" ) {
233 std::getline(iss
, m_path
);
234 if( (m_path
.size() == 0) || !(CheckPath(m_path
)))
237 else if( keyword
== "prompt_backup_label" ) {
240 m_promptBackupLabel
= flag
;
242 else if( keyword
== "auto_select_all" ) {
245 m_autoSelectAll
= flag
;
253 /// Saves current device's config, overwriting or creating a config file
254 bool ConfigFile::Save()
258 if( !CheckPath(m_path
, &m_last_error
) )
261 ofstream
out(m_filename
.c_str(), std::ios::out
| std::ios::binary
);
263 m_last_error
= "Unable to open " + m_filename
+ " for writing.";
267 out
<< "backup_list" << endl
;
268 for( DBListType::iterator i
= m_backupList
.begin(); i
!= m_backupList
.end(); ++i
) {
269 out
<< " " << *i
<< endl
;
272 out
<< "restore_list" << endl
;
273 for( DBListType::iterator i
= m_restoreList
.begin(); i
!= m_restoreList
.end(); ++i
) {
274 out
<< " " << *i
<< endl
;
277 if( m_deviceName
.size() ) {
278 out
<< "device_name " << m_deviceName
<< endl
;
281 if( m_path
.size() ) {
282 out
<< "backup_path " << m_path
<< endl
;
285 out
<< "prompt_backup_label " << (m_promptBackupLabel
? 1 : 0) << endl
;
286 out
<< "auto_select_all " << (m_autoSelectAll
? 1 : 0) << endl
;
289 m_last_error
= "Error during write. Config may be incomplete.";
295 /// Compares a given databasedatabase from a real device with the
296 /// current config. If not yet configured, initialize with valid
298 void ConfigFile::Enlighten(const Barry::DatabaseDatabase
&db
)
301 // if not fully loaded, we use db as our default list
302 // our defaults are: backup everything, restore everything
305 m_backupList
.clear();
306 m_restoreList
.clear();
308 Barry::DatabaseDatabase::DatabaseArrayType::const_iterator i
=
309 db
.Databases
.begin();
310 for( ; i
!= db
.Databases
.end(); ++i
) {
312 m_backupList
.push_back(i
->Name
);
314 // restore everything except email (which could take ages)
315 // and Handheld Agent (which seems write protected)
316 if( i
->Name
!= Barry::Message::GetDBName() &&
317 i
->Name
!= "Handheld Agent" )
319 m_restoreList
.push_back(i
->Name
);
325 // fill list with all databases from dbdb
326 ConfigFile:: DBListType
& ConfigFile::DBListType::operator=(const DatabaseDatabase
&dbdb
)
331 // copy over all DB names
332 DatabaseDatabase::DatabaseArrayType::const_iterator
333 i
= dbdb
.Databases
.begin(), e
= dbdb
.Databases
.end();
334 for( ; i
!= e
; ++i
) {
341 /// Sets list with new config
342 void ConfigFile::SetBackupList(const DBListType
&list
)
348 void ConfigFile::SetRestoreList(const DBListType
&list
)
350 m_restoreList
= list
;
354 void ConfigFile::SetDeviceName(const std::string
&name
)
362 void ConfigFile::SetBackupPath(const std::string
&path
)
364 if( path
.size() && CheckPath(path
) )
370 void ConfigFile::SetPromptBackupLabel(bool prompt
)
372 m_promptBackupLabel
= prompt
;
375 void ConfigFile::SetAutoSelectAll(bool asa
)
377 m_autoSelectAll
= asa
;
380 /// Checks that the path in path exists, and if not, creates it.
381 /// Returns false if unable to create path, true if ok.
382 bool ConfigFile::CheckPath(const std::string
&path
, std::string
*perr
)
384 if( path
.size() == 0 ) {
386 *perr
= "path is empty!";
390 if( access(path
.c_str(), F_OK
) == 0 )
394 std::string::size_type slash
= 0;
395 while( (slash
= path
.find('/', slash
+ 1)) != std::string::npos
) {
396 base
= path
.substr(0, slash
);
397 if( access(base
.c_str(), F_OK
) != 0 ) {
398 if( mkdir(base
.c_str(), 0755) == -1 ) {
400 *perr
= "mkdir(" + base
+ ") failed: ";
401 *perr
+= strerror(errno
);
407 if( mkdir(path
.c_str(), 0755) == -1 ) {
409 *perr
= "last mkdir(" + path
+ ") failed: ";
410 *perr
+= strerror(errno
);
419 //////////////////////////////////////////////////////////////////////////////
420 // GlobalConfigFile class members
422 GlobalConfigFile::GlobalConfigFile()
424 , m_verboseLogging(false)
430 GlobalConfigFile::GlobalConfigFile(const std::string
&appname
)
433 , m_verboseLogging(false)
435 // there can be no spaces in the appname
436 if( m_appname
.find(' ') != std::string::npos
)
437 throw std::logic_error("App name must have no spaces.");
443 GlobalConfigFile::~GlobalConfigFile()
447 void GlobalConfigFile::BuildFilename()
449 struct passwd
*pw
= getpwuid(getuid());
451 throw ConfigFileError("BuildFilename: getpwuid failed", errno
);
453 m_filename
= pw
->pw_dir
;
454 m_filename
+= "/.barry/config";
456 // build the global path too, since this never changes
461 void GlobalConfigFile::Clear()
467 void GlobalConfigFile::Load()
473 std::ifstream
in(m_filename
.c_str(), std::ios::in
| std::ios::binary
);
479 while( std::getline(in
, line
) ) {
481 std::istringstream
iss(line
);
484 if( keyword
== "last_device" ) {
486 m_lastDevice
.Clear();
489 else if( keyword
== "verbose_logging" ) {
492 m_verboseLogging
= flag
;
495 // store any other keys as app keys
496 if( keyword
.substr(0, 2) == "X-" ) {
499 std::getline(iss
, line
);
500 m_keymap
[keyword
] = line
;
508 /// Save the current global config, overwriting or creating as needed
509 bool GlobalConfigFile::Save()
511 if( !ConfigFile::CheckPath(m_path
, &m_last_error
) )
514 std::ofstream
out(m_filename
.c_str(), std::ios::out
| std::ios::binary
);
516 m_last_error
= "Unable to open " + m_filename
+ " for writing.";
520 if( !(m_lastDevice
== 0) ) {
521 out
<< "last_device " << m_lastDevice
.Str() << std::endl
;
524 out
<< "verbose_logging " << (m_verboseLogging
? 1 : 0) << std::endl
;
526 // store all app keys
527 keymap_type::const_iterator ci
= m_keymap
.begin();
528 for( ; ci
!= m_keymap
.end(); ++ci
) {
529 out
<< ci
->first
<< " " << ci
->second
<< std::endl
;
533 m_last_error
= "Error during write. Config may be incomplete.";
539 void GlobalConfigFile::SetKey(const std::string
&key
, const std::string
&value
)
541 if( !m_appname
.size() )
542 throw std::logic_error("Cannot use SetKey() without specifying an appname in the constructor.");
544 if( value
.find_first_of("\n\r") != std::string::npos
)
545 throw std::logic_error("SetKey values may not contain newline characters.");
547 std::string fullkey
= "X-" + m_appname
+ "-" + key
;
548 m_keymap
[fullkey
] = value
;
551 std::string
GlobalConfigFile::GetKey(const std::string
&key
,
552 const std::string
&default_value
) const
554 if( !m_appname
.size() )
555 throw std::logic_error("Cannot use SetKey() without specifying an appname in the constructor.");
557 std::string fullkey
= "X-" + m_appname
+ "-" + key
;
558 keymap_type::const_iterator ci
= m_keymap
.find(fullkey
);
559 if( ci
== m_keymap
.end() )
560 return default_value
;
564 void GlobalConfigFile::SetLastDevice(const Barry::Pin
&pin
)
569 void GlobalConfigFile::SetVerboseLogging(bool verbose
)
571 m_verboseLogging
= verbose
;