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

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