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
5#include "qcoapinternalreply_p.h"
6#include <QtCore/qmath.h>
7
8QT_BEGIN_NAMESPACE
9
10/*!
11 \internal
12
13 \class QCoapInternalReply
14 \brief The QCoapInternalReply class contains data related to
15 a received message.
16
17 It is a subclass of QCoapInternalMessage.
18
19 \reentrant
20
21 \sa QCoapInternalMessage, QCoapInternalRequest
22*/
23
24/*!
25 \internal
26 Constructs a new QCoapInternalReply with \a parent as the parent object.
27*/
28QCoapInternalReply::QCoapInternalReply(QObject *parent) :
29 QCoapInternalMessage(*new QCoapInternalReplyPrivate, parent)
30{
31}
32
33/*!
34 \internal
35 Creates a QCoapInternalReply from the CoAP \a reply frame.
36
37 For more details, refer to section
38 \l{https://tools.ietf.org/html/rfc7252#section-3}{'Message format' of RFC 7252}.
39*/
40//! 0 1 2 3
41//! 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
42//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
43//! |Ver| T | TKL | Code | Message ID |
44//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
45//! | Token (if any, TKL bytes) ...
46//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
47//! | Options (if any) ...
48//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
49//! |1 1 1 1 1 1 1 1| Payload (if any) ...
50//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
51QCoapInternalReply *QCoapInternalReply::createFromFrame(const QByteArray &reply, QObject *parent)
52{
53 QCoapInternalReply *internalReply = new QCoapInternalReply(parent);
54 QCoapInternalReplyPrivate *d = internalReply->d_func();
55
56 const quint8 *pduData = reinterpret_cast<const quint8 *>(reply.data());
57
58 // Parse Header and Token
59 d->message.setVersion((pduData[0] >> 6) & 0x03);
60 d->message.setType(QCoapMessage::Type((pduData[0] >> 4) & 0x03));
61 quint8 tokenLength = (pduData[0]) & 0x0F;
62 d->responseCode = static_cast<QtCoap::ResponseCode>(pduData[1]);
63 d->message.setMessageId(static_cast<quint16>((static_cast<quint16>(pduData[2]) << 8)
64 | static_cast<quint16>(pduData[3])));
65 d->message.setToken(reply.mid(index: 4, len: tokenLength));
66
67 // Parse Options
68 int i = 4 + tokenLength;
69 quint16 lastOptionNumber = 0;
70 while (i != reply.size() && pduData[i] != 0xFF) {
71 quint16 optionDelta = ((pduData[i] >> 4) & 0x0F);
72 quint16 optionLength = (pduData[i] & 0x0F);
73
74 // Delta value > 12 : special values
75 if (optionDelta == 13) {
76 ++i;
77 optionDelta = pduData[i] + 13;
78 } else if (optionDelta == 14) {
79 ++i;
80 optionDelta = pduData[i] + 269;
81 }
82
83 // Delta length > 12 : special values
84 if (optionLength == 13) {
85 ++i;
86 optionLength = pduData[i] + 13;
87 } else if (optionLength == 14) {
88 ++i;
89 optionLength = pduData[i] + 269;
90 }
91
92 quint16 optionNumber = lastOptionNumber + optionDelta;
93 internalReply->addOption(name: QCoapOption::OptionName(optionNumber),
94 value: reply.mid(index: i + 1, len: optionLength));
95 lastOptionNumber = optionNumber;
96 i += 1 + optionLength;
97 }
98
99 // Parse Payload
100 if (i < reply.size() && pduData[i] == 0xFF) {
101 // -1 because of 0xFF at the beginning
102 QByteArray currentPayload = reply.mid(index: i + 1);
103 d->message.setPayload(d->message.payload().append(a: currentPayload));
104 }
105
106 return internalReply;
107}
108
109/*!
110 \internal
111 Appends the given \a data byte array to the current payload.
112*/
113void QCoapInternalReply::appendData(const QByteArray &data)
114{
115 Q_D(QCoapInternalReply);
116 d->message.setPayload(d->message.payload().append(a: data));
117}
118
119/*!
120 \internal
121 Adds the given CoAP \a option and sets block parameters if needed.
122*/
123void QCoapInternalReply::addOption(const QCoapOption &option)
124{
125 if (option.name() == QCoapOption::Block2)
126 setFromDescriptiveBlockOption(option);
127
128 QCoapInternalMessage::addOption(option);
129}
130
131/*!
132 \internal
133 Sets the sender address.
134*/
135void QCoapInternalReply::setSenderAddress(const QHostAddress &address)
136{
137 Q_D(QCoapInternalReply);
138 d->senderAddress = address;
139}
140
141/*!
142 \internal
143 Returns the number of the next block, if there is another block to come,
144 otherwise -1.
145 For more details, refer to the
146 \l{https://tools.ietf.org/html/rfc7959#section-2.2}{RFC 7959}.
147*/
148int QCoapInternalReply::nextBlockToSend() const
149{
150 Q_D(const QCoapInternalReply);
151
152 QCoapOption option = d->message.option(name: QCoapOption::Block1);
153 if (!option.isValid())
154 return -1;
155
156 const auto value = option.opaqueValue();
157 const quint8 *optionData = reinterpret_cast<const quint8 *>(value.data());
158 const quint8 lastByte = optionData[option.length() - 1];
159
160 // M field
161 bool hasNextBlock = ((lastByte & 0x8) == 0x8);
162 if (!hasNextBlock)
163 return -1;
164
165 // NUM field
166 quint32 blockNumber = 0;
167 for (int i = 0; i < option.length() - 1; ++i)
168 blockNumber = (blockNumber << 8) | optionData[i];
169 blockNumber = (blockNumber << 4) | (lastByte >> 4);
170 return static_cast<int>(blockNumber) + 1;
171}
172
173/*!
174 \internal
175 Returns \c true if the client has one or more blocks to send.
176*/
177bool QCoapInternalReply::hasMoreBlocksToSend() const
178{
179 return nextBlockToSend() >= 0;
180}
181
182/*!
183 \internal
184 Returns the response code of the reply.
185*/
186QtCoap::ResponseCode QCoapInternalReply::responseCode() const
187{
188 Q_D(const QCoapInternalReply);
189 return d->responseCode;
190}
191
192/*!
193 \internal
194 Returns the host address from which the reply was received.
195*/
196QHostAddress QCoapInternalReply::senderAddress() const
197{
198 Q_D(const QCoapInternalReply);
199 return d->senderAddress;
200}
201
202QT_END_NAMESPACE
203

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