1 /*
2  * %kadu copyright begin%
3  * Copyright 2014 Rafał Przemysław Malinowski (rafal.przemyslaw.malinowski@gmail.com)
4  * %kadu copyright end%
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <QtCore/QDateTime>
21 #include <QtCore/QVector>
22 #include <QtXml/QDomElement>
23 #include <QtXml/QDomNodeList>
24 
25 #include <errno.h>
26 
27 #include "configuration/configuration-read-error-exception.h"
28 #include "debug.h"
29 
30 #include "configuration-api.h"
31 
ConfigurationApi()32 ConfigurationApi::ConfigurationApi()
33 {
34 	auto root = DomDocument.createElement("Kadu");
35 	DomDocument.appendChild(root);
36 }
37 
ConfigurationApi(const QString & content)38 ConfigurationApi::ConfigurationApi(const QString &content)
39 {
40 	if (!DomDocument.setContent(content))
41 	{
42 		throw ConfigurationReadErrorException();
43 	}
44 
45 	if (DomDocument.documentElement().tagName() != "Kadu")
46 	{
47 		throw ConfigurationReadErrorException();
48 	}
49 }
50 
touch(const QString & version)51 void ConfigurationApi::touch(const QString &version)
52 {
53 	rootElement().setAttribute("last_save_time", QDateTime::currentDateTime().toString());
54 	rootElement().setAttribute("last_save_version", version);
55 }
56 
configuration() const57 QString ConfigurationApi::configuration() const
58 {
59 	return DomDocument.toString();
60 }
61 
rootElement()62 QDomElement ConfigurationApi::rootElement()
63 {
64 	return DomDocument.documentElement();
65 }
66 
createElement(QDomElement parent,const QString & tag_name)67 QDomElement ConfigurationApi::createElement(QDomElement parent, const QString& tag_name)
68 {
69 	const QDomElement &elem = DomDocument.createElement(tag_name);
70 	parent.appendChild(elem);
71 	return elem;
72 }
73 
findElement(const QDomElement & parent,const QString & tag_name) const74 QDomElement ConfigurationApi::findElement(const QDomElement &parent, const QString& tag_name) const
75 {
76 	return parent.firstChildElement(tag_name);
77 }
78 
findElementByProperty(const QDomElement & parent,const QString & tag_name,const QString & property_name,const QString & property_value) const79 QDomElement ConfigurationApi::findElementByProperty(const QDomElement &parent, const QString &tag_name,
80 	const QString &property_name, const QString &property_value) const
81 {
82 	for (QDomElement elem = parent.firstChildElement(tag_name); !elem.isNull(); elem = elem.nextSiblingElement(tag_name))
83 	{
84 		const QString &val = elem.attribute(property_name);
85 		if (val == property_value)
86 			return elem;
87 	}
88 	return QDomNode().toElement();
89 }
90 
findElementByFileNameProperty(const QDomElement & parent,const QString & tag_name,const QString & property_name,const QString & property_value) const91 QDomElement ConfigurationApi::findElementByFileNameProperty(const QDomElement &parent, const QString &tag_name,
92 	const QString &property_name, const QString &property_value) const
93 {
94 	for (QDomElement elem = parent.firstChildElement(tag_name); !elem.isNull(); elem = elem.nextSiblingElement(tag_name))
95 	{
96 		QString val = elem.attribute(property_name);
97 		if (val.section('/', -1).section('\\', -1) == property_value)
98 			return elem;
99 	}
100 	return QDomNode().toElement();
101 }
102 
accessElement(const QDomElement & parent,const QString & tag_name)103 QDomElement ConfigurationApi::accessElement(const QDomElement &parent, const QString& tag_name)
104 {
105 	const QDomElement &elem = findElement(parent, tag_name);
106 	if (elem.isNull())
107 		return createElement(parent, tag_name);
108 	else
109 		return elem;
110 }
111 
accessElementByProperty(const QDomElement & parent,const QString & tag_name,const QString & property_name,const QString & property_value)112 QDomElement ConfigurationApi::accessElementByProperty(const QDomElement &parent, const QString& tag_name,
113 	const QString& property_name, const QString& property_value)
114 {
115 	QDomElement elem = findElementByProperty(parent, tag_name,
116 		property_name, property_value);
117 	if (elem.isNull())
118 	{
119 		elem = createElement(parent, tag_name);
120 		elem.setAttribute(property_name, property_value);
121 	}
122 	return elem;
123 }
124 
accessElementByFileNameProperty(const QDomElement & parent,const QString & tag_name,const QString & property_name,const QString & property_value)125 QDomElement ConfigurationApi::accessElementByFileNameProperty(const QDomElement &parent, const QString& tag_name,
126 	const QString& property_name, const QString& property_value)
127 {
128 	QDomElement elem = findElementByFileNameProperty(parent, tag_name,
129 		property_name, property_value);
130 	if (elem.isNull())
131 	{
132 		elem = createElement(parent, tag_name);
133 		elem.setAttribute(property_name, property_value);
134 	}
135 	return elem;
136 }
137 
removeChildren(QDomElement parent)138 void ConfigurationApi::removeChildren(QDomElement parent)
139 {
140 	while (parent.hasChildNodes())
141 	{
142 		parent.firstChild().clear();
143 		parent.removeChild(parent.firstChild());
144 	}
145 }
146 
removeNodes(QDomElement parentNode,const QVector<QDomElement> & elements)147 void ConfigurationApi::removeNodes(QDomElement parentNode, const QVector<QDomElement> &elements)
148 {
149 	foreach (const QDomElement &element, elements)
150 		parentNode.removeChild(element);
151 }
152 
removeNamedNodes(QDomElement parentNode,const QVector<QDomElement> & elements,const QString & name)153 void ConfigurationApi::removeNamedNodes(QDomElement parentNode, const QVector<QDomElement> &elements, const QString &name)
154 {
155 	foreach (const QDomElement &element, elements)
156 		if (isElementNamed(element, name))
157 			parentNode.removeChild(element);
158 }
159 
removeUuidNodes(QDomElement parentNode,const QVector<QDomElement> & elements,const QString & uuid)160 void ConfigurationApi::removeUuidNodes(QDomElement parentNode, const QVector<QDomElement> &elements, const QString &uuid)
161 {
162 	foreach (const QDomElement &element, elements)
163 		if (isElementUuid(element, uuid))
164 			parentNode.removeChild(element);
165 }
166 
isElementNamed(const QDomElement & element,const QString & name)167 bool ConfigurationApi::isElementNamed(const QDomElement &element, const QString &name)
168 {
169 	return element.hasAttribute("name") && name == element.attribute("name");
170 }
171 
isElementUuid(const QDomElement & element,const QString & uuid)172 bool ConfigurationApi::isElementUuid(const QDomElement &element, const QString &uuid)
173 {
174 	return element.hasAttribute("uuid") && uuid == element.attribute("uuid");
175 }
176 
hasNode(const QString & nodeTagName)177 bool ConfigurationApi::hasNode(const QString &nodeTagName)
178 {
179 	return !getNode(nodeTagName, ModeFind).isNull();
180 }
181 
hasNode(const QDomElement & parentNode,const QString & nodeTagName)182 bool ConfigurationApi::hasNode(const QDomElement &parentNode, const QString &nodeTagName)
183 {
184 	return !getNode(parentNode, nodeTagName, ModeFind).isNull();
185 }
186 
getNode(const QString & nodeTagName,GetNodeMode getMode)187 QDomElement ConfigurationApi::getNode(const QString &nodeTagName, GetNodeMode getMode)
188 {
189 	return getNode(DomDocument.documentElement(), nodeTagName, getMode);
190 }
191 
getNamedNode(const QString & nodeTagName,const QString & nodeName,GetNodeMode getMode)192 QDomElement ConfigurationApi::getNamedNode(const QString &nodeTagName, const QString &nodeName, GetNodeMode getMode)
193 {
194 	return getNamedNode(DomDocument.documentElement(), nodeTagName, nodeName, getMode);
195 }
196 
getUuidNode(const QString & nodeTagName,const QString & nodeUuid,GetNodeMode getMode)197 QDomElement ConfigurationApi::getUuidNode(const QString &nodeTagName, const QString &nodeUuid, GetNodeMode getMode)
198 {
199 	return getUuidNode(DomDocument.documentElement(), nodeTagName, nodeUuid, getMode);
200 }
201 
getNode(QDomElement parentNode,const QString & nodeTagName,GetNodeMode getMode)202 QDomElement ConfigurationApi::getNode(QDomElement parentNode, const QString &nodeTagName, GetNodeMode getMode)
203 {
204 	if (ModeCreate == getMode)
205 	{
206 		QVector<QDomElement> nodes = getNodes(parentNode, nodeTagName);
207 		removeNodes(parentNode, nodes);
208 	}
209 	else if (ModeAppend != getMode)
210 	{
211 		QDomElement elem = parentNode.firstChildElement(nodeTagName);
212 		if (!elem.isNull())
213 			return elem;
214 	}
215 
216 	QDomElement result;
217 	if (ModeFind != getMode)
218 	{
219 		result = DomDocument.createElement(nodeTagName);
220 		parentNode.appendChild(result);
221 	}
222 
223 	return result;
224 }
225 
getNamedNode(QDomElement parentNode,const QString & nodeTagName,const QString & nodeName,GetNodeMode getMode)226 QDomElement ConfigurationApi::getNamedNode(QDomElement parentNode, const QString &nodeTagName, const QString &nodeName, GetNodeMode getMode)
227 {
228 	QVector<QDomElement> nodes = getNodes(parentNode, nodeTagName);
229 
230 	if (ModeCreate == getMode)
231 		removeNamedNodes(parentNode, nodes, nodeName);
232 
233 	if (ModeGet == getMode || ModeFind == getMode)
234 		foreach (const QDomElement &element, nodes)
235 			if (isElementNamed(element, nodeName))
236 				return element;
237 
238 	QDomElement result;
239 	if (ModeFind != getMode)
240 	{
241 		result = DomDocument.createElement(nodeTagName);
242 		result.setAttribute("name", nodeName);
243 		parentNode.appendChild(result);
244 	}
245 	return result;
246 }
247 
getUuidNode(QDomElement parentNode,const QString & nodeTagName,const QString & nodeUuid,GetNodeMode getMode)248 QDomElement ConfigurationApi::getUuidNode(QDomElement parentNode, const QString &nodeTagName, const QString &nodeUuid, GetNodeMode getMode)
249 {
250 	QVector<QDomElement> nodes = getNodes(parentNode, nodeTagName);
251 
252 	if (ModeCreate == getMode)
253 		removeUuidNodes(parentNode, nodes, nodeUuid);
254 
255 	if (ModeGet == getMode || ModeFind == getMode)
256 		foreach (const QDomElement &element, nodes)
257 			if (isElementUuid(element, nodeUuid))
258 				return element;
259 
260 	QDomElement result;
261 	if (ModeFind != getMode)
262 	{
263 		result = DomDocument.createElement(nodeTagName);
264 		result.setAttribute("uuid", nodeUuid);
265 		parentNode.appendChild(result);
266 	}
267 	return result;
268 }
269 
getNodes(const QDomElement & parent,const QString & nodeTagName)270 QVector<QDomElement> ConfigurationApi::getNodes(const QDomElement &parent, const QString &nodeTagName)
271 {
272 	QVector<QDomElement> result;
273 
274 	for (QDomElement elem = parent.firstChildElement(nodeTagName); !elem.isNull(); elem = elem.nextSiblingElement(nodeTagName))
275 	    result.append(elem);
276 
277 	return result;
278 }
279 
cdataOrText(const QString & text)280 QDomNode ConfigurationApi::cdataOrText(const QString &text)
281 {
282 	if (text.trimmed() != text)
283 		return DomDocument.createCDATASection(text);
284 	else
285 		return DomDocument.createTextNode(text);
286 }
287 
appendTextNode(const QDomElement & parentNode,const QString & nodeTagName,const QString & nodeContent)288 void ConfigurationApi::appendTextNode(const QDomElement &parentNode, const QString &nodeTagName, const QString &nodeContent)
289 {
290 	QDomElement element = getNode(parentNode, nodeTagName, ModeAppend);
291 	element.appendChild(cdataOrText(nodeContent));
292 }
293 
createTextNode(const QDomElement & parentNode,const QString & nodeTagName,const QString & nodeContent)294 void ConfigurationApi::createTextNode(const QDomElement &parentNode, const QString &nodeTagName, const QString &nodeContent)
295 {
296 	QDomElement element = getNode(parentNode, nodeTagName, ModeCreate);
297 	element.appendChild(cdataOrText(nodeContent));
298 }
299 
createNamedTextNode(const QDomElement & parentNode,const QString & nodeTagName,const QString & nodeName,const QString & nodeContent)300 void ConfigurationApi::createNamedTextNode(const QDomElement &parentNode, const QString &nodeTagName,
301 		const QString &nodeName, const QString &nodeContent)
302 {
303 	QDomElement element = getNamedNode(parentNode, nodeTagName, nodeName, ModeCreate);
304 	element.appendChild(cdataOrText(nodeContent));
305 }
306 
getTextNode(const QDomElement & parentNode,const QString & nodeTagName,const QString & defaultValue)307 QString ConfigurationApi::getTextNode(const QDomElement &parentNode, const QString &nodeTagName, const QString &defaultValue)
308 {
309 	QDomElement element = getNode(parentNode, nodeTagName, ModeFind);
310 	if (element.isNull())
311 		return defaultValue;
312 
313 	return element.text();
314 }
315 
removeNode(QDomElement parentNode,const QString & nodeTagName)316 void ConfigurationApi::removeNode(QDomElement parentNode, const QString& nodeTagName)
317 {
318 	QDomElement elementToRemove = getNode(parentNode, nodeTagName, ModeFind);
319 	while (!elementToRemove.isNull())
320 	{
321 		parentNode.removeChild(elementToRemove);
322 		elementToRemove = getNode(parentNode, nodeTagName, ModeFind);
323 	}
324 }
325