1// Copyright (C) 2017 Witekio.
2// Copyright (C) 2018 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
6#include "qcoapreply_p.h"
7#include "qcoapinternalreply_p.h"
8#include "qcoapnamespace_p.h"
9
10#include <QtCore/qmath.h>
11#include <QtCore/qloggingcategory.h>
12
13QT_BEGIN_NAMESPACE
14
15/*!
16 \internal
17 Constructor.
18*/
19QCoapReplyPrivate::QCoapReplyPrivate(const QCoapRequest &req) :
20 request(req)
21{
22}
23
24/*!
25 \internal
26 Marks the reply as running, and sets the \a token and \a messageId of this
27 exchange.
28
29 \sa isRunning()
30*/
31void QCoapReplyPrivate::_q_setRunning(const QCoapToken &token, QCoapMessageId messageId)
32{
33 request.setToken(token);
34 request.setMessageId(messageId);
35 isRunning = true;
36}
37
38/*!
39 \internal
40
41 Sets the reply as finished.
42*/
43void QCoapReplyPrivate::_q_setObserveCancelled()
44{
45 Q_Q(QCoapReply);
46
47 bool alreadyFinished = q->isFinished();
48
49 isFinished = true;
50 isRunning = false;
51
52 if (!alreadyFinished)
53 emit q->finished(reply: q);
54}
55
56/*!
57 \internal
58
59 Sets the message and response code of this reply, unless reply is
60 already finished.
61*/
62void QCoapReplyPrivate::_q_setContent(const QHostAddress &, const QCoapMessage &msg,
63 QtCoap::ResponseCode code)
64{
65 Q_Q(QCoapReply);
66
67 if (q->isFinished())
68 return;
69
70 message = msg;
71 responseCode = code;
72 seekBuffer(newPos: 0);
73
74 if (QtCoap::isError(code: responseCode))
75 _q_setError(code: responseCode);
76}
77
78/*!
79 \internal
80
81 For an Observe request, notifies that a new message was received
82 by emitting the notified() signal. If the reply is finished, no
83 signal will be emitted.
84*/
85void QCoapReplyPrivate::_q_setNotified()
86{
87 Q_Q(QCoapReply);
88
89 if (!q->isFinished())
90 emit q->notified(reply: q, message);
91}
92
93/*!
94 \internal
95
96 Sets the reply as finished, sending the finished() signal if it wasn't
97 already.
98*/
99void QCoapReplyPrivate::_q_setFinished(QtCoap::Error newError)
100{
101 Q_Q(QCoapReply);
102
103 if (q->isFinished())
104 return;
105
106 isFinished = true;
107 isRunning = false;
108
109 if (newError != QtCoap::Error::Ok)
110 _q_setError(newError);
111
112 emit q->finished(reply: q);
113}
114
115/*!
116 \internal
117
118 Sets the error of the reply.
119
120 \sa errorReceived()
121*/
122void QCoapReplyPrivate::_q_setError(QtCoap::Error newError)
123{
124 Q_Q(QCoapReply);
125 if (error == newError)
126 return;
127
128 error = newError;
129 emit q->error(reply: q, error);
130}
131
132/*!
133 \internal
134
135 Sets the error of the reply.
136*/
137void QCoapReplyPrivate::_q_setError(QtCoap::ResponseCode code)
138{
139 _q_setError(newError: QtCoap::errorForResponseCode(code));
140}
141
142/*!
143 \class QCoapReply
144 \inmodule QtCoap
145
146 \brief The QCoapReply class holds the data of a CoAP reply.
147
148 \reentrant
149
150 The QCoapReply contains data related to a request sent with the
151 QCoapClient.
152
153 The finished() signal is emitted when the response is fully
154 received or when the request fails.
155
156 For \e Observe requests specifically, the notified() signal is emitted
157 whenever a notification is received.
158
159 \sa QCoapClient, QCoapRequest, QCoapResourceDiscoveryReply
160*/
161
162/*!
163 \fn void QCoapReply::finished(QCoapReply* reply)
164
165 This signal is emitted whenever the corresponding request finished,
166 whether successfully or not. When a resource is observed, this signal
167 will only be emitted once, when the observation ends.
168
169 The \a reply parameter is the QCoapReply itself for convenience.
170
171 \note If the QCoapReply is deleted while not finished, both aborted() and
172 finished() signal will be emitted immediately before the QCoapReply is
173 destroyed. Given the QCoapReply may have been deleted when receiving the
174 signal, you should not rely on the \a reply to be still valid.
175
176 \sa QCoapClient::finished(), isFinished(), notified(), aborted()
177*/
178
179/*!
180 \fn void QCoapReply::notified(QCoapReply* reply, const QCoapMessage &message)
181
182 This signal is emitted whenever a notification is received from an observed
183 resource.
184
185 Its \a message parameter is a QCoapMessage containing the payload and the
186 message details. The \a reply parameter is the QCoapReply itself for
187 convenience.
188
189 \sa QCoapClient::finished(), isFinished(), finished(), notified()
190*/
191
192/*!
193 \fn void QCoapReply::error(QCoapReply* reply, QtCoap::Error error)
194
195 This signal is emitted whenever an error occurs and is followed by the
196 finished() signal.
197
198 Its \a reply parameters is the QCoapReply itself for convenience, and
199 the \a error parameter is the error received.
200
201 \sa finished(), aborted()
202*/
203
204/*!
205 \fn void QCoapReply::aborted(const QCoapToken &token);
206
207 This signal is emitted when the request is aborted or the reply is deleted.
208 Its \a token parameter is the token of the exchange that has been aborted.
209
210 \note If the QCoapReply is deleted while not finished, both aborted() and
211 finished() signal will be emitted immediately before the QCoapReply is
212 destroyed. Given the QCoapReply may have been deleted when receiving the
213 signal, you should not rely on the sender() object to be still valid.
214
215 \sa finished(), error()
216*/
217
218/*!
219 \internal
220 Constructs a new CoAP reply with \a dd as the d_ptr.
221 This constructor must be used when subclassing internally
222 the QCoapReply class.
223*/
224QCoapReply::QCoapReply(QCoapReplyPrivate &dd, QObject *parent) :
225 QIODevice(dd, parent)
226{
227 open(mode: QIODevice::ReadOnly);
228}
229
230/*!
231 Destroys the QCoapReply and aborts the request if its response has
232 not yet been received.
233*/
234QCoapReply::~QCoapReply()
235{
236 abortRequest();
237}
238
239/*!
240 \internal
241
242 \overload
243*/
244qint64 QCoapReply::readData(char *data, qint64 maxSize)
245{
246 Q_D(QCoapReply);
247
248 QByteArray payload = d->message.payload();
249
250 maxSize = qMin(a: maxSize, b: qint64(payload.size()) - pos());
251 if (maxSize <= 0)
252 return qint64(0);
253
254 // Explicitly account for platform size_t limitations
255 size_t len = static_cast<size_t>(maxSize);
256 if (sizeof(qint64) > sizeof(size_t)
257 && maxSize > static_cast<qint64>(std::numeric_limits<size_t>::max())) {
258 qCWarning(lcCoapExchange) << "Cannot read more than"
259 << std::numeric_limits<size_t>::max()
260 << "at a time";
261 len = std::numeric_limits<size_t>::max();
262 }
263
264 memcpy(dest: data, src: payload.constData() + pos(), n: len);
265
266 return static_cast<qint64>(len);
267}
268
269/*!
270 \internal
271
272 \overload
273*/
274qint64 QCoapReply::writeData(const char *data, qint64 maxSize)
275{
276 // The user cannot write to the reply
277 Q_UNUSED(data);
278 Q_UNUSED(maxSize);
279 return -1;
280}
281
282/*!
283 Returns the response code of the request.
284*/
285QtCoap::ResponseCode QCoapReply::responseCode() const
286{
287 Q_D(const QCoapReply);
288 return d->responseCode;
289}
290
291/*!
292 Returns the contained message.
293*/
294QCoapMessage QCoapReply::message() const
295{
296 Q_D(const QCoapReply);
297 return d->message;
298}
299
300/*!
301 Returns the associated request.
302*/
303QCoapRequest QCoapReply::request() const
304{
305 Q_D(const QCoapReply);
306 return d->request;
307}
308
309/*!
310 Returns \c true if the request is finished.
311
312 \sa finished()
313*/
314bool QCoapReply::isFinished() const
315{
316 Q_D(const QCoapReply);
317 return d->isFinished || d->isAborted;
318}
319
320/*!
321 Returns \c true if the request is running.
322*/
323bool QCoapReply::isRunning() const
324{
325 Q_D(const QCoapReply);
326 return d->isRunning && !isFinished();
327}
328
329/*!
330 Returns \c true if the request has been aborted.
331*/
332bool QCoapReply::isAborted() const
333{
334 Q_D(const QCoapReply);
335 return d->isAborted;
336}
337
338/*!
339 Returns \c true if the request finished with no error.
340*/
341bool QCoapReply::isSuccessful() const
342{
343 Q_D(const QCoapReply);
344 return d->isFinished && !QtCoap::isError(code: d->responseCode)
345 && d->error == QtCoap::Error::Ok;
346}
347
348/*!
349 Returns the target uri of the associated request.
350*/
351QUrl QCoapReply::url() const
352{
353 Q_D(const QCoapReply);
354 return d->request.url();
355}
356
357/*!
358 Returns the method of the associated request.
359*/
360QtCoap::Method QCoapReply::method() const
361{
362 Q_D(const QCoapReply);
363 return d->request.method();
364}
365
366/*!
367 Returns the error of the reply or QCoapReply::NoError if there is no error.
368*/
369QtCoap::Error QCoapReply::errorReceived() const
370{
371 Q_D(const QCoapReply);
372 return d->error;
373}
374
375/*!
376 Aborts the request immediately and emits the
377 \l{QCoapReply::aborted(const QCoapToken &token)}{aborted(const QCoapToken &token)}
378 signal if the request was not finished.
379*/
380void QCoapReply::abortRequest()
381{
382 Q_D(QCoapReply);
383
384 if (isFinished())
385 return;
386
387 d->isAborted = true;
388 d->isFinished = true;
389 d->isRunning = false;
390 emit aborted(token: request().token());
391 emit finished(reply: this);
392}
393
394/*!
395 \internal
396
397 Creates a new instance of QCoapReply and returns a pointer to it.
398*/
399QCoapReply *QCoapReplyPrivate::createCoapReply(const QCoapRequest &request, QObject *parent)
400{
401 return new QCoapReply(*new QCoapReplyPrivate(request), parent);
402}
403
404QT_END_NAMESPACE
405
406#include "moc_qcoapreply.cpp"
407

source code of qtcoap/src/coap/qcoapreply.cpp