menu: added new Keywords tag to .desktop files
[barry.git] / src / configfile.cc
blobf69acd0c220c1efa9b3943e23d7fda6625cbdaea
1 ///
2 /// \file configfile.cc
3 /// Barry configuraion class, for one device PIN
4 ///
6 /*
7 Copyright (C) 2007-2013, 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 "i18n.h"
23 #include "configfile.h"
24 #include "error.h"
25 #include "r_message.h"
26 #include <string.h>
27 #include <unistd.h>
28 #include <fstream>
29 #include <sstream>
30 #include <iostream>
31 #include <iomanip>
32 #include <sys/types.h>
33 #include <sys/stat.h>
35 namespace Barry {
37 bool ConfigFile::DBListType::IsSelected(const std::string &dbname) const
39 const_iterator i = begin();
40 for( ; i != end(); ++i ) {
41 if( *i == dbname ) {
42 return true;
45 return false;
48 std::ostream& operator<< (std::ostream &os, const ConfigFile::DBListType &list)
50 os << _("DBListType dump:\n");
52 for( ConfigFile::DBListType::const_iterator i = list.begin();
53 i != list.end();
54 ++i )
56 os << " " << *i << "\n";
58 return os;
62 //////////////////////////////////////////////////////////////////////////////
63 // ConfigFile class members
65 /// Loads config file for the given pin, and ends up in an
66 /// unenlightened state. Throws ConfigFileError on error,
67 /// but it is not an error if the config does not exist.
68 /// Never use this if you have a DatabaseDatabase object!
69 /// This ctor is only for temporary loading of config data.
70 ConfigFile::ConfigFile(Barry::Pin pin)
71 : m_pin(pin)
72 , m_loaded(false)
73 , m_promptBackupLabel(false)
74 , m_autoSelectAll(false)
76 if( m_pin == 0 )
77 throw ConfigFileError(_("Configfile: empty pin"));
79 BuildFilename();
80 BuildDefaultPath(); // this handles the situation that path is not set
81 Load();
84 /// Opens and loads config file for given pin, and calls Enlighten
85 /// Throws ConfigFileError on error. Should never fail unless
86 /// passed a bad pin.
87 ConfigFile::ConfigFile(Barry::Pin pin,
88 const Barry::DatabaseDatabase &db)
89 : m_pin(pin)
90 , m_loaded(false)
91 , m_promptBackupLabel(false)
92 , m_autoSelectAll(false)
94 if( m_pin == 0 )
95 throw ConfigFileError(_("Configfile: empty pin"));
97 BuildFilename();
98 BuildDefaultPath();
99 Load();
100 Enlighten(db);
103 ConfigFile::~ConfigFile()
107 void ConfigFile::Clear()
109 m_loaded = false;
110 m_backupList.clear();
111 m_restoreList.clear();
112 m_deviceName.clear();
113 m_promptBackupLabel = false;
114 m_autoSelectAll = false;
117 /// Attempt to load the configuration file, but do not fail if not available
118 void ConfigFile::Load()
120 // start fresh
121 Clear();
123 // open input file
124 std::ifstream in(m_filename.c_str(), std::ios::in | std::ios::binary);
125 if( !in )
126 return;
128 std::string line;
129 DBListType *pList = 0;
131 while( std::getline(in, line) ) {
132 std::string keyword;
133 std::istringstream iss(line);
134 iss >> keyword;
136 if( keyword == "backup_list" ) {
137 pList = &m_backupList;
139 else if( keyword == "restore_list" ) {
140 pList = &m_restoreList;
142 else if( line[0] == ' ' && pList ) {
143 pList->push_back(line.c_str() + 1);
145 else {
146 pList = 0;
148 // add all remaining keyword checks here
149 if( keyword == "device_name" ) {
150 iss >> std::ws;
151 std::getline(iss, m_deviceName);
152 if( m_deviceName.size() == 0 ) {
153 // if there is a device_name setting,
154 // then this value must hold something,
155 // so that the user can ignore this
156 // field, and not get pestered all
157 // the time
158 m_deviceName = " ";
161 else if( keyword == "backup_path" ) {
162 iss >> std::ws;
163 std::getline(iss, m_path);
164 if( (m_path.size() == 0) || !(CheckPath(m_path)))
165 BuildDefaultPath();
167 else if( keyword == "prompt_backup_label" ) {
168 int flag;
169 iss >> flag;
170 m_promptBackupLabel = flag != 0;
172 else if( keyword == "auto_select_all" ) {
173 int flag;
174 iss >> flag;
175 m_autoSelectAll = flag != 0;
180 m_loaded = true;
183 /// Saves current device's config, overwriting or creating a config file
184 bool ConfigFile::Save()
186 using namespace std;
188 if( !CheckPath(m_path, &m_last_error) )
189 return false;
191 ofstream out(m_filename.c_str(), std::ios::out | std::ios::binary);
192 if( !out ) {
193 m_last_error = _("Unable to open file for writing: ") + m_filename;
194 return false;
197 out << "backup_list" << endl;
198 for( DBListType::iterator i = m_backupList.begin(); i != m_backupList.end(); ++i ) {
199 out << " " << *i << endl;
202 out << "restore_list" << endl;
203 for( DBListType::iterator i = m_restoreList.begin(); i != m_restoreList.end(); ++i ) {
204 out << " " << *i << endl;
207 if( m_deviceName.size() ) {
208 out << "device_name " << m_deviceName << endl;
211 if( m_path.size() ) {
212 out << "backup_path " << m_path << endl;
215 out << "prompt_backup_label " << (m_promptBackupLabel ? 1 : 0) << endl;
216 out << "auto_select_all " << (m_autoSelectAll ? 1 : 0) << endl;
218 if( !out ) {
219 m_last_error = _("Error during write. Config may be incomplete.");
220 return false;
222 return true;
225 /// Compares a given databasedatabase from a real device with the
226 /// current config. If not yet configured, initialize with valid
227 /// defaults.
228 void ConfigFile::Enlighten(const Barry::DatabaseDatabase &db)
230 if( !m_loaded ) {
231 // if not fully loaded, we use db as our default list
232 // our defaults are: backup everything, restore everything
233 // except email
235 m_backupList.clear();
236 m_restoreList.clear();
238 Barry::DatabaseDatabase::DatabaseArrayType::const_iterator i =
239 db.Databases.begin();
240 for( ; i != db.Databases.end(); ++i ) {
241 // backup everything
242 m_backupList.push_back(i->Name);
244 // restore everything except email (which could take ages)
245 // and Handheld Agent (which seems write protected)
246 if( i->Name != Barry::Message::GetDBName() &&
247 i->Name != "Handheld Agent" )
249 m_restoreList.push_back(i->Name);
255 // fill list with all databases from dbdb
256 ConfigFile:: DBListType& ConfigFile::DBListType::operator=(const DatabaseDatabase &dbdb)
258 // start empty
259 clear();
261 // copy over all DB names
262 DatabaseDatabase::DatabaseArrayType::const_iterator
263 i = dbdb.Databases.begin(), e = dbdb.Databases.end();
264 for( ; i != e; ++i ) {
265 push_back(i->Name);
268 return *this;
271 /// Sets list with new config
272 void ConfigFile::SetBackupList(const DBListType &list)
274 m_backupList = list;
275 m_loaded = true;
278 void ConfigFile::SetRestoreList(const DBListType &list)
280 m_restoreList = list;
281 m_loaded = true;
284 void ConfigFile::SetDeviceName(const std::string &name)
286 if( name.size() )
287 m_deviceName = name;
288 else
289 m_deviceName = " ";
292 void ConfigFile::SetBackupPath(const std::string &path)
294 if( path.size() && CheckPath(path) )
295 m_path = path;
296 else
297 BuildDefaultPath();
300 void ConfigFile::SetPromptBackupLabel(bool prompt)
302 m_promptBackupLabel = prompt;
305 void ConfigFile::SetAutoSelectAll(bool asa)
307 m_autoSelectAll = asa;
311 //////////////////////////////////////////////////////////////////////////////
312 // GlobalConfigFile class members
314 GlobalConfigFile::GlobalConfigFile()
315 : m_loaded(false)
316 , m_verboseLogging(false)
318 BuildFilename();
319 Load();
322 GlobalConfigFile::GlobalConfigFile(const std::string &appname)
323 : m_loaded(false)
324 , m_appname(appname)
325 , m_verboseLogging(false)
327 // there can be no spaces in the appname
328 if( m_appname.find(' ') != std::string::npos )
329 throw std::logic_error(_("App name must have no spaces."));
331 BuildFilename();
332 Load();
335 GlobalConfigFile::~GlobalConfigFile()
339 void GlobalConfigFile::Clear()
341 m_loaded = false;
342 m_lastDevice = 0;
345 void GlobalConfigFile::Load()
347 // start fresh
348 Clear();
350 // open input file
351 std::ifstream in(m_filename.c_str(), std::ios::in | std::ios::binary);
352 if( !in )
353 return;
355 std::string line;
357 while( std::getline(in, line) ) {
358 std::string keyword;
359 std::istringstream iss(line);
360 iss >> keyword;
362 if( keyword == "last_device" ) {
363 iss >> std::ws;
364 m_lastDevice.Clear();
365 iss >> m_lastDevice;
367 else if( keyword == "verbose_logging" ) {
368 int flag = 0;
369 iss >> flag;
370 m_verboseLogging = flag != 0;
372 else {
373 // store any other keys as app keys
374 if( keyword.substr(0, 2) == "X-" ) {
375 iss >> std::ws;
376 line.clear();
377 std::getline(iss, line);
378 m_keymap[keyword] = line;
383 m_loaded = true;
386 /// Save the current global config, overwriting or creating as needed
387 bool GlobalConfigFile::Save()
389 if( !ConfigFile::CheckPath(m_path, &m_last_error) )
390 return false;
392 std::ofstream out(m_filename.c_str(), std::ios::out | std::ios::binary);
393 if( !out ) {
394 m_last_error = _("Unable to open file for writing: ") + m_filename;
395 return false;
398 if( !(m_lastDevice == 0) ) {
399 out << "last_device " << m_lastDevice.Str() << std::endl;
402 out << "verbose_logging " << (m_verboseLogging ? 1 : 0) << std::endl;
404 // store all app keys
405 keymap_type::const_iterator ci = m_keymap.begin();
406 for( ; ci != m_keymap.end(); ++ci ) {
407 out << ci->first << " " << ci->second << std::endl;
410 if( !out ) {
411 m_last_error = _("Error during write. Config may be incomplete.");
412 return false;
414 return true;
417 void GlobalConfigFile::SetKey(const std::string &key, const std::string &value)
419 if( !m_appname.size() )
420 throw std::logic_error(_("Cannot use SetKey() without specifying an appname in the constructor."));
422 if( value.find_first_of("\n\r") != std::string::npos )
423 throw std::logic_error(_("SetKey values may not contain newline characters."));
425 std::string fullkey = "X-" + m_appname + "-" + key;
426 m_keymap[fullkey] = value;
429 std::string GlobalConfigFile::GetKey(const std::string &key,
430 const std::string &default_value) const
432 if( !m_appname.size() )
433 throw std::logic_error(_("Cannot use SetKey() without specifying an appname in the constructor."));
435 std::string fullkey = "X-" + m_appname + "-" + key;
436 keymap_type::const_iterator ci = m_keymap.find(fullkey);
437 if( ci == m_keymap.end() )
438 return default_value;
439 return ci->second;
442 void GlobalConfigFile::SetLastDevice(const Barry::Pin &pin)
444 m_lastDevice = pin;
447 void GlobalConfigFile::SetVerboseLogging(bool verbose)
449 m_verboseLogging = verbose;
453 } // namespace Barry