1/*
2 * BluezQt - Asynchronous Bluez wrapper library
3 *
4 * SPDX-FileCopyrightText: 2014 David Rosca <nowrep@gmail.com>
5 *
6 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
7 */
8
9#include "pendingcall.h"
10#include "bluezqt_dbustypes.h"
11#include "debug.h"
12#include "obexfiletransferentry.h"
13#include "obextransfer.h"
14#include "obextransfer_p.h"
15
16#include <QDBusPendingCallWatcher>
17#include <QTimer>
18
19namespace BluezQt
20{
21static PendingCall::Error nameToError(const QString &name)
22{
23 if (name.startsWith(QLatin1String("org.freedesktop.DBus.Error"))) {
24 return PendingCall::DBusError;
25 }
26
27 if (!name.startsWith(QLatin1String("org.bluez.Error"))) {
28 return PendingCall::UnknownError;
29 }
30
31#define FROM_BLUEZ_ERROR(string, value) \
32 if (errorName == QLatin1String(string)) { \
33 return value; \
34 }
35
36 const QString &errorName = name.mid(16);
37 FROM_BLUEZ_ERROR("NotReady", PendingCall::NotReady);
38 FROM_BLUEZ_ERROR("Failed", PendingCall::Failed);
39 FROM_BLUEZ_ERROR("Rejected", PendingCall::Rejected);
40 FROM_BLUEZ_ERROR("Canceled", PendingCall::Canceled);
41 FROM_BLUEZ_ERROR("InvalidArguments", PendingCall::InvalidArguments);
42 FROM_BLUEZ_ERROR("AlreadyExists", PendingCall::AlreadyExists);
43 FROM_BLUEZ_ERROR("DoesNotExist", PendingCall::DoesNotExist);
44 FROM_BLUEZ_ERROR("AlreadyConnected", PendingCall::AlreadyConnected);
45 FROM_BLUEZ_ERROR("ConnectFailed", PendingCall::ConnectFailed);
46 FROM_BLUEZ_ERROR("NotConnected", PendingCall::NotConnected);
47 FROM_BLUEZ_ERROR("NotSupported", PendingCall::NotSupported);
48 FROM_BLUEZ_ERROR("NotAuthorized", PendingCall::NotAuthorized);
49 FROM_BLUEZ_ERROR("AuthenticationCanceled", PendingCall::AuthenticationCanceled);
50 FROM_BLUEZ_ERROR("AuthenticationFailed", PendingCall::AuthenticationFailed);
51 FROM_BLUEZ_ERROR("AuthenticationRejected", PendingCall::AuthenticationRejected);
52 FROM_BLUEZ_ERROR("AuthenticationTimeout", PendingCall::AuthenticationTimeout);
53 FROM_BLUEZ_ERROR("ConnectionAttemptFailed", PendingCall::ConnectionAttemptFailed);
54 FROM_BLUEZ_ERROR("InvalidLength", PendingCall::InvalidLength);
55 FROM_BLUEZ_ERROR("NotPermitted", PendingCall::NotPermitted);
56#undef FROM_BLUEZ_ERROR
57
58 return PendingCall::UnknownError;
59}
60
61class PendingCallPrivate : public QObject
62{
63public:
64 explicit PendingCallPrivate(PendingCall *parent);
65
66 void processReply(QDBusPendingCallWatcher *call);
67 void processVoidReply(const QDBusPendingReply<> &reply);
68 void processUint32Reply(const QDBusPendingReply<quint32> &reply);
69 void processStringReply(const QDBusPendingReply<QString> &reply);
70 void processStringListReply(const QDBusPendingReply<QStringList> &reply);
71 void processObjectPathReply(const QDBusPendingReply<QDBusObjectPath> &reply);
72 void processFileTransferListReply(const QDBusPendingReply<QVariantMapList> &reply);
73 void processTransferWithPropertiesReply(const QDBusPendingReply<QDBusObjectPath, QVariantMap> &reply);
74 void processByteArrayReply(const QDBusPendingReply<QByteArray> &reply);
75 void processError(const QDBusError &m_error);
76
77 void emitFinished();
78 void emitDelayedFinished();
79 void emitInternalError(const QString &errorText);
80 void pendingCallFinished(QDBusPendingCallWatcher *m_watcher);
81
82 PendingCall *q;
83 int m_error;
84 QString m_errorText;
85 QVariant m_userData;
86 QVariantList m_value;
87 PendingCall::ReturnType m_type;
88 QDBusPendingCallWatcher *m_watcher;
89};
90
91PendingCallPrivate::PendingCallPrivate(PendingCall *parent)
92 : QObject(parent)
93 , q(parent)
94 , m_error(PendingCall::NoError)
95 , m_type(PendingCall::ReturnVoid)
96 , m_watcher(nullptr)
97{
98}
99
100void PendingCallPrivate::processReply(QDBusPendingCallWatcher *call)
101{
102 switch (m_type) {
103 case PendingCall::ReturnVoid:
104 processVoidReply(*call);
105 break;
106
107 case PendingCall::ReturnUint32:
108 processUint32Reply(*call);
109 break;
110
111 case PendingCall::ReturnString:
112 processStringReply(*call);
113 break;
114
115 case PendingCall::ReturnStringList:
116 processStringListReply(*call);
117 break;
118
119 case PendingCall::ReturnObjectPath:
120 processObjectPathReply(*call);
121 break;
122
123 case PendingCall::ReturnFileTransferList:
124 processFileTransferListReply(*call);
125 break;
126
127 case PendingCall::ReturnTransferWithProperties:
128 processTransferWithPropertiesReply(*call);
129 break;
130
131 case PendingCall::ReturnByteArray:
132 processByteArrayReply(*call);
133 break;
134
135 default:
136 break;
137 }
138}
139
140void PendingCallPrivate::processVoidReply(const QDBusPendingReply<> &reply)
141{
142 processError(m_error: reply.error());
143}
144
145void PendingCallPrivate::processUint32Reply(const QDBusPendingReply<quint32> &reply)
146{
147 processError(m_error: reply.error());
148 if (!reply.isError()) {
149 m_value.append(reply.value());
150 }
151}
152
153void PendingCallPrivate::processStringReply(const QDBusPendingReply<QString> &reply)
154{
155 processError(m_error: reply.error());
156 if (!reply.isError()) {
157 m_value.append(reply.value());
158 }
159}
160
161void PendingCallPrivate::processStringListReply(const QDBusPendingReply<QStringList> &reply)
162{
163 processError(m_error: reply.error());
164 if (!reply.isError()) {
165 m_value.append(reply.value());
166 }
167}
168
169void PendingCallPrivate::processObjectPathReply(const QDBusPendingReply<QDBusObjectPath> &reply)
170{
171 processError(m_error: reply.error());
172 if (!reply.isError()) {
173 m_value.append(QVariant::fromValue(reply.value()));
174 }
175}
176
177void PendingCallPrivate::processFileTransferListReply(const QDBusPendingReply<QVariantMapList> &reply)
178{
179 processError(m_error: reply.error());
180 if (!reply.isError()) {
181 QList<ObexFileTransferEntry> items;
182 items.reserve(reply.value().size());
183 const auto maps = reply.value();
184 for (const QVariantMap &map : maps) {
185 items.append(ObexFileTransferEntry(map));
186 }
187 m_value.append(QVariant::fromValue(items));
188 }
189}
190
191void PendingCallPrivate::processTransferWithPropertiesReply(const QDBusPendingReply<QDBusObjectPath, QVariantMap> &reply)
192{
193 processError(m_error: reply.error());
194 if (reply.isError()) {
195 return;
196 }
197
198 ObexTransferPtr transfer = ObexTransferPtr(new ObexTransfer(reply.argumentAt<0>().path(), reply.argumentAt<1>()));
199 transfer->d->q = transfer.toWeakRef();
200 transfer->d->m_suspendable = true;
201 m_value.append(QVariant::fromValue(transfer));
202}
203
204void PendingCallPrivate::processByteArrayReply(const QDBusPendingReply<QByteArray> &reply)
205{
206 processError(m_error: reply.error());
207 if (!reply.isError()) {
208 m_value.append(QVariant::fromValue(reply.value()));
209 }
210}
211
212void PendingCallPrivate::processError(const QDBusError &error)
213{
214 if (error.isValid()) {
215 qCWarning(BLUEZQT) << "PendingCall Error:" << error.message();
216 m_error = nameToError(error.name());
217 m_errorText = error.message();
218 }
219}
220
221void PendingCallPrivate::emitFinished()
222{
223 m_watcher->deleteLater();
224 m_watcher = nullptr;
225 Q_EMIT q->finished(q);
226 q->deleteLater();
227}
228
229void PendingCallPrivate::emitDelayedFinished()
230{
231 Q_ASSERT(qobject_cast<QTimer *>(sender()));
232
233 Q_EMIT q->finished(q);
234 static_cast<QTimer *>(sender())->deleteLater();
235}
236
237void PendingCallPrivate::emitInternalError(const QString &errorText)
238{
239 qCWarning(BLUEZQT) << "PendingCall Internal error:" << errorText;
240 m_error = PendingCall::InternalError;
241 m_errorText = errorText;
242 emitFinished();
243}
244
245void PendingCallPrivate::pendingCallFinished(QDBusPendingCallWatcher *watcher)
246{
247 processReply(call: watcher);
248 emitFinished();
249}
250
251PendingCall::PendingCall(const QDBusPendingCall &call, ReturnType type, QObject *parent)
252 : QObject(parent)
253 , d(new PendingCallPrivate(this))
254{
255 qDBusRegisterMetaType<QVariantMapList>();
256
257 d->m_type = type;
258 d->m_watcher = new QDBusPendingCallWatcher(call, this);
259
260 connect(d->m_watcher, &QDBusPendingCallWatcher::finished, d.get(), &PendingCallPrivate::pendingCallFinished);
261}
262
263PendingCall::PendingCall(PendingCall::Error error, const QString &errorText, QObject *parent)
264 : QObject(parent)
265 , d(new PendingCallPrivate(this))
266{
267 d->m_error = error;
268 d->m_errorText = errorText;
269
270 QTimer *timer = new QTimer(this);
271 timer->setSingleShot(true);
272 timer->start(0);
273 connect(timer, &QTimer::timeout, d.get(), &PendingCallPrivate::emitDelayedFinished);
274}
275
276PendingCall::PendingCall(const QDBusPendingCall &call, ExternalProcessor externalProcessor, QObject *parent)
277 : QObject(parent)
278 , d(new PendingCallPrivate(this))
279{
280 qDBusRegisterMetaType<QVariantMapList>();
281
282 d->m_watcher = new QDBusPendingCallWatcher(call, this);
283 connect(d->m_watcher, &QDBusPendingCallWatcher::finished, [externalProcessor, this](QDBusPendingCallWatcher *watcher) {
284 externalProcessor(watcher, std::bind(&PendingCallPrivate::processError, d.get(), std::placeholders::_1), &d->m_value);
285 d->emitFinished();
286 });
287}
288
289PendingCall::~PendingCall() = default;
290
291QVariant PendingCall::value() const
292{
293 if (d->m_value.isEmpty()) {
294 return QVariant();
295 }
296 return d->m_value.first();
297}
298
299QVariantList PendingCall::values() const
300{
301 return d->m_value;
302}
303
304int PendingCall::error() const
305{
306 return d->m_error;
307}
308
309QString PendingCall::errorText() const
310{
311 return d->m_errorText;
312}
313
314bool PendingCall::isFinished() const
315{
316 if (d->m_watcher) {
317 return d->m_watcher->isFinished();
318 }
319 return true;
320}
321
322void PendingCall::waitForFinished()
323{
324 if (d->m_watcher) {
325 d->m_watcher->waitForFinished();
326 }
327}
328
329QVariant PendingCall::userData() const
330{
331 return d->m_userData;
332}
333
334void PendingCall::setUserData(const QVariant &userData)
335{
336 d->m_userData = userData;
337}
338
339} // namespace BluezQt
340
341#include "moc_pendingcall.cpp"
342

source code of bluez-qt/src/pendingcall.cpp