1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtDBus module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qdbusreply.h"
41#include "qdbusmetatype.h"
42#include "qdbusmetatype_p.h"
43#include <QDebug>
44
45#ifndef QT_NO_DBUS
46
47QT_BEGIN_NAMESPACE
48
49/*!
50 \class QDBusReply
51 \inmodule QtDBus
52 \since 4.2
53
54 \brief The QDBusReply class stores the reply for a method call to a remote object.
55
56 A QDBusReply object is a subset of the QDBusMessage object that represents a method call's
57 reply. It contains only the first output argument or the error code and is used by
58 QDBusInterface-derived classes to allow returning the error code as the function's return
59 argument.
60
61 It can be used in the following manner:
62 \snippet code/src_qdbus_qdbusreply.cpp 0
63
64 If the remote method call cannot fail, you can skip the error checking:
65 \snippet code/src_qdbus_qdbusreply.cpp 1
66
67 However, if it does fail under those conditions, the value returned by QDBusReply<T>::value() is
68 a default-constructed value. It may be indistinguishable from a valid return value.
69
70 QDBusReply objects are used for remote calls that have no output
71 arguments or return values (i.e., they have a "void" return
72 type). Use the isValid() function to test if the reply succeeded.
73
74 \sa QDBusMessage, QDBusInterface
75*/
76
77/*!
78 \fn template<typename T> QDBusReply<T>::QDBusReply(const QDBusReply &other)
79 \since 5.15
80
81 Constructs a copy of \a other.
82*/
83
84/*!
85 \fn template<typename T> QDBusReply<T>::QDBusReply(const QDBusMessage &reply)
86 Automatically construct a QDBusReply object from the reply message \a reply, extracting the
87 first return value from it if it is a success reply.
88*/
89
90/*!
91 \fn template<typename T> QDBusReply<T>::QDBusReply(const QDBusPendingReply<T> &reply)
92 Constructs a QDBusReply object from the pending reply message, \a reply.
93*/
94
95/*!
96 \fn template <typename T> QDBusReply<T>::QDBusReply(const QDBusPendingCall &pcall)
97 Automatically construct a QDBusReply object from the asynchronous
98 pending call \a pcall. If the call isn't finished yet, QDBusReply
99 will call QDBusPendingCall::waitForFinished(), which is a blocking
100 operation.
101
102 If the return types patch, QDBusReply will extract the first
103 return argument from the reply.
104*/
105
106/*!
107 \fn template <typename T> QDBusReply<T>::QDBusReply(const QDBusError &error)
108 Constructs an error reply from the D-Bus error code given by \a error.
109*/
110
111/*!
112 \fn template <typename T> QDBusReply<T>::operator=(const QDBusReply &other)
113 Makes this object be a copy of the object \a other.
114*/
115
116/*!
117 \fn template <typename T> QDBusReply<T>::operator=(const QDBusError &dbusError)
118 Sets this object to contain the error code given by \a dbusError. You
119 can later access it with error().
120*/
121
122/*!
123 \fn template <typename T> QDBusReply<T>::operator=(const QDBusMessage &reply)
124
125 Makes this object contain the \a reply message. If \a reply
126 is an error message, this function will
127 copy the error code and message into this object
128
129 If \a reply is a standard reply message and contains at least
130 one parameter, it will be copied into this object, as long as it
131 is of the correct type. If it's not of the same type as this
132 QDBusError object, this function will instead set an error code
133 indicating a type mismatch.
134*/
135
136/*!
137 \fn template <typename T> QDBusReply<T>::operator=(const QDBusPendingCall &pcall)
138
139 Makes this object contain the reply specified by the pending
140 asynchronous call \a pcall. If the call is not finished yet, this
141 function will call QDBusPendingCall::waitForFinished() to block
142 until the reply arrives.
143
144 If \a pcall finishes with an error message, this function will
145 copy the error code and message into this object
146
147 If \a pcall finished with a standard reply message and contains at
148 least one parameter, it will be copied into this object, as long
149 as it is of the correct type. If it's not of the same type as this
150 QDBusError object, this function will instead set an error code
151 indicating a type mismatch.
152*/
153
154/*!
155 \fn template <typename T> bool QDBusReply<T>::isValid() const
156
157 Returns \c true if no error occurred; otherwise, returns \c false.
158
159 \sa error()
160*/
161
162/*!
163 \fn template<typename T> const QDBusError& QDBusReply<T>::error() const
164
165 Returns the error code that was returned from the remote function call. If the remote call did
166 not return an error (i.e., if it succeeded), then the QDBusError object that is returned will
167 not be a valid error code (QDBusError::isValid() will return false).
168
169 \sa isValid()
170*/
171
172/*!
173 \fn template <typename T> const QDBusError& QDBusReply<T>::error()
174 \overload
175*/
176
177/*!
178 \fn template <typename T> QDBusReply<T>::value() const
179 Returns the remote function's calls return value. If the remote call returned with an error,
180 the return value of this function is undefined and may be undistinguishable from a valid return
181 value.
182
183 This function is not available if the remote call returns \c void.
184*/
185
186/*!
187 \fn template <typename T> QDBusReply<T>::operator Type() const
188 Returns the same as value().
189
190 This function is not available if the remote call returns \c void.
191*/
192
193/*!
194 \internal
195 Fills in the QDBusReply data \a error and \a data from the reply message \a reply.
196*/
197void qDBusReplyFill(const QDBusMessage &reply, QDBusError &error, QVariant &data)
198{
199 error = QDBusError(reply);
200
201 if (error.isValid()) {
202 data = QVariant(); // clear it
203 return;
204 }
205
206 if (reply.arguments().count() >= 1 && reply.arguments().at(0).metaType() == data.metaType()) {
207 data = reply.arguments().at(0);
208 return;
209 }
210
211 const char *expectedSignature = QDBusMetaType::typeToSignature(data.metaType());
212 const char *receivedType = nullptr;
213 QByteArray receivedSignature;
214
215 if (reply.arguments().count() >= 1) {
216 if (reply.arguments().at(0).metaType() == QDBusMetaTypeId::argument()) {
217 // compare signatures instead
218 QDBusArgument arg = qvariant_cast<QDBusArgument>(reply.arguments().at(0));
219 receivedSignature = arg.currentSignature().toLatin1();
220 if (receivedSignature == expectedSignature) {
221 // matched. Demarshall it
222 QDBusMetaType::demarshall(arg, data.metaType(), data.data());
223 return;
224 }
225 } else {
226 // not an argument and doesn't match?
227 QMetaType type = reply.arguments().at(0).metaType();
228 receivedType = type.name();
229 receivedSignature = QDBusMetaType::typeToSignature(type);
230 }
231 }
232
233 // error
234 if (receivedSignature.isEmpty())
235 receivedSignature = "<empty signature>";
236 QString errorMsg;
237 if (receivedType) {
238 errorMsg = QLatin1String("Unexpected reply signature: got \"%1\" (%4), "
239 "expected \"%2\" (%3)")
240 .arg(QLatin1String(receivedSignature),
241 QLatin1String(expectedSignature),
242 QLatin1String(data.typeName()),
243 QLatin1String(receivedType));
244 } else {
245 errorMsg = QLatin1String("Unexpected reply signature: got \"%1\", "
246 "expected \"%2\" (%3)")
247 .arg(QLatin1String(receivedSignature),
248 QLatin1String(expectedSignature),
249 QLatin1String(data.typeName()));
250 }
251
252 error = QDBusError(QDBusError::InvalidSignature, errorMsg);
253 data = QVariant(); // clear it
254}
255
256QT_END_NAMESPACE
257
258#endif // QT_NO_DBUS
259