~chris.gagnon/+junk/qtpim-coverage

« back to all changes in this revision

Viewing changes to src/versit/qvcard21writer.cpp

  • Committer: chris.gagnon
  • Date: 2013-12-10 23:09:37 UTC
  • Revision ID: chris.gagnon@canonical.com-20131210230937-2akf1ft1edcttk87
first post

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
 
4
** Contact: http://www.qt-project.org/legal
 
5
**
 
6
** This file is part of the QtVersit module of the Qt Toolkit.
 
7
**
 
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.
 
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 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.
 
24
**
 
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.
 
28
**
 
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.
 
36
**
 
37
**
 
38
** $QT_END_LICENSE$
 
39
**
 
40
****************************************************************************/
 
41
 
 
42
#include "qvcard21writer_p.h"
 
43
 
 
44
#include <QTextCodec>
 
45
 
 
46
QT_BEGIN_NAMESPACE_VERSIT
 
47
 
 
48
/*! Constructs a writer. */
 
49
QVCard21Writer::QVCard21Writer(QVersitDocument::VersitType type) : QVersitDocumentWriter(type)
 
50
{
 
51
}
 
52
 
 
53
QTextEncoder* QVCard21Writer::utf8Encoder()
 
54
{
 
55
    static QTextEncoder* encoder = 0;
 
56
    if (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());
 
60
    }
 
61
    return encoder;
 
62
}
 
63
 
 
64
/*! Destroys a writer. */
 
65
QVCard21Writer::~QVCard21Writer()
 
66
{
 
67
}
 
68
 
 
69
/*!
 
70
 * Encodes the \a property and writes it to the device.
 
71
 */
 
72
void QVCard21Writer::encodeVersitProperty(const QVersitProperty& property)
 
73
{
 
74
    encodeGroupsAndName(property);
 
75
    QMultiHash<QString,QString> parameters = property.parameters();
 
76
    QVariant variant(property.variantValue());
 
77
 
 
78
    QString renderedValue;
 
79
    QByteArray renderedBytes;
 
80
 
 
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();
 
85
        QString separator;
 
86
        if (property.valueType() == QVersitProperty::CompoundType) {
 
87
            separator = QStringLiteral(";");
 
88
        } else {
 
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");
 
92
            }
 
93
            // Assume it's a ListType
 
94
            separator = QStringLiteral(",");
 
95
        }
 
96
        QString replacement = QLatin1Char('\\') + separator;
 
97
        QRegExp separatorRegex = QRegExp(separator);
 
98
 
 
99
        // Check first if any of the values need to be UTF-8 encoded (if so, all of them must be
 
100
        // UTF-8 encoded)
 
101
        bool forceUtf8 = requiresUtf8(values);
 
102
 
 
103
        bool first = true;
 
104
        foreach (QString value, values) {
 
105
            if (!(value.isEmpty() && property.valueType() == QVersitProperty::ListType)) {
 
106
                encodeVersitValue(parameters, value, forceUtf8);
 
107
                if (!first) {
 
108
                    renderedValue += separator;
 
109
                }
 
110
                renderedValue += value.replace(separatorRegex, replacement);
 
111
                first = false;
 
112
            }
 
113
        }
 
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();
 
121
        else
 
122
            renderedValue = QLatin1String(variant.toByteArray().toBase64().data());
 
123
    }
 
124
 
 
125
    // Encode parameters
 
126
    encodeParameters(parameters);
 
127
 
 
128
    // Encode value
 
129
    writeString(QStringLiteral(":"));
 
130
    if (variant.canConvert<QVersitDocument>()) {
 
131
        writeCrlf();
 
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);
 
140
        else
 
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
 
145
        writeCrlf();
 
146
        writeString(QStringLiteral(" "));
 
147
        if (renderedBytes.isEmpty())
 
148
            writeString(renderedValue);
 
149
        else
 
150
            writeBytes(renderedBytes);
 
151
        writeCrlf();
 
152
    }
 
153
    writeCrlf();
 
154
}
 
155
 
 
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))) {
 
162
            return true;
 
163
        }
 
164
    }
 
165
    return false;
 
166
}
 
167
 
 
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,
 
171
                                       bool forceUtf8)
 
172
{
 
173
    // Add the CHARSET parameter, if necessary and encode in UTF-8 later
 
174
    if (forceUtf8
 
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));
 
180
    }
 
181
 
 
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"));
 
185
}
 
186
 
 
187
int sortIndexOfTypeValue(const QString& type) {
 
188
    if (   type == QStringLiteral("CELL")
 
189
        || type == QStringLiteral("FAX")) {
 
190
        return 0;
 
191
    } else if (type == QStringLiteral("HOME")
 
192
            || type == QStringLiteral("WORK")) {
 
193
        return 1;
 
194
    } else {
 
195
        return 2;
 
196
    }
 
197
}
 
198
 
 
199
bool typeValueLessThan(const QString& a, const QString& b) {
 
200
    return sortIndexOfTypeValue(a) < sortIndexOfTypeValue(b);
 
201
}
 
202
 
 
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)
 
206
{
 
207
    qSort(values->begin(), values->end(), typeValueLessThan);
 
208
}
 
209
 
 
210
/*!
 
211
 * Encodes the \a parameters and writes it to the device.
 
212
 */
 
213
void QVCard21Writer::encodeParameters(const QMultiHash<QString,QString>& parameters)
 
214
{
 
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);
 
221
        }
 
222
        foreach (const QString& value, values) {
 
223
            writeString(QStringLiteral(";"));
 
224
            if (name.length() > 0 && name != QStringLiteral("TYPE")) {
 
225
                writeString(name);
 
226
                writeString(QStringLiteral("="));
 
227
            }
 
228
            writeString(value);
 
229
        }
 
230
    }
 
231
}
 
232
 
 
233
bool QVCard21Writer::containsNonAscii(const QString& str)
 
234
{
 
235
    for (int i = 0; i < str.length(); i++) {
 
236
        if (str[i].unicode() > 127)
 
237
            return true;
 
238
    }
 
239
    return false;
 
240
}
 
241
 
 
242
/*!
 
243
 * Encodes special characters in \a text
 
244
 * using Quoted-Printable encoding (RFC 1521).
 
245
 * Returns true if at least one character was encoded.
 
246
 */
 
247
bool QVCard21Writer::quotedPrintableEncode(QString& text)
 
248
{
 
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);
 
256
            i += 2;
 
257
            encoded = true;
 
258
        }
 
259
    }
 
260
    return encoded;
 
261
}
 
262
 
 
263
 
 
264
/*!
 
265
 * Checks whether the \a chr should be Quoted-Printable encoded (RFC 1521).
 
266
 */
 
267
bool QVCard21Writer::shouldBeQuotedPrintableEncoded(QChar chr)
 
268
{
 
269
    int c = chr.unicode();
 
270
    return (c < 32 ||
 
271
            c == '!' || c == '"' || c == '#' || c == '$' ||
 
272
            c == '=' || c == '@' || c == '[' || c == '\\' ||
 
273
            c == ']' || c == '^' || c == '`' ||
 
274
            (c > 122 && c < 256));
 
275
}
 
276
 
 
277
QT_END_NAMESPACE_VERSIT