1
/****************************************************************************
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4
** Contact: http://www.qt-project.org/legal
6
** This file is part of the QtVersit module of the Qt Toolkit.
8
** $QT_BEGIN_LICENSE:LGPL$
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 Digia. For licensing terms and
14
** conditions see http://qt.digia.com/licensing. For further information
15
** use the contact form at http://qt.digia.com/contact-us.
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 as published by the Free Software
20
** Foundation and appearing in the file LICENSE.LGPL included in the
21
** packaging of this file. Please review the following information to
22
** ensure the GNU Lesser General Public License version 2.1 requirements
23
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25
** In addition, as a special exception, Digia gives you certain additional
26
** rights. These rights are described in the Digia Qt LGPL Exception
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29
** GNU General Public License Usage
30
** Alternatively, this file may be used under the terms of the GNU
31
** General Public License version 3.0 as published by the Free Software
32
** Foundation and appearing in the file LICENSE.GPL included in the
33
** packaging of this file. Please review the following information to
34
** ensure the GNU General Public License version 3.0 requirements will be
35
** met: http://www.gnu.org/copyleft/gpl.html.
40
****************************************************************************/
42
#include "qvcard21writer_p.h"
46
QT_BEGIN_NAMESPACE_VERSIT
48
/*! Constructs a writer. */
49
QVCard21Writer::QVCard21Writer(QVersitDocument::VersitType type) : QVersitDocumentWriter(type)
53
QTextEncoder* QVCard21Writer::utf8Encoder()
55
static QTextEncoder* encoder = 0;
57
encoder = QTextCodec::codecForName("UTF-8")->makeEncoder();
58
// Hack so the encoder doesn't output a byte order mark
59
encoder->fromUnicode(QString());
64
/*! Destroys a writer. */
65
QVCard21Writer::~QVCard21Writer()
70
* Encodes the \a property and writes it to the device.
72
void QVCard21Writer::encodeVersitProperty(const QVersitProperty& property)
74
encodeGroupsAndName(property);
75
QMultiHash<QString,QString> parameters = property.parameters();
76
QVariant variant(property.variantValue());
78
QString renderedValue;
79
QByteArray renderedBytes;
81
/* Structured values need to have their components backslash-escaped (in vCard 2.1, semicolons
82
must be escaped for compound values and commas must be escaped for list values). */
83
if (variant.type() == QVariant::StringList) {
84
QStringList values = property.variantValue().toStringList();
86
if (property.valueType() == QVersitProperty::CompoundType) {
87
separator = QStringLiteral(";");
89
if (property.valueType() != QVersitProperty::ListType) {
90
qWarning("Variant value is a QStringList but the property's value type is neither "
91
"CompoundType or ListType");
93
// Assume it's a ListType
94
separator = QStringLiteral(",");
96
QString replacement = QLatin1Char('\\') + separator;
97
QRegExp separatorRegex = QRegExp(separator);
99
// Check first if any of the values need to be UTF-8 encoded (if so, all of them must be
101
bool forceUtf8 = requiresUtf8(values);
104
foreach (QString value, values) {
105
if (!(value.isEmpty() && property.valueType() == QVersitProperty::ListType)) {
106
encodeVersitValue(parameters, value, forceUtf8);
108
renderedValue += separator;
110
renderedValue += value.replace(separatorRegex, replacement);
114
} else if (variant.type() == QVariant::String) {
115
renderedValue = variant.toString();
116
encodeVersitValue(parameters, renderedValue, false);
117
} else if (variant.type() == QVariant::ByteArray) {
118
parameters.replace(QStringLiteral("ENCODING"), QStringLiteral("BASE64"));
119
if (mCodecIsAsciiCompatible) // optimize by not converting to unicode
120
renderedBytes = variant.toByteArray().toBase64();
122
renderedValue = QLatin1String(variant.toByteArray().toBase64().data());
126
encodeParameters(parameters);
129
writeString(QStringLiteral(":"));
130
if (variant.canConvert<QVersitDocument>()) {
132
QVersitDocument embeddedDocument = variant.value<QVersitDocument>();
133
encodeVersitDocument(embeddedDocument);
134
} else if (variant.type() == QVariant::String || variant.type() == QVariant::StringList) {
135
// Some devices don't support vCard-style line folding if the property is
136
// quoted-printable-encoded. Therefore, we use QP soft linebreaks if the property is being
137
// QP-encoded, and normal vCard folding otherwise.
138
if (parameters.contains(QStringLiteral("ENCODING"), QStringLiteral("QUOTED-PRINTABLE")))
139
writeStringQp(renderedValue);
141
writeString(renderedValue);
142
} else if (variant.type() == QVariant::ByteArray) {
143
// One extra folding before the value and
144
// one extra line break after the value are needed in vCard 2.1
146
writeString(QStringLiteral(" "));
147
if (renderedBytes.isEmpty())
148
writeString(renderedValue);
150
writeBytes(renderedBytes);
156
/*! Returns true if and only if the current codec is incapable of encoding any of the \a values */
157
bool QVCard21Writer::requiresUtf8(const QStringList& values) {
158
foreach (const QString& value, values) {
159
if (!mCodec->canEncode(value)
160
// if codec is ASCII and there is a character > U+007F in value, encode it as UTF-8
161
|| (mCodecIsAscii && containsNonAscii(value))) {
168
/*! Performs Quoted-Printable encoding and charset encoding on \a value as per vCard 2.1 spec.
169
Returns true if the value will need to be encoded with UTF-8, false if mCodec is sufficient. */
170
void QVCard21Writer::encodeVersitValue(QMultiHash<QString,QString>& parameters, QString& value,
173
// Add the CHARSET parameter, if necessary and encode in UTF-8 later
175
|| !mCodec->canEncode(value)
176
// if codec is ASCII and there is a character > U+007F in value, encode it as UTF-8
177
|| (mCodecIsAscii && containsNonAscii(value))) {
178
parameters.replace(QStringLiteral("CHARSET"), QStringLiteral("UTF-8"));
179
value = QString::fromLatin1(utf8Encoder()->fromUnicode(value));
182
// Quoted-Printable encode the value and add Quoted-Printable parameter, if necessary
183
if (quotedPrintableEncode(value))
184
parameters.replace(QStringLiteral("ENCODING"), QStringLiteral("QUOTED-PRINTABLE"));
187
int sortIndexOfTypeValue(const QString& type) {
188
if ( type == QStringLiteral("CELL")
189
|| type == QStringLiteral("FAX")) {
191
} else if (type == QStringLiteral("HOME")
192
|| type == QStringLiteral("WORK")) {
199
bool typeValueLessThan(const QString& a, const QString& b) {
200
return sortIndexOfTypeValue(a) < sortIndexOfTypeValue(b);
203
/*! Ensure CELL and FAX are at the front because they are "more important" and some vCard
204
parsers may ignore everything after the first TYPE */
205
void sortTypeValues(QStringList* values)
207
qSort(values->begin(), values->end(), typeValueLessThan);
211
* Encodes the \a parameters and writes it to the device.
213
void QVCard21Writer::encodeParameters(const QMultiHash<QString,QString>& parameters)
215
QList<QString> names = parameters.uniqueKeys();
216
foreach (const QString& name, names) {
217
QStringList values = parameters.values(name);
218
if (name == QStringLiteral("TYPE")) {
219
// TYPE parameters should be sorted
220
sortTypeValues(&values);
222
foreach (const QString& value, values) {
223
writeString(QStringLiteral(";"));
224
if (name.length() > 0 && name != QStringLiteral("TYPE")) {
226
writeString(QStringLiteral("="));
233
bool QVCard21Writer::containsNonAscii(const QString& str)
235
for (int i = 0; i < str.length(); i++) {
236
if (str[i].unicode() > 127)
243
* Encodes special characters in \a text
244
* using Quoted-Printable encoding (RFC 1521).
245
* Returns true if at least one character was encoded.
247
bool QVCard21Writer::quotedPrintableEncode(QString& text)
249
bool encoded = false;
250
for (int i=0; i<text.length(); i++) {
251
QChar current = text.at(i);
252
if (shouldBeQuotedPrintableEncoded(current)) {
253
QString encodedStr(QString::fromLatin1("=%1").
254
arg(current.unicode(), 2, 16, QLatin1Char('0')).toUpper());
255
text.replace(i, 1, encodedStr);
265
* Checks whether the \a chr should be Quoted-Printable encoded (RFC 1521).
267
bool QVCard21Writer::shouldBeQuotedPrintableEncoded(QChar chr)
269
int c = chr.unicode();
271
c == '!' || c == '"' || c == '#' || c == '$' ||
272
c == '=' || c == '@' || c == '[' || c == '\\' ||
273
c == ']' || c == '^' || c == '`' ||
274
(c > 122 && c < 256));
277
QT_END_NAMESPACE_VERSIT