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 | |
19 | namespace BluezQt |
20 | { |
21 | static PendingCall::Error nameToError(const QString &name) |
22 | { |
23 | if (name.startsWith(s: QLatin1String("org.freedesktop.DBus.Error" ))) { |
24 | return PendingCall::DBusError; |
25 | } |
26 | |
27 | if (!name.startsWith(s: 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(position: 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 | |
61 | class PendingCallPrivate : public QObject |
62 | { |
63 | public: |
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 | |
91 | PendingCallPrivate::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 | |
100 | void PendingCallPrivate::processReply(QDBusPendingCallWatcher *call) |
101 | { |
102 | switch (m_type) { |
103 | case PendingCall::ReturnVoid: |
104 | processVoidReply(reply: *call); |
105 | break; |
106 | |
107 | case PendingCall::ReturnUint32: |
108 | processUint32Reply(reply: *call); |
109 | break; |
110 | |
111 | case PendingCall::ReturnString: |
112 | processStringReply(reply: *call); |
113 | break; |
114 | |
115 | case PendingCall::ReturnStringList: |
116 | processStringListReply(reply: *call); |
117 | break; |
118 | |
119 | case PendingCall::ReturnObjectPath: |
120 | processObjectPathReply(reply: *call); |
121 | break; |
122 | |
123 | case PendingCall::ReturnFileTransferList: |
124 | processFileTransferListReply(reply: *call); |
125 | break; |
126 | |
127 | case PendingCall::ReturnTransferWithProperties: |
128 | processTransferWithPropertiesReply(reply: *call); |
129 | break; |
130 | |
131 | case PendingCall::ReturnByteArray: |
132 | processByteArrayReply(reply: *call); |
133 | break; |
134 | |
135 | default: |
136 | break; |
137 | } |
138 | } |
139 | |
140 | void PendingCallPrivate::processVoidReply(const QDBusPendingReply<> &reply) |
141 | { |
142 | processError(m_error: reply.error()); |
143 | } |
144 | |
145 | void PendingCallPrivate::processUint32Reply(const QDBusPendingReply<quint32> &reply) |
146 | { |
147 | processError(m_error: reply.error()); |
148 | if (!reply.isError()) { |
149 | m_value.append(t: reply.value()); |
150 | } |
151 | } |
152 | |
153 | void PendingCallPrivate::processStringReply(const QDBusPendingReply<QString> &reply) |
154 | { |
155 | processError(m_error: reply.error()); |
156 | if (!reply.isError()) { |
157 | m_value.append(t: reply.value()); |
158 | } |
159 | } |
160 | |
161 | void PendingCallPrivate::processStringListReply(const QDBusPendingReply<QStringList> &reply) |
162 | { |
163 | processError(m_error: reply.error()); |
164 | if (!reply.isError()) { |
165 | m_value.append(t: reply.value()); |
166 | } |
167 | } |
168 | |
169 | void PendingCallPrivate::processObjectPathReply(const QDBusPendingReply<QDBusObjectPath> &reply) |
170 | { |
171 | processError(m_error: reply.error()); |
172 | if (!reply.isError()) { |
173 | m_value.append(t: QVariant::fromValue(value: reply.value())); |
174 | } |
175 | } |
176 | |
177 | void PendingCallPrivate::processFileTransferListReply(const QDBusPendingReply<QVariantMapList> &reply) |
178 | { |
179 | processError(m_error: reply.error()); |
180 | if (!reply.isError()) { |
181 | QList<ObexFileTransferEntry> items; |
182 | items.reserve(asize: reply.value().size()); |
183 | const auto maps = reply.value(); |
184 | for (const QVariantMap &map : maps) { |
185 | items.append(t: ObexFileTransferEntry(map)); |
186 | } |
187 | m_value.append(t: QVariant::fromValue(value: items)); |
188 | } |
189 | } |
190 | |
191 | void 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(t: QVariant::fromValue(value: transfer)); |
202 | } |
203 | |
204 | void PendingCallPrivate::processByteArrayReply(const QDBusPendingReply<QByteArray> &reply) |
205 | { |
206 | processError(m_error: reply.error()); |
207 | if (!reply.isError()) { |
208 | m_value.append(t: QVariant::fromValue(value: reply.value())); |
209 | } |
210 | } |
211 | |
212 | void PendingCallPrivate::processError(const QDBusError &error) |
213 | { |
214 | if (error.isValid()) { |
215 | qCWarning(BLUEZQT) << "PendingCall Error:" << error.message(); |
216 | m_error = nameToError(name: error.name()); |
217 | m_errorText = error.message(); |
218 | } |
219 | } |
220 | |
221 | void PendingCallPrivate::emitFinished() |
222 | { |
223 | m_watcher->deleteLater(); |
224 | m_watcher = nullptr; |
225 | Q_EMIT q->finished(call: q); |
226 | q->deleteLater(); |
227 | } |
228 | |
229 | void PendingCallPrivate::emitDelayedFinished() |
230 | { |
231 | Q_ASSERT(qobject_cast<QTimer *>(sender())); |
232 | |
233 | Q_EMIT q->finished(call: q); |
234 | static_cast<QTimer *>(sender())->deleteLater(); |
235 | } |
236 | |
237 | void 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 | |
245 | void PendingCallPrivate::pendingCallFinished(QDBusPendingCallWatcher *watcher) |
246 | { |
247 | processReply(call: watcher); |
248 | emitFinished(); |
249 | } |
250 | |
251 | PendingCall::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(sender: d->m_watcher, signal: &QDBusPendingCallWatcher::finished, context: d.get(), slot: &PendingCallPrivate::pendingCallFinished); |
261 | } |
262 | |
263 | PendingCall::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(msec: 0); |
273 | connect(sender: timer, signal: &QTimer::timeout, context: d.get(), slot: &PendingCallPrivate::emitDelayedFinished); |
274 | } |
275 | |
276 | PendingCall::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(sender: d->m_watcher, signal: &QDBusPendingCallWatcher::finished, slot: [externalProcessor, this](QDBusPendingCallWatcher *watcher) { |
284 | externalProcessor(watcher, std::bind(f: &PendingCallPrivate::processError, args: d.get(), args: std::placeholders::_1), &d->m_value); |
285 | d->emitFinished(); |
286 | }); |
287 | } |
288 | |
289 | PendingCall::~PendingCall() = default; |
290 | |
291 | QVariant PendingCall::value() const |
292 | { |
293 | if (d->m_value.isEmpty()) { |
294 | return QVariant(); |
295 | } |
296 | return d->m_value.first(); |
297 | } |
298 | |
299 | QVariantList PendingCall::values() const |
300 | { |
301 | return d->m_value; |
302 | } |
303 | |
304 | int PendingCall::error() const |
305 | { |
306 | return d->m_error; |
307 | } |
308 | |
309 | QString PendingCall::errorText() const |
310 | { |
311 | return d->m_errorText; |
312 | } |
313 | |
314 | bool PendingCall::isFinished() const |
315 | { |
316 | if (d->m_watcher) { |
317 | return d->m_watcher->isFinished(); |
318 | } |
319 | return true; |
320 | } |
321 | |
322 | void PendingCall::waitForFinished() |
323 | { |
324 | if (d->m_watcher) { |
325 | d->m_watcher->waitForFinished(); |
326 | } |
327 | } |
328 | |
329 | QVariant PendingCall::userData() const |
330 | { |
331 | return d->m_userData; |
332 | } |
333 | |
334 | void PendingCall::setUserData(const QVariant &userData) |
335 | { |
336 | d->m_userData = userData; |
337 | } |
338 | |
339 | } // namespace BluezQt |
340 | |
341 | #include "moc_pendingcall.cpp" |
342 | |