2015-12-10 05:06:13 +02:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
|
|
|
** Copyright (C) 2015 The Qt Company Ltd.
|
2019-06-03 14:21:30 +00:00
|
|
|
** Copyright (C) 2016-2019 Ivailo Monev
|
2015-12-10 05:06:13 +02:00
|
|
|
**
|
2019-06-03 13:38:02 +00:00
|
|
|
** This file is part of the QtDBus module of the Katie Toolkit.
|
2015-12-10 05:06:13 +02:00
|
|
|
**
|
|
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
|
|
|
** Commercial License Usage
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
** and conditions see http://www.qt.io/terms-conditions. For further
|
|
|
|
** information use the contact form at http://www.qt.io/contact-us.
|
|
|
|
**
|
|
|
|
** GNU Lesser General Public License Usage
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
|
|
** General Public License version 2.1 or version 3 as published by the Free
|
|
|
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
|
|
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
|
|
|
** following information to ensure the GNU Lesser General Public License
|
|
|
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
|
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
|
|
**
|
|
|
|
** As a special exception, The Qt Company gives you certain additional
|
|
|
|
** rights. These rights are described in The Qt Company LGPL Exception
|
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
**
|
|
|
|
** GNU General Public License Usage
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
** General Public License version 3.0 as published by the Free Software
|
|
|
|
** Foundation and appearing in the file LICENSE.GPL included in the
|
|
|
|
** packaging of this file. Please review the following information to
|
|
|
|
** ensure the GNU General Public License version 3.0 requirements will be
|
|
|
|
** met: http://www.gnu.org/copyleft/gpl.html.
|
|
|
|
**
|
|
|
|
** $QT_END_LICENSE$
|
|
|
|
**
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include "qdbusxmlparser_p.h"
|
|
|
|
#include "qdbusinterface.h"
|
|
|
|
#include "qdbusinterface_p.h"
|
|
|
|
#include "qdbusconnection_p.h"
|
|
|
|
#include "qdbusutil_p.h"
|
|
|
|
|
|
|
|
#include <QtXml/qdom.h>
|
|
|
|
#include <QtCore/qmap.h>
|
|
|
|
#include <QtCore/qvariant.h>
|
|
|
|
#include <QtCore/qtextstream.h>
|
|
|
|
|
|
|
|
|
|
|
|
//#define QDBUS_PARSER_DEBUG
|
|
|
|
#ifdef QDBUS_PARSER_DEBUG
|
|
|
|
# define qDBusParserError qWarning
|
|
|
|
#else
|
|
|
|
# define qDBusParserError if (true) {} else qDebug
|
|
|
|
#endif
|
|
|
|
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
|
|
|
static QDBusIntrospection::Annotations
|
|
|
|
parseAnnotations(const QDomElement& elem)
|
|
|
|
{
|
|
|
|
QDBusIntrospection::Annotations retval;
|
|
|
|
QDomNodeList list = elem.elementsByTagName(QLatin1String("annotation"));
|
|
|
|
for (int i = 0; i < list.count(); ++i)
|
|
|
|
{
|
|
|
|
QDomElement ann = list.item(i).toElement();
|
|
|
|
if (ann.isNull())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
QString name = ann.attribute(QLatin1String("name")),
|
|
|
|
value = ann.attribute(QLatin1String("value"));
|
|
|
|
|
|
|
|
if (!QDBusUtil::isValidInterfaceName(name)) {
|
|
|
|
qDBusParserError("Invalid D-BUS annotation '%s' found while parsing introspection",
|
|
|
|
qPrintable(name));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
retval.insert(name, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static QDBusIntrospection::Arguments
|
|
|
|
parseArgs(const QDomElement& elem, const QLatin1String& direction, bool acceptEmpty)
|
|
|
|
{
|
|
|
|
QDBusIntrospection::Arguments retval;
|
|
|
|
QDomNodeList list = elem.elementsByTagName(QLatin1String("arg"));
|
|
|
|
for (int i = 0; i < list.count(); ++i)
|
|
|
|
{
|
|
|
|
QDomElement arg = list.item(i).toElement();
|
|
|
|
if (arg.isNull())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if ((acceptEmpty && !arg.hasAttribute(QLatin1String("direction"))) ||
|
|
|
|
arg.attribute(QLatin1String("direction")) == direction) {
|
|
|
|
|
|
|
|
QDBusIntrospection::Argument argData;
|
|
|
|
if (arg.hasAttribute(QLatin1String("name")))
|
|
|
|
argData.name = arg.attribute(QLatin1String("name")); // can be empty
|
|
|
|
argData.type = arg.attribute(QLatin1String("type"));
|
|
|
|
if (!QDBusUtil::isValidSingleSignature(argData.type)) {
|
|
|
|
qDBusParserError("Invalid D-BUS type signature '%s' found while parsing introspection",
|
|
|
|
qPrintable(argData.type));
|
|
|
|
}
|
|
|
|
|
|
|
|
retval << argData;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
QDBusXmlParser::QDBusXmlParser(const QString& service, const QString& path,
|
|
|
|
const QString& xmlData)
|
|
|
|
: m_service(service), m_path(path)
|
|
|
|
{
|
|
|
|
QDomDocument doc;
|
|
|
|
doc.setContent(xmlData);
|
|
|
|
m_node = doc.firstChildElement(QLatin1String("node"));
|
|
|
|
}
|
|
|
|
|
|
|
|
QDBusXmlParser::QDBusXmlParser(const QString& service, const QString& path,
|
|
|
|
const QDomElement& node)
|
|
|
|
: m_service(service), m_path(path), m_node(node)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QDBusIntrospection::Interfaces
|
|
|
|
QDBusXmlParser::interfaces() const
|
|
|
|
{
|
|
|
|
QDBusIntrospection::Interfaces retval;
|
|
|
|
|
|
|
|
if (m_node.isNull())
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
QDomNodeList interfaceList = m_node.elementsByTagName(QLatin1String("interface"));
|
|
|
|
for (int i = 0; i < interfaceList.count(); ++i)
|
|
|
|
{
|
|
|
|
QDomElement iface = interfaceList.item(i).toElement();
|
|
|
|
QString ifaceName = iface.attribute(QLatin1String("name"));
|
|
|
|
if (iface.isNull())
|
|
|
|
continue; // for whatever reason
|
|
|
|
if (!QDBusUtil::isValidInterfaceName(ifaceName)) {
|
|
|
|
qDBusParserError("Invalid D-BUS interface name '%s' found while parsing introspection",
|
|
|
|
qPrintable(ifaceName));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
QDBusIntrospection::Interface *ifaceData = new QDBusIntrospection::Interface;
|
|
|
|
ifaceData->name = ifaceName;
|
|
|
|
{
|
|
|
|
// save the data
|
|
|
|
QTextStream ts(&ifaceData->introspection);
|
|
|
|
iface.save(ts,2);
|
|
|
|
}
|
|
|
|
|
|
|
|
// parse annotations
|
|
|
|
ifaceData->annotations = parseAnnotations(iface);
|
|
|
|
|
|
|
|
// parse methods
|
|
|
|
QDomNodeList list = iface.elementsByTagName(QLatin1String("method"));
|
|
|
|
for (int j = 0; j < list.count(); ++j)
|
|
|
|
{
|
|
|
|
QDomElement method = list.item(j).toElement();
|
|
|
|
QString methodName = method.attribute(QLatin1String("name"));
|
|
|
|
if (method.isNull())
|
|
|
|
continue;
|
|
|
|
if (!QDBusUtil::isValidMemberName(methodName)) {
|
|
|
|
qDBusParserError("Invalid D-BUS member name '%s' found in interface '%s' while parsing introspection",
|
|
|
|
qPrintable(methodName), qPrintable(ifaceName));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
QDBusIntrospection::Method methodData;
|
|
|
|
methodData.name = methodName;
|
|
|
|
|
|
|
|
// parse arguments
|
|
|
|
methodData.inputArgs = parseArgs(method, QLatin1String("in"), true);
|
|
|
|
methodData.outputArgs = parseArgs(method, QLatin1String("out"), false);
|
|
|
|
methodData.annotations = parseAnnotations(method);
|
|
|
|
|
|
|
|
// add it
|
|
|
|
ifaceData->methods.insert(methodName, methodData);
|
|
|
|
}
|
|
|
|
|
|
|
|
// parse signals
|
|
|
|
list = iface.elementsByTagName(QLatin1String("signal"));
|
|
|
|
for (int j = 0; j < list.count(); ++j)
|
|
|
|
{
|
|
|
|
QDomElement signal = list.item(j).toElement();
|
|
|
|
QString signalName = signal.attribute(QLatin1String("name"));
|
|
|
|
if (signal.isNull())
|
|
|
|
continue;
|
|
|
|
if (!QDBusUtil::isValidMemberName(signalName)) {
|
|
|
|
qDBusParserError("Invalid D-BUS member name '%s' found in interface '%s' while parsing introspection",
|
|
|
|
qPrintable(signalName), qPrintable(ifaceName));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
QDBusIntrospection::Signal signalData;
|
|
|
|
signalData.name = signalName;
|
|
|
|
|
|
|
|
// parse data
|
|
|
|
signalData.outputArgs = parseArgs(signal, QLatin1String("out"), true);
|
|
|
|
signalData.annotations = parseAnnotations(signal);
|
|
|
|
|
|
|
|
// add it
|
|
|
|
ifaceData->signals_.insert(signalName, signalData);
|
|
|
|
}
|
|
|
|
|
|
|
|
// parse properties
|
|
|
|
list = iface.elementsByTagName(QLatin1String("property"));
|
|
|
|
for (int j = 0; j < list.count(); ++j)
|
|
|
|
{
|
|
|
|
QDomElement property = list.item(j).toElement();
|
|
|
|
QString propertyName = property.attribute(QLatin1String("name"));
|
|
|
|
if (property.isNull())
|
|
|
|
continue;
|
|
|
|
if (!QDBusUtil::isValidMemberName(propertyName)) {
|
|
|
|
qDBusParserError("Invalid D-BUS member name '%s' found in interface '%s' while parsing introspection",
|
|
|
|
qPrintable(propertyName), qPrintable(ifaceName));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
QDBusIntrospection::Property propertyData;
|
|
|
|
|
|
|
|
// parse data
|
|
|
|
propertyData.name = propertyName;
|
|
|
|
propertyData.type = property.attribute(QLatin1String("type"));
|
|
|
|
propertyData.annotations = parseAnnotations(property);
|
|
|
|
|
|
|
|
if (!QDBusUtil::isValidSingleSignature(propertyData.type)) {
|
|
|
|
// cannot be!
|
|
|
|
qDBusParserError("Invalid D-BUS type signature '%s' found in property '%s.%s' while parsing introspection",
|
|
|
|
qPrintable(propertyData.type), qPrintable(ifaceName),
|
|
|
|
qPrintable(propertyName));
|
|
|
|
}
|
|
|
|
|
|
|
|
QString access = property.attribute(QLatin1String("access"));
|
|
|
|
if (access == QLatin1String("read"))
|
|
|
|
propertyData.access = QDBusIntrospection::Property::Read;
|
|
|
|
else if (access == QLatin1String("write"))
|
|
|
|
propertyData.access = QDBusIntrospection::Property::Write;
|
|
|
|
else if (access == QLatin1String("readwrite"))
|
|
|
|
propertyData.access = QDBusIntrospection::Property::ReadWrite;
|
|
|
|
else {
|
|
|
|
qDBusParserError("Invalid D-BUS property access '%s' found in property '%s.%s' while parsing introspection",
|
|
|
|
qPrintable(access), qPrintable(ifaceName),
|
|
|
|
qPrintable(propertyName));
|
|
|
|
continue; // invalid one!
|
|
|
|
}
|
|
|
|
|
|
|
|
// add it
|
|
|
|
ifaceData->properties.insert(propertyName, propertyData);
|
|
|
|
}
|
|
|
|
|
|
|
|
// add it
|
|
|
|
retval.insert(ifaceName, QSharedDataPointer<QDBusIntrospection::Interface>(ifaceData));
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
QSharedDataPointer<QDBusIntrospection::Object>
|
|
|
|
QDBusXmlParser::object() const
|
|
|
|
{
|
|
|
|
if (m_node.isNull())
|
|
|
|
return QSharedDataPointer<QDBusIntrospection::Object>();
|
|
|
|
|
|
|
|
QDBusIntrospection::Object* objData;
|
|
|
|
objData = new QDBusIntrospection::Object;
|
|
|
|
objData->service = m_service;
|
|
|
|
objData->path = m_path;
|
|
|
|
|
|
|
|
// check if we have anything to process
|
|
|
|
if (objData->introspection.isNull() && !m_node.firstChild().isNull()) {
|
|
|
|
// yes, introspect this object
|
|
|
|
QTextStream ts(&objData->introspection);
|
|
|
|
m_node.save(ts,2);
|
|
|
|
|
|
|
|
QDomNodeList objects = m_node.elementsByTagName(QLatin1String("node"));
|
|
|
|
for (int i = 0; i < objects.count(); ++i) {
|
|
|
|
QDomElement obj = objects.item(i).toElement();
|
|
|
|
QString objName = obj.attribute(QLatin1String("name"));
|
|
|
|
if (obj.isNull())
|
|
|
|
continue; // for whatever reason
|
|
|
|
if (!QDBusUtil::isValidObjectPath(m_path + QLatin1Char('/') + objName)) {
|
|
|
|
qDBusParserError("Invalid D-BUS object path '%s/%s' found while parsing introspection",
|
|
|
|
qPrintable(m_path), qPrintable(objName));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
objData->childObjects.append(objName);
|
|
|
|
}
|
|
|
|
|
|
|
|
QDomNodeList interfaceList = m_node.elementsByTagName(QLatin1String("interface"));
|
|
|
|
for (int i = 0; i < interfaceList.count(); ++i) {
|
|
|
|
QDomElement iface = interfaceList.item(i).toElement();
|
|
|
|
QString ifaceName = iface.attribute(QLatin1String("name"));
|
|
|
|
if (iface.isNull())
|
|
|
|
continue;
|
|
|
|
if (!QDBusUtil::isValidInterfaceName(ifaceName)) {
|
|
|
|
qDBusParserError("Invalid D-BUS interface name '%s' found while parsing introspection",
|
|
|
|
qPrintable(ifaceName));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
objData->interfaces.append(ifaceName);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
objData->introspection = QLatin1String("<node/>\n");
|
|
|
|
}
|
|
|
|
|
2016-10-16 21:12:21 +00:00
|
|
|
return QSharedDataPointer<QDBusIntrospection::Object>(objData);
|
2015-12-10 05:06:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
QSharedDataPointer<QDBusIntrospection::ObjectTree>
|
|
|
|
QDBusXmlParser::objectTree() const
|
|
|
|
{
|
|
|
|
QSharedDataPointer<QDBusIntrospection::ObjectTree> retval;
|
|
|
|
|
|
|
|
if (m_node.isNull())
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
retval = new QDBusIntrospection::ObjectTree;
|
|
|
|
|
|
|
|
retval->service = m_service;
|
|
|
|
retval->path = m_path;
|
|
|
|
|
|
|
|
QTextStream ts(&retval->introspection);
|
|
|
|
m_node.save(ts,2);
|
|
|
|
|
|
|
|
// interfaces are easy:
|
|
|
|
retval->interfaceData = interfaces();
|
|
|
|
retval->interfaces = retval->interfaceData.keys();
|
|
|
|
|
|
|
|
// sub-objects are slightly more difficult:
|
|
|
|
QDomNodeList objects = m_node.elementsByTagName(QLatin1String("node"));
|
|
|
|
for (int i = 0; i < objects.count(); ++i) {
|
|
|
|
QDomElement obj = objects.item(i).toElement();
|
|
|
|
QString objName = obj.attribute(QLatin1String("name"));
|
|
|
|
if (obj.isNull() || objName.isEmpty())
|
|
|
|
continue; // for whatever reason
|
|
|
|
|
|
|
|
// check if we have anything to process
|
|
|
|
if (!obj.firstChild().isNull()) {
|
|
|
|
// yes, introspect this object
|
|
|
|
QString xml;
|
|
|
|
QTextStream ts2(&xml);
|
|
|
|
obj.save(ts2,0);
|
|
|
|
|
|
|
|
// parse it
|
|
|
|
QString objAbsName = m_path;
|
|
|
|
if (!objAbsName.endsWith(QLatin1Char('/')))
|
|
|
|
objAbsName.append(QLatin1Char('/'));
|
|
|
|
objAbsName += objName;
|
|
|
|
|
|
|
|
QDBusXmlParser parser(m_service, objAbsName, obj);
|
|
|
|
retval->childObjectData.insert(objName, parser.objectTree());
|
|
|
|
}
|
|
|
|
|
|
|
|
retval->childObjects << objName;
|
|
|
|
}
|
|
|
|
|
|
|
|
return QSharedDataPointer<QDBusIntrospection::ObjectTree>( retval );
|
|
|
|
}
|
|
|
|
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
|