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 
47 QT_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 */
qDBusReplyFill(const QDBusMessage & reply,QDBusError & error,QVariant & data)197 void 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).userType() == data.userType()) {
207         data = reply.arguments().at(0);
208         return;
209     }
210 
211     const char *expectedSignature = QDBusMetaType::typeToSignature(data.userType());
212     const char *receivedType = nullptr;
213     QByteArray receivedSignature;
214 
215     if (reply.arguments().count() >= 1) {
216         if (reply.arguments().at(0).userType() == 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.userType(), data.data());
223                 return;
224             }
225         } else {
226             // not an argument and doesn't match?
227             int type = reply.arguments().at(0).userType();
228             receivedType = QMetaType::typeName(type);
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 
256 QT_END_NAMESPACE
257 
258 #endif // QT_NO_DBUS
259