MythTV  master
configuration.cpp
Go to the documentation of this file.
1 // Program Name: configuration.cpp
3 // Created : Feb. 12, 2007
4 //
5 // Purpose : Configuration file Class
6 //
7 // Copyright (c) 2007 David Blain <dblain@mythtv.org>
8 //
9 // Licensed under the GPL v2 or later, see LICENSE for details
10 //
12 
13 #include "configuration.h"
14 
15 #include <unistd.h> // for fsync
16 
17 #include <iostream>
18 
19 #include <QDir>
20 #include <QFile>
21 #include <QTextStream>
22 
23 #include "mythlogging.h"
24 #include "mythdb.h"
25 #include "compat.h"
26 
27 
29 {
30  QString pathName = m_path + '/' + m_fileName;
31  QFile file(pathName);
32  return file.exists();
33 }
34 
36 {
37  QString pathName = m_path + '/' + m_fileName;
38 
39  LOG(VB_GENERAL, LOG_DEBUG, QString("Loading %1").arg(pathName));
40 
41  QFile file(pathName);
42 
43  if (file.exists() && !m_fileName.isEmpty()) // Ignore empty filenames
44  {
45 
46  if (!file.open(QIODevice::ReadOnly))
47  {
48  return false;
49  }
50 
51 #if QT_VERSION < QT_VERSION_CHECK(6,5,0)
52  QString error;
53  int line = 0;
54  int column = 0;
55  bool success = m_config.setContent(&file, false, &error, &line, &column);
56 
57  file.close();
58 
59  if (!success)
60  {
61  LOG(VB_GENERAL, LOG_ERR,
62  QString("Error parsing: %1 at line: %2 column: %3")
63  .arg(pathName, QString::number(line), QString::number(column)));
64 
65  LOG(VB_GENERAL, LOG_ERR, QString("Error Msg: %1").arg(error));
66  return false;
67  }
68 #else
69  auto parseresult = m_config.setContent(&file);
70 
71  file.close();
72 
73  if (!parseresult)
74  {
75  LOG(VB_GENERAL, LOG_ERR,
76  QString("Error parsing: %1 at line: %2 column: %3")
77  .arg(pathName, QString::number(parseresult.errorLine),
78  QString::number(parseresult.errorColumn)));
79 
80  LOG(VB_GENERAL, LOG_ERR, QString("Error Msg: %1").arg(parseresult.errorMessage));
81  return false;
82  }
83 #endif
84 
85  m_rootNode = m_config.namedItem("Configuration");
86  }
87  else
88  {
89  m_rootNode = m_config.createElement("Configuration");
90  m_config.appendChild(m_rootNode);
91  }
92 
93  return true;
94 }
95 
97 {
98  if (m_fileName.isEmpty()) // Special case. No file is created
99  {
100  return true;
101  }
102 
103  QString pathName = m_path + '/' + m_fileName;
104  QString backupName = pathName + ".old";
105 
106  LOG(VB_GENERAL, LOG_DEBUG, QString("Saving %1").arg(pathName));
107 
108  QFile file(pathName + ".new");
109 
110  if (!file.exists())
111  {
112  QDir directory(m_path);
113  if (!directory.exists() && !directory.mkdir(m_path))
114  {
115  LOG(VB_GENERAL, LOG_ERR, QString("Could not create %1").arg(m_path));
116  return false;
117  }
118  }
119 
120  if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
121  {
122  LOG(VB_GENERAL, LOG_ERR, QString("Could not open settings file %1 for writing").arg(file.fileName()));
123  return false;
124  }
125 
126  {
127  QTextStream ts(&file);
128  m_config.save(ts, 2);
129  }
130 
131  file.flush();
132  fsync(file.handle());
133  file.close();
134 
135  bool success = true;
136  if (QFile::exists(pathName))
137  {
138  if (QFile::exists(backupName) && !QFile::remove(backupName)) // if true, rename will fail
139  {
140  LOG(VB_GENERAL, LOG_ERR, QString("Failed to remove '%1', cannot backup current settings").arg(backupName));
141  }
142  success = QFile::rename(pathName, backupName); // backup old settings in case it fails
143  }
144 
145  if (success) // no settings to overwrite or settings backed up successfully
146  {
147  success = file.rename(pathName); // move new settings into target location
148  if (success)
149  {
150  if (QFile::exists(backupName) && !QFile::remove(backupName))
151  {
152  LOG(VB_GENERAL, LOG_WARNING, QString("Failed to remove '%1'").arg(backupName));
153  }
154  }
155  else if (QFile::exists(backupName) && !QFile::rename(backupName, pathName)) // !success &&
156  {
157  LOG(VB_GENERAL, LOG_ERR, QString("Failed to rename (restore) '%1").arg(backupName));
158  }
159  }
160 
161  if (!success)
162  {
163  LOG(VB_GENERAL, LOG_ERR, QString("Could not save settings file %1").arg(pathName));
164  }
165 
166  return success;
167 }
168 
169 QDomNode XmlConfiguration::FindNode(const QString &setting, bool create)
170 {
171  QStringList path = setting.split('/', Qt::SkipEmptyParts);
172  return FindNode(path, m_rootNode, create);
173 }
174 
175 QDomNode XmlConfiguration::FindNode(QStringList &path, QDomNode &current, bool create)
176 {
177  if (path.empty())
178  {
179  return current;
180  }
181 
182  QString name = path.front();
183  path.pop_front();
184 
185  QDomNode child = current.namedItem(name);
186 
187  if (child.isNull())
188  {
189  if (!create)
190  {
191  path.clear();
192  }
193  else if (!current.isNull()) // && create
194  {
195  child = current.appendChild(m_config.createElement(name));
196  }
197  }
198 
199  return FindNode(path, child, create);
200 }
201 
202 QString XmlConfiguration::GetValue(const QString &setting)
203 {
204  QDomNode node = FindNode(setting, false);
205  QDomText textNode;
206  // -=>TODO: This Always assumes firstChild is a Text Node... should change
207  if (!node.isNull())
208  {
209  textNode = node.firstChild().toText();
210  if (!textNode.isNull())
211  {
212  LOG(VB_GENERAL, LOG_DEBUG, QString("Got \"%1\" for \"%2\"").arg(textNode.nodeValue(), setting));
213 
214  return textNode.nodeValue();
215  }
216  }
217 
218  LOG(VB_GENERAL, LOG_DEBUG, QString("Using default for \"%1\"").arg(setting));
219  return {};
220 }
221 
222 void XmlConfiguration::SetValue(const QString &setting, const QString& value)
223 {
224  LOG(VB_GENERAL, LOG_DEBUG, QString("Setting \"%1\" to \"%2\"").arg(setting, value));
225 
226  QDomNode node = FindNode(setting, true);
227 
228  if (!node.isNull())
229  {
230  QDomText textNode;
231 
232  if (node.hasChildNodes())
233  {
234  // -=>TODO: This Always assumes only child is a Text Node... should change
235  textNode = node.firstChild().toText();
236  textNode.setNodeValue(value);
237  }
238  else
239  {
240  textNode = m_config.createTextNode(value);
241  node.appendChild(textNode);
242  }
243  }
244 }
245 
246 void XmlConfiguration::ClearValue(const QString &setting)
247 {
248  LOG(VB_GENERAL, LOG_DEBUG, QString("clearing %1").arg(setting));
249  QDomNode node = FindNode(setting, false);
250  if (!node.isNull())
251  {
252  QDomNode parent = node.parentNode();
253  parent.removeChild(node);
254  while (parent.childNodes().count() == 0)
255  {
256  QDomNode next_parent = parent.parentNode();
257  next_parent.removeChild(parent);
258  parent = next_parent;
259  }
260  }
261 }
XmlConfiguration::FileExists
bool FileExists()
Definition: configuration.cpp:28
error
static void error(const char *str,...)
Definition: vbi.cpp:37
mythdb.h
xbmcvfs.exists
bool exists(str path)
Definition: xbmcvfs.py:51
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
XmlConfiguration::m_rootNode
QDomNode m_rootNode
Definition: configuration.h:46
build_compdb.file
file
Definition: build_compdb.py:55
MythDate::current
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
XmlConfiguration::ClearValue
void ClearValue(const QString &setting)
Definition: configuration.cpp:246
XmlConfiguration::m_path
QString m_path
Definition: configuration.h:42
XmlConfiguration::SetValue
void SetValue(const QString &setting, bool value)
Definition: configuration.h:93
XmlConfiguration::m_config
QDomDocument m_config
Definition: configuration.h:45
mythlogging.h
compat.h
XmlConfiguration::Save
bool Save()
Definition: configuration.cpp:96
XmlConfiguration::Load
bool Load()
Definition: configuration.cpp:35
XmlConfiguration::GetValue
QString GetValue(const QString &setting)
Definition: configuration.cpp:202
XmlConfiguration::m_fileName
QString m_fileName
Definition: configuration.h:43
XmlConfiguration::FindNode
QDomNode FindNode(const QString &setting, bool create)
Definition: configuration.cpp:169
configuration.h
fsync
#define fsync(FD)
Definition: compat.h:76