1/****************************************************************************
2**
3** Copyright (C) 2015 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the QtVersit module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL21$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 2.1 or version 3 as published by the Free
20** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22** following information to ensure the GNU Lesser General Public License
23** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25**
26** As a special exception, The Qt Company gives you certain additional
27** rights. These rights are described in The Qt Company LGPL Exception
28** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29**
30** $QT_END_LICENSE$
31**
32****************************************************************************/
33
34#include "qversitutils_p.h"
35
36#include <QtCore/QTextCodec>
37#include <QtCore/qstring.h>
38#include <QtCore/qtextcodec.h>
39#include <QtCore/qvariant.h>
40
41#include <QtCore/QJsonArray>
42#include <QtCore/QJsonDocument>
43#include <QtCore/QJsonValue>
44
45#include "qversitdocument.h"
46
47QT_BEGIN_NAMESPACE_VERSIT
48
49QTextCodec* VersitUtils::m_previousCodec = 0;
50QList<QByteArrayMatcher>* VersitUtils::m_newlineList = 0;
51QByteArray VersitUtils::m_encodingMap[256];
52QBasicMutex VersitUtils::m_staticLock;
53
54/*!
55 * Encode \a ch with \a codec, without adding an byte-order mark
56 */
57QByteArray VersitUtils::encode(char ch, QTextCodec* codec)
58{
59 changeCodec(codec);
60 return m_encodingMap[(int)ch];
61}
62
63/*!
64 * Encode \a ba with \a codec, without adding an byte-order mark. \a ba is interpreted as ASCII
65 */
66QByteArray VersitUtils::encode(const QByteArray& ba, QTextCodec* codec)
67{
68 QTextCodec::ConverterState state(QTextCodec::IgnoreHeader);
69 return codec->fromUnicode(in: QString::fromLatin1(str: ba.data()).data(), length: ba.length(), state: &state);
70}
71
72/*!
73 * Returns the list of DOS, UNIX and Mac newline characters for \a codec.
74 */
75QList<QByteArrayMatcher>* VersitUtils::newlineList(QTextCodec* codec)
76{
77 changeCodec(codec);
78 return m_newlineList;
79}
80
81/*!
82 * Update the cached tables of pregenerated encoded text with \a codec.
83 */
84void VersitUtils::changeCodec(QTextCodec* codec) {
85 QMutexLocker readWriterLocker(&VersitUtils::m_staticLock);
86
87 if (VersitUtils::m_newlineList != 0 && codec == VersitUtils::m_previousCodec)
88 return;
89
90 // Build m_encodingMap
91 QChar qch;
92 QTextCodec::ConverterState state(QTextCodec::IgnoreHeader);
93 for (int c = 0; c < 256; c++) {
94 qch = QLatin1Char(c);
95 m_encodingMap[c] = codec->fromUnicode(in: &qch, length: 1, state: &state);
96 }
97
98 // Build m_newlineList
99 if (m_newlineList != 0)
100 delete m_newlineList;
101 m_newlineList = new QList<QByteArrayMatcher>;
102 m_newlineList->append(t: QByteArrayMatcher(encode(ba: "\r\n", codec)));
103 m_newlineList->append(t: QByteArrayMatcher(encode(ba: "\n", codec)));
104 m_newlineList->append(t: QByteArrayMatcher(encode(ba: "\r", codec)));
105
106 m_previousCodec = codec;
107}
108
109/*!
110 * Finds a property in the \a document with the given \a propertyName, adds it to \a toBeRemoved,
111 * and returns it.
112 */
113QVersitProperty VersitUtils::takeProperty(const QVersitDocument& document,
114 const QString& propertyName,
115 QList<QVersitProperty>* toBeRemoved) {
116 foreach (const QVersitProperty& currentProperty, document.properties()) {
117 if (currentProperty.name() == propertyName) {
118 *toBeRemoved << currentProperty;
119 return currentProperty;
120 }
121 }
122 return QVersitProperty();
123}
124
125/*!
126 * Returns true iff \a bytes is a valid UTF-8 sequence.
127 */
128bool VersitUtils::isValidUtf8(const QByteArray& bytes) {
129 int sequenceLength = 1; // number of bytes in total for a sequence
130 int continuation = 0; // number of bytes left in a continuation
131 quint32 codePoint = 0;
132 for (int i = 0; i < bytes.size(); i++) {
133 quint8 byte = bytes[i];
134 if (continuation == 0) {
135 if (byte & 0x80) { // 1xxxxxxx
136 if (byte & 0x40) { // 11xxxxxx
137 if (byte == 0xc0 || byte == 0xc1) // 1100000x
138 return false; // overlong 2 byte sequence
139 if (byte & 0x20) { // 111xxxxx
140 if (byte & 0x10) { // 1111xxxx
141 if (byte & 0x08) { // 11111xxx
142 // Outside unicode range
143 return false;
144 } else { // 11110xxx
145 sequenceLength = 4;
146 continuation = 3; // three more bytes
147 codePoint = byte & 0x07; // take the last 3 bits
148 }
149 } else { // 1110xxxx
150 sequenceLength = 3;
151 continuation = 2; // two more bytes
152 codePoint = byte & 0x0f; // take last 4 bits
153 }
154 } else { // 110xxxxx
155 sequenceLength = 2;
156 continuation = 1; // one more byte
157 codePoint = byte & 0x1f; // take last 5 bits
158 }
159 } else { // 10xxxxxx
160 // unexpected continuation
161 return false;
162 }
163 } else { // 0xxxxxxx
164 sequenceLength = 1;
165 }
166 } else { // continuation > 0
167 if ((byte & 0xc0) != 0x80) // 10xxxxxx
168 return false; // expected continuation not found
169 codePoint = (codePoint << 6) | (byte & 0x3f); // append last 6 bits
170 continuation--;
171 }
172
173 if (continuation == 0) {
174 // Finished decoding a character - it's not overlong and that it's in range
175 switch (sequenceLength) {
176 // 1-byte sequence can't be overlong
177 // 2-byte sequence has already been checked for overlongness
178 case 3:
179 if (codePoint < 0x800) // overlong
180 return false;
181
182 // Filter out codepoints outside the Unicode range
183 if ((codePoint >= 0xd800 && codePoint <= 0xdfff) // utf-16 surrogate halves
184 || (codePoint >= 0xfffe && codePoint <= 0xffff)) { // reversed utf-16 BOM
185 return false;
186 }
187 break;
188 case 4:
189 if (codePoint < 0x10000 // overlong
190 || codePoint > 0x10ffff) // above Unicode range
191 return false;
192 break;
193 }
194 codePoint = 0;
195 }
196 }
197 return continuation == 0;
198}
199
200/*!
201 * Convert variant \a data to string \a json in JSON format.
202 *
203 * The data is encoded as an array containing one item
204 * to allow the same encoding to be used for both
205 * primitive and compound data types.
206 *
207 * Returns true if the conversion is successful, false otherwise.
208 *
209 * \sa convertFromJson()
210 */
211bool VersitUtils::convertToJson(const QVariant &data, QString *json)
212{
213 const QJsonValue dataAsJsonValue = QJsonValue::fromVariant(variant: data);
214 if (data.isValid() && dataAsJsonValue.isNull())
215 return false;
216 QJsonArray jsonArray;
217 jsonArray.append(value: dataAsJsonValue);
218 const QJsonDocument jsonDocument(jsonArray);
219 *json = QString::fromUtf8(str: jsonDocument.toJson());
220 return true;
221}
222
223/*!
224 * Convert string \a json in JSON format to variant \a data.
225 *
226 * The format of the json string is assumed to be a one-item array.
227 *
228 * Returns true if the conversion is successful, false otherwise.
229 *
230 * \sa convertToJson()
231 */
232bool VersitUtils::convertFromJson(const QString &json, QVariant *data)
233{
234 const QJsonDocument jsonDoc = QJsonDocument::fromJson(json: json.toUtf8());
235 const QJsonValue jsonValue = jsonDoc.array().at(i: 0);
236 if (jsonValue.isUndefined())
237 return false;
238 *data = jsonValue.toVariant();
239 return true;
240}
241
242QT_END_NAMESPACE_VERSIT
243

source code of qtpim/src/versit/qversitutils.cpp