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