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 "qversitdocumentwriter_p.h"
35
36#include <QtCore/qiodevice.h>
37#include <QtCore/qtextcodec.h>
38
39#include "qversitutils_p.h"
40
41QT_BEGIN_NAMESPACE_VERSIT
42
43#define MAX_LINE_LENGTH 76
44
45/*!
46 \class QVersitDocumentWriter
47 \internal
48 \brief The QVersitDocumentWriter class provides an interface for writing a
49 single versit document into a vCard text string.
50 */
51
52/*! Constructs a writer.
53 * \a documentType is the type of Versit document, as printed on the BEGIN line of output
54 * eg. "VCARD"
55 * \a version is the version of the Versit format, as printed on the VERSION line of output.
56 * eg. "2.1"
57 */
58QVersitDocumentWriter::QVersitDocumentWriter(QVersitDocument::VersitType type)
59 : mType(type),
60 mDevice(0),
61 mCodec(0),
62 mCodecIsAscii(false),
63 mEncoder(0),
64 mSuccessful(true),
65 mCurrentLineLength(0)
66{
67}
68
69QVersitDocumentWriter::~QVersitDocumentWriter()
70{
71 if (mEncoder)
72 delete mEncoder;
73}
74
75/*!
76 Sets the codec to write with.
77 */
78void QVersitDocumentWriter::setCodec(QTextCodec *codec)
79{
80 if (mEncoder)
81 delete mEncoder;
82 mCodec = codec;
83 mEncoder = codec->makeEncoder();
84
85 // Hack so the encoder doesn't output a byte order mark for UTF-8.
86 if (mCodec->name() == "UTF-8")
87 mEncoder->fromUnicode(str: QString());
88
89 // UTF-(16|32)(LE|BE) are the only codecs where characters in the base64 range aren't encoded
90 // the same as in ASCII. For ASCII compatible codecs, we can do some optimizations.
91 mCodecIsAsciiCompatible = !(mCodec->name().startsWith(c: "UTF-16")
92 || mCodec->name().startsWith(c: "UTF-32"));
93}
94
95/*!
96 Specifies that the codec is actually ASCII (setCodec must also be called with an ASCII-compatible
97 codec.
98 */
99void QVersitDocumentWriter::setAsciiCodec()
100{
101 mCodecIsAscii = true;
102}
103
104/*!
105 Sets the device to write to.
106 */
107void QVersitDocumentWriter::setDevice(QIODevice *device)
108{
109 mDevice = device;
110}
111
112/*!
113 * Encodes the \a document and writes it to the device. A "VERSION:" line is added iff \a
114 * encodeVersion is true.
115 */
116bool QVersitDocumentWriter::encodeVersitDocument(const QVersitDocument& document, bool encodeVersion)
117{
118 mSuccessful = true;
119
120 if (document.componentType().isEmpty()) {
121 // for compatibility with code for Qt Mobility 1.0, which didn't have componentType
122 writeString(QStringLiteral("BEGIN:VCARD"));
123 } else {
124 writeString(QStringLiteral("BEGIN:") + document.componentType());
125 }
126 writeCrlf();
127 if (encodeVersion) {
128 switch (mType) {
129 case QVersitDocument::VCard21Type:
130 writeString(QStringLiteral("VERSION:2.1"));
131 writeCrlf();
132 break;
133 case QVersitDocument::VCard30Type:
134 writeString(QStringLiteral("VERSION:3.0"));
135 writeCrlf();
136 break;
137 case QVersitDocument::VCard40Type:
138 writeString(QStringLiteral("VERSION:4.0"));
139 writeCrlf();
140 break;
141 case QVersitDocument::ICalendar20Type:
142 writeString(QStringLiteral("VERSION:2.0"));
143 writeCrlf();
144 break;
145 default:
146 ; // don't print version
147 }
148 }
149
150 foreach (const QVersitProperty& property, document.properties()) {
151 encodeVersitProperty(property);
152 }
153
154 foreach (const QVersitDocument& document, document.subDocuments()) {
155 encodeVersitDocument(document, encodeVersion: false);
156 }
157
158 if (document.componentType().isEmpty()) {
159 writeString(QStringLiteral("END:VCARD"));
160 } else {
161 writeString(QStringLiteral("END:") + document.componentType());
162 }
163 writeCrlf();
164
165 // This has been set by the methods called from this function
166 return mSuccessful;
167}
168
169/*!
170 * Encodes the groups and name in the \a property and writes it to the device
171 */
172void QVersitDocumentWriter::encodeGroupsAndName(const QVersitProperty& property)
173{
174 QStringList groups = property.groups();
175 if (!groups.isEmpty()) {
176 writeString(value: groups.join(QStringLiteral(".")));
177 writeString(QStringLiteral("."));
178 }
179 writeString(value: property.name());
180}
181
182/*!
183 Writes \a value to the device.
184
185 This function tracks how many characters have been written to the line and folds (wraps) the line
186 according to RFC2425.
187 */
188void QVersitDocumentWriter::writeBytes(const QByteArray &value)
189{
190 int spaceRemaining = MAX_LINE_LENGTH - mCurrentLineLength;
191 int charsWritten = 0;
192 while (spaceRemaining < value.length() - charsWritten) {
193 // Write the first "spaceRemaining" characters
194 if (mDevice->write(data: value.constData() + charsWritten, len: spaceRemaining) < 0
195 || mDevice->write(data: "\r\n ") < 0)
196 mSuccessful = false;
197 charsWritten += spaceRemaining;
198 spaceRemaining = MAX_LINE_LENGTH - 1; // minus 1 for the space at the front.
199 mCurrentLineLength = 1;
200 }
201
202 if (mDevice->write(data: value.constData() + charsWritten) < 0)
203 mSuccessful = false;
204 mCurrentLineLength += value.length() - charsWritten;
205}
206
207/*!
208 Writes \a value to the device.
209
210 This function tracks how many characters have been written to the line and folds (wraps) the line
211 according to RFC2425.
212 */
213void QVersitDocumentWriter::writeString(const QString &value)
214{
215 int spaceRemaining = MAX_LINE_LENGTH - mCurrentLineLength;
216 int charsWritten = 0;
217 QString crlfSpace(QStringLiteral("\r\n "));
218 while (spaceRemaining < value.length() - charsWritten) {
219 // Write the first "spaceRemaining" characters
220 QStringRef line(&value, charsWritten, spaceRemaining);
221 charsWritten += spaceRemaining;
222 if (mDevice->write(data: mEncoder->fromUnicode(uc: line.constData(), len: line.length())) < 0
223 || mDevice->write(data: mEncoder->fromUnicode(str: crlfSpace)) < 0)
224 mSuccessful = false;
225 spaceRemaining = MAX_LINE_LENGTH - 1; // minus 1 for the space at the front.
226 mCurrentLineLength = 1;
227 }
228
229 if (mDevice->write(data: mEncoder->fromUnicode(str: value.mid(position: charsWritten))) < 0)
230 mSuccessful = false;
231 mCurrentLineLength += value.length() - charsWritten;
232}
233
234/*!
235 Writes \a value to the device.
236
237 This function tracks how many characters have been written to the line and wraps the line
238 according to RFC2045 (a Quoted-Printable soft line break is an EQUALS-CR-LF sequence)
239 */
240void QVersitDocumentWriter::writeStringQp(const QString &value)
241{
242 int spaceRemaining = MAX_LINE_LENGTH - mCurrentLineLength - 1;
243 // minus 1 for the equals required at the end
244 int charsWritten = 0;
245 QString softBreak(QStringLiteral("=\r\n"));
246 while (spaceRemaining < value.length() - charsWritten) {
247 // Write the first "spaceRemaining" characters
248 if (value[charsWritten + spaceRemaining - 2] == QLatin1Char('=')) {
249 spaceRemaining -= 2;
250 } else if (value[charsWritten + spaceRemaining - 1] == QLatin1Char('=')) {
251 spaceRemaining -= 1;
252 }
253 QStringRef line(&value, charsWritten, spaceRemaining);
254
255 charsWritten += spaceRemaining;
256 if (mDevice->write(data: mEncoder->fromUnicode(uc: line.constData(), len: line.length())) < 0
257 || mDevice->write(data: mEncoder->fromUnicode(str: softBreak)) < 0)
258 mSuccessful = false;
259 spaceRemaining = MAX_LINE_LENGTH - 1; // minus 1 for the equals required at the end
260 mCurrentLineLength = 0;
261 }
262
263 if (mDevice->write(data: mEncoder->fromUnicode(str: value.mid(position: charsWritten))) < 0)
264 mSuccessful = false;
265 mCurrentLineLength += value.length() - charsWritten;
266}
267
268/*!
269 Writes a CRLF to the device. By using this function, rather than writeString("\\r\\n"), you will
270 allow the writer to know where a line starts, for folding purposes.
271 */
272void QVersitDocumentWriter::writeCrlf()
273{
274 writeString(QStringLiteral("\r\n"));
275 mCurrentLineLength = 0;
276}
277
278QT_END_NAMESPACE_VERSIT
279

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