Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qndefmessage.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qndefmessage.h"
5#include "qndefrecord_p.h"
6
8
10
11
61{
63
64 bool seenMessageBegin = false;
65 bool seenMessageEnd = false;
66
67 QByteArray partialChunk;
69
70 qsizetype idx = 0;
71 while (idx < message.size()) {
72 quint8 flags = message.at(idx);
73
74 const bool messageBegin = flags & 0x80;
75 const bool messageEnd = flags & 0x40;
76
77 const bool cf = flags & 0x20;
78 const bool sr = flags & 0x10;
79 const bool il = flags & 0x08;
80 const quint8 typeNameFormat = flags & 0x07;
81
82 if (messageBegin && seenMessageBegin) {
83 qWarning("Got message begin but already parsed some records");
84 return QNdefMessage();
85 } else if (!messageBegin && !seenMessageBegin) {
86 qWarning("Haven't got message begin yet");
87 return QNdefMessage();
88 } else if (messageBegin && !seenMessageBegin) {
89 seenMessageBegin = true;
90 }
91 if (messageEnd && seenMessageEnd) {
92 qWarning("Got message end but already parsed final record");
93 return QNdefMessage();
94 } else if (messageEnd && !seenMessageEnd) {
95 seenMessageEnd = true;
96 }
97 // TNF must be 0x06 even for the last chunk, when cf == 0.
98 if ((typeNameFormat != 0x06) && !partialChunk.isEmpty()) {
99 qWarning("Partial chunk not empty, but TNF not 0x06 as expected");
100 return QNdefMessage();
101 }
102
103 int headerLength = 1;
104 headerLength += (sr) ? 1 : 4;
105 headerLength += (il) ? 1 : 0;
106
107 if (idx + headerLength >= message.size()) {
108 qWarning("Unexpected end of message");
109 return QNdefMessage();
110 }
111
112 const quint8 typeLength = message.at(++idx);
113
114 if ((typeNameFormat == 0x06) && (typeLength != 0)) {
115 qWarning("Invalid chunked data, TYPE_LENGTH != 0");
116 return QNdefMessage();
117 }
118
119 quint32 payloadLength;
120 if (sr)
121 payloadLength = quint8(message.at(++idx));
122 else {
123 payloadLength = quint8(message.at(++idx)) << 24;
124 payloadLength |= quint8(message.at(++idx)) << 16;
125 payloadLength |= quint8(message.at(++idx)) << 8;
126 payloadLength |= quint8(message.at(++idx)) << 0;
127 }
128
129 quint8 idLength;
130 if (il)
131 idLength = message.at(++idx);
132 else
133 idLength = 0;
134
135 // On 32-bit systems this can overflow
136 const qsizetype convertedPayloadLength = static_cast<qsizetype>(payloadLength);
137 const qsizetype contentLength = convertedPayloadLength + typeLength + idLength;
138
139 // On a 32 bit platform the payload can theoretically exceed the max.
140 // size of a QByteArray. This will never happen in practice with correct
141 // data because there are no NFC tags that can store such data sizes,
142 // but still can be possible if the data is corrupted.
143 if ((contentLength < 0) || (convertedPayloadLength < 0)
144 || ((std::numeric_limits<qsizetype>::max() - idx) < contentLength)) {
145 qWarning("Payload can't fit into QByteArray");
146 return QNdefMessage();
147 }
148
149 if (idx + contentLength >= message.size()) {
150 qWarning("Unexpected end of message");
151 return QNdefMessage();
152 }
153
154 if ((typeNameFormat == 0x06) && il) {
155 qWarning("Invalid chunked data, IL != 0");
156 return QNdefMessage();
157 }
158
159 if (typeNameFormat != 0x06)
160 record.setTypeNameFormat(QNdefRecord::TypeNameFormat(typeNameFormat));
161
162 if (typeLength > 0) {
163 QByteArray type(&message.constData()[++idx], typeLength);
164 record.setType(type);
165 idx += typeLength - 1;
166 }
167
168 if (idLength > 0) {
169 QByteArray id(&message.constData()[++idx], idLength);
170 record.setId(id);
171 idx += idLength - 1;
172 }
173
174 if (payloadLength > 0) {
175 QByteArray payload(&message.constData()[++idx], payloadLength);
176
177 if (cf) {
178 // chunked payload, except last
179 partialChunk.append(payload);
180 } else if (typeNameFormat == 0x06) {
181 // last chunk of chunked payload
182 record.setPayload(partialChunk + payload);
183 partialChunk.clear();
184 } else {
185 // non-chunked payload
186 record.setPayload(payload);
187 }
188
189 idx += payloadLength - 1;
190 }
191
192 if (!cf) {
193 result.append(record);
195 }
196
197 if (!cf && seenMessageEnd)
198 break;
199
200 // move to start of next record
201 ++idx;
202 }
203
204 if (!seenMessageBegin || !seenMessageEnd) {
205 qWarning("Malformed NDEF Message, missing begin or end");
206 return QNdefMessage();
207 }
208
209 return result;
210}
211
242{
243 // both records are empty
244 if (isEmpty() && other.isEmpty())
245 return true;
246
247 // compare empty to really empty
248 if (isEmpty() && other.size() == 1 && other.first().typeNameFormat() == QNdefRecord::Empty)
249 return true;
250 if (other.isEmpty() && size() == 1 && first().typeNameFormat() == QNdefRecord::Empty)
251 return true;
252
253 if (size() != other.size())
254 return false;
255
256 for (qsizetype i = 0; i < size(); ++i) {
257 if (at(i) != other.at(i))
258 return false;
259 }
260
261 return true;
262}
263
271{
272 // An empty message is treated as a message containing a single empty record.
273 if (isEmpty())
274 return QNdefMessage(QNdefRecord()).toByteArray();
275
277
278 for (qsizetype i = 0; i < size(); ++i) {
279 const QNdefRecord &record = at(i);
280
281 quint8 flags = record.typeNameFormat();
282
283 if (i == 0)
284 flags |= 0x80;
285 if (i == size() - 1)
286 flags |= 0x40;
287
288 // cf (chunked records) not supported yet
289
290 if (record.payload().size() < 255)
291 flags |= 0x10;
292
293 if (!record.id().isEmpty())
294 flags |= 0x08;
295
296 m.append(flags);
297 m.append(record.type().size());
298
299 if (flags & 0x10) {
300 m.append(quint8(record.payload().size()));
301 } else {
302 quint32 length = record.payload().size();
303 m.append(length >> 24);
304 m.append(length >> 16);
305 m.append(length >> 8);
306 m.append(length & 0x000000ff);
307 }
308
309 if (flags & 0x08)
310 m.append(record.id().size());
311
312 if (!record.type().isEmpty())
313 m.append(record.type());
314
315 if (!record.id().isEmpty())
316 m.append(record.id());
317
318 if (!record.payload().isEmpty())
319 m.append(record.payload());
320 }
321
322 return m;
323}
324
\inmodule QtCore
Definition qbytearray.h:57
qsizetype size() const noexcept
Definition qlist.h:397
bool isEmpty() const noexcept
Definition qlist.h:401
QNdefRecord & first()
Definition qlist.h:645
The QNdefMessage class provides an NFC NDEF message.
Q_NFC_EXPORT_COMPAT QNdefMessage()=default
Constructs a new empty NDEF message.
Q_NFC_EXPORT QByteArray toByteArray() const
Returns the NDEF message as a byte array.
Q_NFC_EXPORT bool operator==(const QNdefMessage &other) const
Returns true if this NDEF message is equivalent to other; otherwise returns false.
The QNdefRecord class provides an NFC NDEF record.
Definition qndefrecord.h:16
TypeNameFormat
This enum describes the type name format of an NDEF record.
Definition qndefrecord.h:18
Combined button and popup list for selecting options.
#define qWarning
Definition qlogging.h:166
#define QT_IMPL_METATYPE_EXTERN(TYPE)
Definition qmetatype.h:1390
const GLfloat * m
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLuint GLenum GLsizei length
GLenum GLuint id
[7]
GLenum type
GLbitfield flags
GLuint GLsizei const GLchar * message
GLuint64EXT * result
[6]
unsigned int quint32
Definition qtypes.h:50
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned char quint8
Definition qtypes.h:46
MyRecord record(int row) const
[0]
QSharedPointer< T > other(t)
[5]
QAction * at