1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <string.h>
5
6#ifndef QT_BOOTSTRAPPED
7#include <QtCore/qcoreapplication.h>
8#include <QtCore/qlist.h>
9#include <QtCore/qmetaobject.h>
10#include <QtCore/qvariant.h>
11
12#include "qdbusutil_p.h"
13#include "qdbusconnection_p.h"
14#include "qdbusabstractadaptor_p.h" // for QCLASSINFO_DBUS_*
15#endif
16#include "qdbusmetatype_p.h"
17
18#ifndef QT_NO_DBUS
19
20QT_BEGIN_NAMESPACE
21
22using namespace Qt::StringLiterals;
23
24bool qDBusCheckAsyncTag(const char *tag)
25{
26 static const char noReplyTag[] = "Q_NOREPLY";
27 if (!tag || !*tag)
28 return false;
29
30 const char *p = strstr(haystack: tag, needle: noReplyTag);
31 if (p != nullptr &&
32 (p == tag || *(p-1) == ' ') &&
33 (p[sizeof noReplyTag - 1] == '\0' || p[sizeof noReplyTag - 1] == ' '))
34 return true;
35
36 return false;
37}
38
39#ifndef QT_BOOTSTRAPPED
40
41QString qDBusInterfaceFromMetaObject(const QMetaObject *mo)
42{
43 QString interface;
44
45 int idx = mo->indexOfClassInfo(QCLASSINFO_DBUS_INTERFACE);
46 if (idx >= mo->classInfoOffset()) {
47 interface = QLatin1StringView(mo->classInfo(index: idx).value());
48 } else {
49 interface = QLatin1StringView(mo->className());
50 interface.replace(before: "::"_L1, after: "."_L1);
51
52 if (interface.startsWith(s: "QDBus"_L1)) {
53 interface.prepend(s: "org.qtproject.QtDBus."_L1);
54 } else if (interface.startsWith(c: u'Q') &&
55 interface.size() >= 2 && interface.at(i: 1).isUpper()) {
56 // assume it's Qt
57 interface.prepend(s: "org.qtproject.Qt."_L1);
58 } else if (!QCoreApplication::instance()||
59 QCoreApplication::instance()->applicationName().isEmpty()) {
60 interface.prepend(s: "local."_L1);
61 } else {
62 interface.prepend(c: u'.').prepend(s: QCoreApplication::instance()->applicationName());
63 const QString organizationDomain = QCoreApplication::instance()->organizationDomain();
64 const auto domainName = QStringView{organizationDomain}.split(sep: u'.', behavior: Qt::SkipEmptyParts);
65 if (domainName.isEmpty()) {
66 interface.prepend(s: "local."_L1);
67 } else {
68 QString composedDomain;
69 // + 1 for additional dot, e.g. organizationDomain equals "example.com",
70 // then composedDomain will be equal "com.example."
71 composedDomain.reserve(asize: organizationDomain.size() + 1);
72 for (auto it = domainName.rbegin(), end = domainName.rend(); it != end; ++it)
73 composedDomain += *it + u'.';
74
75 interface.prepend(s: composedDomain);
76 }
77 }
78 }
79
80 return interface;
81}
82
83bool qDBusInterfaceInObject(QObject *obj, const QString &interface_name)
84{
85 const QMetaObject *mo = obj->metaObject();
86 for ( ; mo != &QObject::staticMetaObject; mo = mo->superClass())
87 if (interface_name == qDBusInterfaceFromMetaObject(mo))
88 return true;
89 return false;
90}
91
92// calculates the metatypes for the method
93// the slot must have the parameters in the following form:
94// - zero or more value or const-ref parameters of any kind
95// - zero or one const ref of QDBusMessage
96// - zero or more non-const ref parameters
97// No parameter may be a template.
98// this function returns -1 if the parameters don't match the above form
99// this function returns the number of *input* parameters, including the QDBusMessage one if any
100// this function does not check the return type, so metaTypes[0] is always 0 and always present
101// metaTypes.count() >= retval + 1 in all cases
102//
103// sig must be the normalised signature for the method
104int qDBusParametersForMethod(const QMetaMethod &mm, QList<QMetaType> &metaTypes, QString &errorMsg)
105{
106 return qDBusParametersForMethod(parameters: mm.parameterTypes(), metaTypes, errorMsg);
107}
108
109#endif // QT_BOOTSTRAPPED
110
111int qDBusParametersForMethod(const QList<QByteArray> &parameterTypes, QList<QMetaType> &metaTypes,
112 QString &errorMsg)
113{
114 QDBusMetaTypeId::init();
115 metaTypes.clear();
116
117 metaTypes.append(t: QMetaType()); // return type
118 int inputCount = 0;
119 bool seenMessage = false;
120 for (QByteArray type : parameterTypes) {
121 if (type.endsWith(c: '*')) {
122 errorMsg = "Pointers are not supported: "_L1 + QLatin1StringView(type);
123 return -1;
124 }
125
126 if (type.endsWith(c: '&')) {
127 QByteArray basictype = type;
128 basictype.truncate(pos: type.size() - 1);
129
130 QMetaType id = QMetaType::fromName(name: basictype);
131 if (!id.isValid()) {
132 errorMsg = "Unregistered output type in parameter list: "_L1 + QLatin1StringView(type);
133 return -1;
134 } else if (QDBusMetaType::typeToSignature(type: id) == nullptr)
135 return -1;
136
137 metaTypes.append(t: id);
138 seenMessage = true; // it cannot appear anymore anyways
139 continue;
140 }
141
142 if (seenMessage) { // && !type.endsWith('&')
143 errorMsg = "Invalid method, non-output parameters after message or after output parameters: "_L1 + QLatin1StringView(type);
144 return -1; // not allowed
145 }
146
147 if (type.startsWith(bv: "QVector<"))
148 type = "QList<" + type.mid(index: sizeof("QVector<") - 1);
149
150 QMetaType id = QMetaType::fromName(name: type);
151#ifdef QT_BOOTSTRAPPED
152 // in bootstrap mode QDBusMessage isn't included, thus we need to resolve it manually here
153 if (type == "QDBusMessage") {
154 id = QDBusMetaTypeId::message();
155 }
156#endif
157
158 if (!id.isValid()) {
159 errorMsg = "Unregistered input type in parameter list: "_L1 + QLatin1StringView(type);
160 return -1;
161 }
162
163 if (id == QDBusMetaTypeId::message())
164 seenMessage = true;
165 else if (QDBusMetaType::typeToSignature(type: id) == nullptr) {
166 errorMsg = "Type not registered with QtDBus in parameter list: "_L1 + QLatin1StringView(type);
167 return -1;
168 }
169
170 metaTypes.append(t: id);
171 ++inputCount;
172 }
173
174 return inputCount;
175}
176
177QT_END_NAMESPACE
178
179#endif // QT_NO_DBUS
180

source code of qtbase/src/dbus/qdbusmisc.cpp