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 "qdbusreply.h"
5#include "qdbusmetatype.h"
6#include "qdbusmetatype_p.h"
7#include <QDebug>
8
9#ifndef QT_NO_DBUS
10
11QT_BEGIN_NAMESPACE
12
13using namespace Qt::StringLiterals;
14
15/*!
16 \class QDBusReply
17 \inmodule QtDBus
18 \since 4.2
19
20 \brief The QDBusReply class stores the reply for a method call to a remote object.
21
22 A QDBusReply object is a subset of the QDBusMessage object that represents a method call's
23 reply. It contains only the first output argument or the error code and is used by
24 QDBusInterface-derived classes to allow returning the error code as the function's return
25 argument.
26
27 It can be used in the following manner:
28 \snippet code/src_qdbus_qdbusreply.cpp 0
29
30 If the remote method call cannot fail, you can skip the error checking:
31 \snippet code/src_qdbus_qdbusreply.cpp 1
32
33 However, if it does fail under those conditions, the value returned by QDBusReply<T>::value() is
34 a default-constructed value. It may be indistinguishable from a valid return value.
35
36 QDBusReply objects are used for remote calls that have no output
37 arguments or return values (i.e., they have a "void" return
38 type). Use the isValid() function to test if the reply succeeded.
39
40 \sa QDBusMessage, QDBusInterface
41*/
42
43/*!
44 \fn template<typename T> QDBusReply<T>::QDBusReply(const QDBusReply &other)
45 \since 5.15
46
47 Constructs a copy of \a other.
48*/
49
50/*!
51 \fn template<typename T> QDBusReply<T>::QDBusReply(const QDBusMessage &reply)
52 Automatically construct a QDBusReply object from the reply message \a reply, extracting the
53 first return value from it if it is a success reply.
54*/
55
56/*!
57 \fn template<typename T> QDBusReply<T>::QDBusReply(const QDBusPendingReply<T> &reply)
58 Constructs a QDBusReply object from the pending reply message, \a reply.
59*/
60
61/*!
62 \fn template <typename T> QDBusReply<T>::QDBusReply(const QDBusPendingCall &pcall)
63 Automatically construct a QDBusReply object from the asynchronous
64 pending call \a pcall. If the call isn't finished yet, QDBusReply
65 will call QDBusPendingCall::waitForFinished(), which is a blocking
66 operation.
67
68 If the return types patch, QDBusReply will extract the first
69 return argument from the reply.
70*/
71
72/*!
73 \fn template <typename T> QDBusReply<T>::QDBusReply(const QDBusError &error)
74 Constructs an error reply from the D-Bus error code given by \a error.
75*/
76
77/*!
78 \fn template <typename T> QDBusReply<T>::operator=(const QDBusReply &other)
79 Makes this object be a copy of the object \a other.
80*/
81
82/*!
83 \fn template <typename T> QDBusReply<T>::operator=(const QDBusError &dbusError)
84 Sets this object to contain the error code given by \a dbusError. You
85 can later access it with error().
86*/
87
88/*!
89 \fn template <typename T> QDBusReply<T>::operator=(const QDBusMessage &reply)
90
91 Makes this object contain the \a reply message. If \a reply
92 is an error message, this function will
93 copy the error code and message into this object
94
95 If \a reply is a standard reply message and contains at least
96 one parameter, it will be copied into this object, as long as it
97 is of the correct type. If it's not of the same type as this
98 QDBusError object, this function will instead set an error code
99 indicating a type mismatch.
100*/
101
102/*!
103 \fn template <typename T> QDBusReply<T>::operator=(const QDBusPendingCall &pcall)
104
105 Makes this object contain the reply specified by the pending
106 asynchronous call \a pcall. If the call is not finished yet, this
107 function will call QDBusPendingCall::waitForFinished() to block
108 until the reply arrives.
109
110 If \a pcall finishes with an error message, this function will
111 copy the error code and message into this object
112
113 If \a pcall finished with a standard reply message and contains at
114 least one parameter, it will be copied into this object, as long
115 as it is of the correct type. If it's not of the same type as this
116 QDBusError object, this function will instead set an error code
117 indicating a type mismatch.
118*/
119
120/*!
121 \fn template <typename T> bool QDBusReply<T>::isValid() const
122
123 Returns \c true if no error occurred; otherwise, returns \c false.
124
125 \sa error()
126*/
127
128/*!
129 \fn template<typename T> const QDBusError& QDBusReply<T>::error() const
130
131 Returns the error code that was returned from the remote function call. If the remote call did
132 not return an error (i.e., if it succeeded), then the QDBusError object that is returned will
133 not be a valid error code (QDBusError::isValid() will return false).
134
135 \sa isValid()
136*/
137
138/*!
139 \fn template <typename T> const QDBusError& QDBusReply<T>::error()
140 \overload
141*/
142
143/*!
144 \fn template <typename T> QDBusReply<T>::value() const
145 Returns the remote function's calls return value. If the remote call returned with an error,
146 the return value of this function is undefined and may be undistinguishable from a valid return
147 value.
148
149 This function is not available if the remote call returns \c void.
150*/
151
152/*!
153 \fn template <typename T> QDBusReply<T>::operator Type() const
154 Returns the same as value().
155
156 This function is not available if the remote call returns \c void.
157*/
158
159/*!
160 \internal
161 Fills in the QDBusReply data \a error and \a data from the reply message \a reply.
162*/
163void qDBusReplyFill(const QDBusMessage &reply, QDBusError &error, QVariant &data)
164{
165 error = QDBusError(reply);
166
167 if (error.isValid()) {
168 data = QVariant(); // clear it
169 return;
170 }
171
172 if (reply.arguments().size() >= 1 && reply.arguments().at(i: 0).metaType() == data.metaType()) {
173 data = reply.arguments().at(i: 0);
174 return;
175 }
176
177 const char *expectedSignature = QDBusMetaType::typeToSignature(type: data.metaType());
178 const char *receivedType = nullptr;
179 QByteArray receivedSignature;
180
181 if (reply.arguments().size() >= 1) {
182 if (reply.arguments().at(i: 0).metaType() == QDBusMetaTypeId::argument()) {
183 // compare signatures instead
184 QDBusArgument arg = qvariant_cast<QDBusArgument>(v: reply.arguments().at(i: 0));
185 receivedSignature = arg.currentSignature().toLatin1();
186 if (receivedSignature == expectedSignature) {
187 // matched. Demarshall it
188 QDBusMetaType::demarshall(arg, id: data.metaType(), data: data.data());
189 return;
190 }
191 } else {
192 // not an argument and doesn't match?
193 QMetaType type = reply.arguments().at(i: 0).metaType();
194 receivedType = type.name();
195 receivedSignature = QDBusMetaType::typeToSignature(type);
196 }
197 }
198
199 // error
200 if (receivedSignature.isEmpty())
201 receivedSignature = "<empty signature>";
202 QString errorMsg;
203 if (receivedType) {
204 errorMsg = "Unexpected reply signature: got \"%1\" (%4), expected \"%2\" (%3)"_L1
205 .arg(args: QLatin1StringView(receivedSignature),
206 args: QLatin1StringView(expectedSignature),
207 args: QLatin1StringView(data.typeName()),
208 args: QLatin1StringView(receivedType));
209 } else {
210 errorMsg = "Unexpected reply signature: got \"%1\", expected \"%2\" (%3)"_L1
211 .arg(args: QLatin1StringView(receivedSignature),
212 args: QLatin1StringView(expectedSignature),
213 args: QLatin1StringView(data.typeName()));
214 }
215
216 error = QDBusError(QDBusError::InvalidSignature, errorMsg);
217 data = QVariant(); // clear it
218}
219
220QT_END_NAMESPACE
221
222#endif // QT_NO_DBUS
223

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