1 /*
2  * xmlcommon.cpp - helper functions for dealing with XML
3  * Copyright (C) 2001, 2002  Justin Karneges
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  *
19  */
20 
21 #include <QString>
22 #include <QDateTime>
23 #include <QSize>
24 #include <QRect>
25 #include <QStringList>
26 #include <QColor>
27 
28 #include "xmpp_xmlcommon.h"
29 #include "xmpp_stanza.h"
30 
31 //----------------------------------------------------------------------------
32 // XDomNodeList
33 //----------------------------------------------------------------------------
XDomNodeList()34 XDomNodeList::XDomNodeList()
35 {
36 }
37 
XDomNodeList(const XDomNodeList & from)38 XDomNodeList::XDomNodeList(const XDomNodeList &from) :
39 	list(from.list)
40 {
41 }
42 
XDomNodeList(const QDomNodeList & from)43 XDomNodeList::XDomNodeList(const QDomNodeList &from)
44 {
45 	for(int n = 0; n < from.count(); ++n)
46 		list += from.item(n);
47 }
48 
~XDomNodeList()49 XDomNodeList::~XDomNodeList()
50 {
51 }
52 
operator =(const XDomNodeList & from)53 XDomNodeList & XDomNodeList::operator=(const XDomNodeList &from)
54 {
55 	list = from.list;
56 	return *this;
57 }
58 
isEmpty() const59 bool XDomNodeList::isEmpty() const
60 {
61 	return list.isEmpty();
62 }
63 
item(int index) const64 QDomNode XDomNodeList::item(int index) const
65 {
66 	return list.value(index);
67 }
68 
length() const69 uint XDomNodeList::length() const
70 {
71 	return (uint)list.count();
72 }
73 
operator ==(const XDomNodeList & a) const74 bool XDomNodeList::operator==(const XDomNodeList &a) const
75 {
76 	return (list == a.list);
77 }
78 
append(const QDomNode & i)79 void XDomNodeList::append(const QDomNode &i)
80 {
81 	list += i;
82 }
83 
84 
stamp2TS(const QString & ts)85 QDateTime stamp2TS(const QString &ts)
86 {
87 	if(ts.length() != 17)
88 		return QDateTime();
89 
90 	int year  = ts.mid(0,4).toInt();
91 	int month = ts.mid(4,2).toInt();
92 	int day   = ts.mid(6,2).toInt();
93 
94 	int hour  = ts.mid(9,2).toInt();
95 	int min   = ts.mid(12,2).toInt();
96 	int sec   = ts.mid(15,2).toInt();
97 
98 	QDate xd;
99 	xd.setDate(year, month, day);
100 	if(!xd.isValid())
101 		return QDateTime();
102 
103 	QTime xt;
104 	xt.setHMS(hour, min, sec);
105 	if(!xt.isValid())
106 		return QDateTime();
107 
108 	return QDateTime(xd, xt);
109 }
110 
stamp2TS(const QString & ts,QDateTime * d)111 bool stamp2TS(const QString &ts, QDateTime *d)
112 {
113 	QDateTime dateTime = stamp2TS(ts);
114 	if (dateTime.isNull())
115 		return false;
116 
117 	*d = dateTime;
118 
119 	return true;
120 }
121 
TS2stamp(const QDateTime & d)122 QString TS2stamp(const QDateTime &d)
123 {
124 	QString str;
125 
126 	str.sprintf("%04d%02d%02dT%02d:%02d:%02d",
127 		d.date().year(),
128 		d.date().month(),
129 		d.date().day(),
130 		d.time().hour(),
131 		d.time().minute(),
132 		d.time().second());
133 
134 	return str;
135 }
136 
textTag(QDomDocument * doc,const QString & name,const QString & content)137 QDomElement textTag(QDomDocument *doc, const QString &name, const QString &content)
138 {
139 	QDomElement tag = doc->createElement(name);
140 	QDomText text = doc->createTextNode(content);
141 	tag.appendChild(text);
142 
143 	return tag;
144 }
145 
tagContent(const QDomElement & e)146 QString tagContent(const QDomElement &e)
147 {
148 	// look for some tag content
149 	for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
150 		QDomText i = n.toText();
151 		if(i.isNull())
152 			continue;
153 		return i.data();
154 	}
155 
156 	return "";
157 }
158 
159 
160 /**
161  * \brief obtain direct child elements of a certain kind.  unlike
162  *        elementsByTagNameNS, this function does not descend beyond the first
163  *        level of children.
164  * \param e parent element
165  * \param nsURI namespace of the elements to find
166  * \param localName local name of the elements to find
167  * \return the node list of found elements (empty list if none are found)
168  */
childElementsByTagNameNS(const QDomElement & e,const QString & nsURI,const QString & localName)169 XDomNodeList childElementsByTagNameNS(const QDomElement &e, const QString &nsURI, const QString &localName)
170 {
171 	XDomNodeList out;
172 	for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
173 		if(!n.isElement())
174 			continue;
175 		QDomElement i = n.toElement();
176 		if(i.namespaceURI() == nsURI && i.localName() == localName)
177 			out.append(i);
178 	}
179 	return out;
180 }
181 
182 
183 /**
184  * \brief create a new IQ stanza
185  * \param doc
186  * \param type
187  * \param to destination jid
188  * \param id stanza id
189  * \return the created stanza
190 */
createIQ(QDomDocument * doc,const QString & type,const QString & to,const QString & id)191 QDomElement createIQ(QDomDocument *doc, const QString &type, const QString &to, const QString &id)
192 {
193 	QDomElement iq = doc->createElement("iq");
194 	if(!type.isEmpty())
195 		iq.setAttribute("type", type);
196 	if(!to.isEmpty())
197 		iq.setAttribute("to", to);
198 	if(!id.isEmpty())
199 		iq.setAttribute("id", id);
200 
201 	return iq;
202 }
203 
204 /** \brief returns direct child element named "query"
205  * \return the element (or a null QDomElemnt if not found)
206 */
queryTag(const QDomElement & e)207 QDomElement queryTag(const QDomElement &e)
208 {
209 	return e.firstChildElement("query");
210 }
211 
queryNS(const QDomElement & e)212 QString queryNS(const QDomElement &e)
213 {
214 	return e.firstChildElement("query").attribute("xmlns");
215 }
216 
217 /**
218 	\brief Extracts the error code and description from the stanza element.
219 
220 	This function finds the error element in the given stanza element \a e.
221 
222 	You need to provide the base namespace of the stream to which this stanza belongs to
223 	(probably by using stream.baseNS() function).
224 
225 	The error description is either error text extracted from XML
226 	or - if no text is found - the error name and description (separated by '\n') taken from RFC-3920
227 	or - if the error is not defined in the RFC - the empty string.
228 
229 	Note: This function uses the Stanza::Error class,
230 	so it may guess missing values as defined in JEP-0086.
231 
232 	\param e	the element representing stanza
233 	\param baseNS	the base namespace of the stream
234 	\param code	if not NULL, will be filled with numeric error code
235 	\param str	if not NULL, will be filled with human readable error description
236 */
237 
getErrorFromElement(const QDomElement & e,const QString & baseNS,int * code,QString * str)238 void getErrorFromElement(const QDomElement &e, const QString &baseNS, int *code, QString *str)
239 {
240 	QDomElement tag = e.firstChildElement("error");
241 	if(tag.isNull())
242 		return;
243 
244 	XMPP::Stanza::Error err;
245 	err.fromXml(tag, baseNS);
246 
247 	if(code)
248 		*code = err.code();
249 	if(str) {
250 		QPair<QString, QString> desc = err.description();
251 		if (err.text.isEmpty())
252 			*str = desc.first + ".\n" + desc.second;
253 		else
254 			*str = desc.first + ".\n" + desc.second + "\n" + err.text;
255 	}
256 
257 }
258 
addCorrectNS(const QDomElement & e)259 QDomElement addCorrectNS(const QDomElement &e)
260 {
261 	int x;
262 
263 	// grab child nodes
264 	/*QDomDocumentFragment frag = e.ownerDocument().createDocumentFragment();
265 	QDomNodeList nl = e.childNodes();
266 	for(x = 0; x < nl.count(); ++x)
267 		frag.appendChild(nl.item(x).cloneNode());*/
268 
269 	// find closest xmlns
270 	QDomNode n = e;
271 	while(!n.isNull() && !n.toElement().hasAttribute("xmlns") && n.toElement().namespaceURI() == "" )
272 		n = n.parentNode();
273 	QString ns;
274 	if(n.isNull() || !n.toElement().hasAttribute("xmlns")){
275 		if (n.toElement().namespaceURI () == ""){
276 			ns = "jabber:client";
277 		} else {
278 			ns = n.toElement().namespaceURI();
279 		}
280 	} else {
281 		ns = n.toElement().attribute("xmlns");
282 	}
283 	// make a new node
284 	QDomElement i = e.ownerDocument().createElementNS(ns, e.tagName());
285 
286 	// copy attributes
287 	QDomNamedNodeMap al = e.attributes();
288 	for(x = 0; x < al.count(); ++x) {
289 		QDomAttr a = al.item(x).toAttr();
290 		if(a.name() != "xmlns")
291 			i.setAttributeNodeNS(a.cloneNode().toAttr());
292 	}
293 
294 	// copy children
295 	QDomNodeList nl = e.childNodes();
296 	for(x = 0; x < nl.count(); ++x) {
297 		QDomNode n = nl.item(x);
298 		if(n.isElement())
299 			i.appendChild(addCorrectNS(n.toElement()));
300 		else
301 			i.appendChild(n.cloneNode());
302 	}
303 
304 	//i.appendChild(frag);
305 	return i;
306 }
307 
308 //----------------------------------------------------------------------------
309 // XMLHelper
310 //----------------------------------------------------------------------------
311 
312 namespace XMLHelper {
313 
emptyTag(QDomDocument * doc,const QString & name)314 QDomElement emptyTag(QDomDocument *doc, const QString &name)
315 {
316 	QDomElement tag = doc->createElement(name);
317 
318 	return tag;
319 }
320 
hasSubTag(const QDomElement & e,const QString & name)321 bool hasSubTag(const QDomElement &e, const QString &name)
322 {
323 	return !e.firstChildElement(name).isNull();
324 }
325 
subTagText(const QDomElement & e,const QString & name)326 QString subTagText(const QDomElement &e, const QString &name)
327 {
328 	QDomElement i = e.firstChildElement(name);
329 	if ( !i.isNull() )
330 		return i.text();
331     return QString();
332 }
333 
textTag(QDomDocument & doc,const QString & name,const QString & content)334 QDomElement textTag(QDomDocument &doc, const QString &name, const QString &content)
335 {
336 	QDomElement tag = doc.createElement(name);
337 	QDomText text = doc.createTextNode(content);
338 	tag.appendChild(text);
339 
340 	return tag;
341 }
342 
textTag(QDomDocument & doc,const QString & name,int content)343 QDomElement textTag(QDomDocument &doc, const QString &name, int content)
344 {
345 	QDomElement tag = doc.createElement(name);
346 	QDomText text = doc.createTextNode(QString::number(content));
347 	tag.appendChild(text);
348 
349 	return tag;
350 }
351 
textTag(QDomDocument & doc,const QString & name,bool content)352 QDomElement textTag(QDomDocument &doc, const QString &name, bool content)
353 {
354 	QDomElement tag = doc.createElement(name);
355 	QDomText text = doc.createTextNode(content ? "true" : "false");
356 	tag.appendChild(text);
357 
358 	return tag;
359 }
360 
textTag(QDomDocument & doc,const QString & name,QSize & s)361 QDomElement textTag(QDomDocument &doc, const QString &name, QSize &s)
362 {
363 	QString str;
364 	str.sprintf("%d,%d", s.width(), s.height());
365 
366 	QDomElement tag = doc.createElement(name);
367 	QDomText text = doc.createTextNode(str);
368 	tag.appendChild(text);
369 
370 	return tag;
371 }
372 
textTag(QDomDocument & doc,const QString & name,QRect & r)373 QDomElement textTag(QDomDocument &doc, const QString &name, QRect &r)
374 {
375 	QString str;
376 	str.sprintf("%d,%d,%d,%d", r.x(), r.y(), r.width(), r.height());
377 
378 	QDomElement tag = doc.createElement(name);
379 	QDomText text = doc.createTextNode(str);
380 	tag.appendChild(text);
381 
382 	return tag;
383 }
384 
stringListToXml(QDomDocument & doc,const QString & name,const QStringList & l)385 QDomElement stringListToXml(QDomDocument &doc, const QString &name, const QStringList &l)
386 {
387 	QDomElement tag = doc.createElement(name);
388 	for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it)
389 		tag.appendChild(textTag(doc, "item", *it));
390 
391 	return tag;
392 }
393 
394 /*QString tagContent(const QDomElement &e)
395 {
396 	// look for some tag content
397 	for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
398 		QDomText i = n.toText();
399 		if(i.isNull())
400 			continue;
401 		return i.data();
402 	}
403 
404 	return "";
405 }*/
406 
407 /*QDomElement findSubTag(const QDomElement &e, const QString &name, bool *found)
408 {
409 	if(found)
410 		*found = FALSE;
411 
412 	for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
413 		QDomElement i = n.toElement();
414 		if(i.isNull())
415 			continue;
416 		if(i.tagName() == name) {
417 			if(found)
418 				*found = TRUE;
419 			return i;
420 		}
421 	}
422 
423 	QDomElement tmp;
424 	return tmp;
425 }*/
426 
readEntry(const QDomElement & e,const QString & name,QString * v)427 void readEntry(const QDomElement &e, const QString &name, QString *v)
428 {
429 	QDomElement tag = e.firstChildElement(name);
430 	if(tag.isNull())
431 		return;
432 	*v = tagContent(tag);
433 }
434 
readNumEntry(const QDomElement & e,const QString & name,int * v)435 void readNumEntry(const QDomElement &e, const QString &name, int *v)
436 {
437 	QDomElement tag = e.firstChildElement(name);
438 	if(tag.isNull())
439 		return;
440 	*v = tagContent(tag).toInt();
441 }
442 
readBoolEntry(const QDomElement & e,const QString & name,bool * v)443 void readBoolEntry(const QDomElement &e, const QString &name, bool *v)
444 {
445 	QDomElement tag = e.firstChildElement(name);
446 	if(tag.isNull())
447 		return;
448 	*v = (tagContent(tag) == "true") ? true: false;
449 }
450 
readSizeEntry(const QDomElement & e,const QString & name,QSize * v)451 void readSizeEntry(const QDomElement &e, const QString &name, QSize *v)
452 {
453 	QDomElement tag = e.firstChildElement(name);
454 	if(tag.isNull())
455 		return;
456 	QStringList list = tagContent(tag).split(',');
457 	if(list.count() != 2)
458 		return;
459 	QSize s;
460 	s.setWidth(list[0].toInt());
461 	s.setHeight(list[1].toInt());
462 	*v = s;
463 }
464 
readRectEntry(const QDomElement & e,const QString & name,QRect * v)465 void readRectEntry(const QDomElement &e, const QString &name, QRect *v)
466 {
467 	QDomElement tag = e.firstChildElement(name);
468 	if(tag.isNull())
469 		return;
470 	QStringList list = tagContent(tag).split(',');
471 	if(list.count() != 4)
472 		return;
473 	QRect r;
474 	r.setX(list[0].toInt());
475 	r.setY(list[1].toInt());
476 	r.setWidth(list[2].toInt());
477 	r.setHeight(list[3].toInt());
478 	*v = r;
479 }
480 
readColorEntry(const QDomElement & e,const QString & name,QColor * v)481 void readColorEntry(const QDomElement &e, const QString &name, QColor *v)
482 {
483 	QDomElement tag = e.firstChildElement(name);
484 	if(tag.isNull())
485 		return;
486 	QColor c;
487 	c.setNamedColor(tagContent(tag));
488 	if(c.isValid())
489 		*v = c;
490 }
491 
xmlToStringList(const QDomElement & e,const QString & name,QStringList * v)492 void xmlToStringList(const QDomElement &e, const QString &name, QStringList *v)
493 {
494 	QDomElement tag = e.firstChildElement(name);
495 	if(tag.isNull())
496 		return;
497 	QStringList list;
498 	for(QDomNode n = tag.firstChild(); !n.isNull(); n = n.nextSibling()) {
499 		QDomElement i = n.toElement();
500 		if(i.isNull())
501 			continue;
502 		if(i.tagName() == "item")
503 			list += tagContent(i);
504 	}
505 	*v = list;
506 }
507 
setBoolAttribute(QDomElement e,const QString & name,bool b)508 void setBoolAttribute(QDomElement e, const QString &name, bool b)
509 {
510 	e.setAttribute(name, b ? "true" : "false");
511 }
512 
readBoolAttribute(QDomElement e,const QString & name,bool * v)513 void readBoolAttribute(QDomElement e, const QString &name, bool *v)
514 {
515 	if(e.hasAttribute(name)) {
516 		QString s = e.attribute(name);
517 		*v = (s == "true") ? true: false;
518 	}
519 }
520 
521 };
522 
523