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
) {
90 //////////////////////////////////////////////////////////////////////////////
91 // ConfigFile class members
93 /// Loads config file for the given pin, and ends up in an
94 /// unenlightened state. Throws ConfigFileError on error,
95 /// but it is not an error if the config does not exist.
96 /// Never use this if you have a DatabaseDatabase object!
97 /// This ctor is only for temporary loading of config data.
98 ConfigFile::ConfigFile(Barry::Pin pin
)
101 , m_promptBackupLabel(false)
104 throw ConfigFileError("Configfile: empty pin");
107 BuildDefaultPath(); // this handles the situation that path is not set
111 /// Opens and loads config file for given pin, and calls Enlighten
112 /// Throws ConfigFileError on error. Should never fail unless
113 /// passed a bad pin.
114 ConfigFile::ConfigFile(Barry::Pin pin
,
115 const Barry::DatabaseDatabase
&db
)
118 , m_promptBackupLabel(false)
121 throw ConfigFileError("Configfile: empty pin");
129 ConfigFile::~ConfigFile()
133 void ConfigFile::BuildFilename()
135 size_t strsize
= 255 * 5;
136 char *strbuf
= new char[strsize
];
140 getpwuid_r(getuid(), &pwbuf
, strbuf
, strsize
, &pw
);
143 throw ConfigFileError("BuildFilename: getpwuid failed", errno
);
146 m_filename
= pw
->pw_dir
;
147 m_filename
+= "/.barry/backup/";
148 m_filename
+= m_pin
.Str();
149 m_filename
+= "/config";
154 void ConfigFile::BuildDefaultPath()
156 struct passwd
*pw
= getpwuid(getuid());
158 m_path
+= "/.barry/backup/";
159 m_path
+= m_pin
.Str();
162 void ConfigFile::Clear()
165 m_backupList
.clear();
166 m_restoreList
.clear();
167 m_deviceName
.clear();
168 m_promptBackupLabel
= false;
171 /// Attempt to load the configuration file, but do not fail if not available
172 void ConfigFile::Load()
178 std::ifstream
in(m_filename
.c_str(), std::ios::in
| std::ios::binary
);
183 DBListType
*pList
= 0;
185 while( std::getline(in
, line
) ) {
187 std::istringstream
iss(line
);
190 if( keyword
== "backup_list" ) {
191 pList
= &m_backupList
;
193 else if( keyword
== "restore_list" ) {
194 pList
= &m_restoreList
;
196 else if( line
[0] == ' ' && pList
) {
197 pList
->push_back(line
.c_str() + 1);
202 // add all remaining keyword checks here
203 if( keyword
== "device_name" ) {
205 std::getline(iss
, m_deviceName
);
206 if( m_deviceName
.size() == 0 ) {
207 // if there is a device_name setting,
208 // then this value must hold something,
209 // so that the user can ignore this
210 // field, and not get pestered all
215 else if( keyword
== "backup_path" ) {
217 std::getline(iss
, m_path
);
218 if( (m_path
.size() == 0) || !(CheckPath(m_path
)))
221 else if( keyword
== "prompt_backup_label" ) {
224 m_promptBackupLabel
= flag
;
232 /// Saves current device's config, overwriting or creating a config file
233 bool ConfigFile::Save()
235 if( !CheckPath(m_path
, &m_last_error
) )
238 std::ofstream
out(m_filename
.c_str(), std::ios::out
| std::ios::binary
);
240 m_last_error
= "Unable to open " + m_filename
+ " for writing.";
244 out
<< "backup_list" << std::endl
;
245 for( DBListType::iterator i
= m_backupList
.begin(); i
!= m_backupList
.end(); ++i
) {
246 out
<< " " << *i
<< std::endl
;
249 out
<< "restore_list" << std::endl
;
250 for( DBListType::iterator i
= m_restoreList
.begin(); i
!= m_restoreList
.end(); ++i
) {
251 out
<< " " << *i
<< std::endl
;
254 if( m_deviceName
.size() ) {
255 out
<< "device_name " << m_deviceName
<< std::endl
;
258 if( m_path
.size() ) {
259 out
<< "backup_path " << m_path
<< std::endl
;
262 out
<< "prompt_backup_label " << (m_promptBackupLabel
? 1 : 0) << std::endl
;
265 m_last_error
= "Error during write. Config may be incomplete.";
271 /// Compares a given databasedatabase from a real device with the
272 /// current config. If not yet configured, initialize with valid
274 void ConfigFile::Enlighten(const Barry::DatabaseDatabase
&db
)
277 // if not fully loaded, we use db as our default list
278 // our defaults are: backup everything, restore everything
281 m_backupList
.clear();
282 m_restoreList
.clear();
284 Barry::DatabaseDatabase::DatabaseArrayType::const_iterator i
=
285 db
.Databases
.begin();
286 for( ; i
!= db
.Databases
.end(); ++i
) {
288 m_backupList
.push_back(i
->Name
);
290 // restore everything except email (which could take ages)
291 // and Handheld Agent (which seems write protected)
292 if( i
->Name
!= Barry::Message::GetDBName() &&
293 i
->Name
!= "Handheld Agent" )
295 m_restoreList
.push_back(i
->Name
);
301 /// Sets list with new config
302 void ConfigFile::SetBackupList(const DBListType
&list
)
308 void ConfigFile::SetRestoreList(const DBListType
&list
)
310 m_restoreList
= list
;
314 void ConfigFile::SetDeviceName(const std::string
&name
)
322 void ConfigFile::SetBackupPath(const std::string
&path
)
324 if( path
.size() && CheckPath(path
) )
330 void ConfigFile::SetPromptBackupLabel(bool prompt
)
332 m_promptBackupLabel
= prompt
;
335 /// Checks that the path in path exists, and if not, creates it.
336 /// Returns false if unable to create path, true if ok.
337 bool ConfigFile::CheckPath(const std::string
&path
, std::string
*perr
)
339 if( path
.size() == 0 ) {
341 *perr
= "path is empty!";
345 if( access(path
.c_str(), F_OK
) == 0 )
349 std::string::size_type slash
= 0;
350 while( (slash
= path
.find('/', slash
+ 1)) != std::string::npos
) {
351 base
= path
.substr(0, slash
);
352 if( access(base
.c_str(), F_OK
) != 0 ) {
353 if( mkdir(base
.c_str(), 0755) == -1 ) {
355 *perr
= "mkdir(" + base
+ ") failed: ";
356 *perr
+= strerror(errno
);
362 if( mkdir(path
.c_str(), 0755) == -1 ) {
364 *perr
= "last mkdir(" + path
+ ") failed: ";
365 *perr
+= strerror(errno
);
374 //////////////////////////////////////////////////////////////////////////////
375 // GlobalConfigFile class members
377 GlobalConfigFile::GlobalConfigFile()
379 , m_verboseLogging(false)
385 GlobalConfigFile::GlobalConfigFile(const std::string
&appname
)
388 , m_verboseLogging(false)
390 // there can be no spaces in the appname
391 if( m_appname
.find(' ') != std::string::npos
)
392 throw std::logic_error("App name must have no spaces.");
398 GlobalConfigFile::~GlobalConfigFile()
402 void GlobalConfigFile::BuildFilename()
404 struct passwd
*pw
= getpwuid(getuid());
406 throw ConfigFileError("BuildFilename: getpwuid failed", errno
);
408 m_filename
= pw
->pw_dir
;
409 m_filename
+= "/.barry/config";
411 // build the global path too, since this never changes
416 void GlobalConfigFile::Clear()
422 void GlobalConfigFile::Load()
428 std::ifstream
in(m_filename
.c_str(), std::ios::in
| std::ios::binary
);
434 while( std::getline(in
, line
) ) {
436 std::istringstream
iss(line
);
439 if( keyword
== "last_device" ) {
441 m_lastDevice
.Clear();
444 else if( keyword
== "verbose_logging" ) {
447 m_verboseLogging
= flag
;
450 // store any other keys as app keys
451 if( keyword
.substr(0, 2) == "X-" ) {
454 std::getline(iss
, line
);
455 m_keymap
[keyword
] = line
;
463 /// Save the current global config, overwriting or creating as needed
464 bool GlobalConfigFile::Save()
466 if( !ConfigFile::CheckPath(m_path
, &m_last_error
) )
469 std::ofstream
out(m_filename
.c_str(), std::ios::out
| std::ios::binary
);
471 m_last_error
= "Unable to open " + m_filename
+ " for writing.";
475 if( !(m_lastDevice
== 0) ) {
476 out
<< "last_device " << m_lastDevice
.Str() << std::endl
;
479 out
<< "verbose_logging " << (m_verboseLogging
? 1 : 0) << std::endl
;
481 // store all app keys
482 keymap_type::const_iterator ci
= m_keymap
.begin();
483 for( ; ci
!= m_keymap
.end(); ++ci
) {
484 out
<< ci
->first
<< " " << ci
->second
<< std::endl
;
488 m_last_error
= "Error during write. Config may be incomplete.";
494 void GlobalConfigFile::SetKey(const std::string
&key
, const std::string
&value
)
496 if( !m_appname
.size() )
497 throw std::logic_error("Cannot use SetKey() without specifying an appname in the constructor.");
499 if( value
.find_first_of("\n\r") != std::string::npos
)
500 throw std::logic_error("SetKey values may not contain newline characters.");
502 std::string fullkey
= "X-" + m_appname
+ "-" + key
;
503 m_keymap
[fullkey
] = value
;
506 std::string
GlobalConfigFile::GetKey(const std::string
&key
,
507 const std::string
&default_value
) const
509 if( !m_appname
.size() )
510 throw std::logic_error("Cannot use SetKey() without specifying an appname in the constructor.");
512 std::string fullkey
= "X-" + m_appname
+ "-" + key
;
513 keymap_type::const_iterator ci
= m_keymap
.find(fullkey
);
514 if( ci
== m_keymap
.end() )
515 return default_value
;
519 void GlobalConfigFile::SetLastDevice(const Barry::Pin
&pin
)
524 void GlobalConfigFile::SetVerboseLogging(bool verbose
)
526 m_verboseLogging
= verbose
;