• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdepimlibs-4.14.10 API Reference
  • KDE Home
  • Contact Us
 

kabc

  • kabc
  • vcardparser
vcardparser.cpp
1/*
2 This file is part of libkabc.
3 Copyright (c) 2003 Tobias Koenig <tokoe@kde.org>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19*/
20
21#include "vcardparser.h"
22#include <kcodecs.h>
23#include <kdebug.h>
24#include <QtCore/QTextCodec>
25
26class StringCache
27{
28public:
29 QString fromLatin1(const QByteArray &value)
30 {
31 if (value.isEmpty()) {
32 return QString();
33 }
34
35 QHash<QByteArray, QString>::const_iterator it = m_values.constFind(value);
36 if (it != m_values.constEnd()) {
37 return it.value();
38 }
39
40 QString string = QString::fromLatin1(value, value.size());
41 m_values.insert(value, string);
42 return string;
43 }
44
45private:
46 QHash<QByteArray, QString> m_values;
47};
48
49#define FOLD_WIDTH 75
50
51using namespace KABC;
52
53static void addEscapes( QByteArray &str, bool excludeEscapteComma )
54{
55 str.replace( '\\', (char *)"\\\\" );
56 if ( !excludeEscapteComma ) {
57 str.replace( ',', (char *)"\\," );
58 }
59 str.replace( '\r', (char *)"\\r" );
60 str.replace( '\n', (char *)"\\n" );
61}
62
63static void removeEscapes( QByteArray &str )
64{
65 str.replace( (char *)"\\n", "\n" );
66 str.replace( (char *)"\\N", "\n" );
67 str.replace( (char *)"\\r", "\r" );
68 str.replace( (char *)"\\,", "," );
69 str.replace( (char *)"\\\\", "\\" );
70}
71
72VCardParser::VCardParser()
73 : d(0)
74{
75}
76
77VCardParser::~VCardParser()
78{
79}
80
81VCard::List VCardParser::parseVCards( const QByteArray &text )
82{
83 VCard currentVCard;
84 VCard::List vCardList;
85 QByteArray currentLine;
86
87 QList<QByteArray> lines = text.split( '\n' );
88
89 bool inVCard = false;
90 QList<QByteArray>::Iterator it( lines.begin() );
91 QList<QByteArray>::Iterator linesEnd( lines.end() );
92
93 StringCache cache;
94
95 for ( ; it != linesEnd; ++it ) {
96 // remove the trailing \r, left from \r\n
97 if ( ( *it ).endsWith( '\r' ) ) {
98 ( *it ).chop( 1 );
99 }
100
101 if ( ( *it ).startsWith( ' ' ) ||
102 ( *it ).startsWith( '\t' ) ) { //folded line => append to previous
103 currentLine.append( ( *it ).mid( 1 ) );
104 continue;
105 } else {
106 if ( ( *it ).trimmed().isEmpty() ) { // empty line
107 continue;
108 }
109 if ( inVCard && !currentLine.isEmpty() ) { // now parse the line
110 int colon = currentLine.indexOf( ':' );
111 if ( colon == -1 ) { // invalid line
112 currentLine = ( *it );
113 continue;
114 }
115
116 VCardLine vCardLine;
117 const QByteArray key = currentLine.left( colon ).trimmed();
118 QByteArray value = currentLine.mid( colon + 1 );
119
120 QList<QByteArray> params = key.split( ';' );
121
122 // check for group
123 int groupPos = params[ 0 ].indexOf( '.' );
124 if ( groupPos != -1 ) {
125 vCardLine.setGroup( cache.fromLatin1( params[ 0 ].left( groupPos ) ) );
126 vCardLine.setIdentifier( cache.fromLatin1( params[ 0 ].mid( groupPos + 1 ) ) );
127 } else {
128 vCardLine.setIdentifier( cache.fromLatin1( params[ 0 ] ) );
129 }
130
131 if ( params.count() > 1 ) { // find all parameters
132 QList<QByteArray>::ConstIterator paramIt( params.constBegin() );
133 for ( ++paramIt; paramIt != params.constEnd(); ++paramIt ) {
134 QList<QByteArray> pair = ( *paramIt ).split( '=' );
135 if ( pair.count() == 1 ) {
136 // correct the fucking 2.1 'standard'
137 if ( pair[ 0 ].toLower() == "quoted-printable" ) {
138 pair[ 0 ] = "encoding";
139 pair.append( "quoted-printable" );
140 } else if ( pair[ 0 ].toLower() == "base64" ) {
141 pair[ 0 ] = "encoding";
142 pair.append( "base64" );
143 } else {
144 pair.prepend( "type" );
145 }
146 }
147 if ( pair[ 1 ].indexOf( ',' ) != -1 ) { // parameter in type=x,y,z format
148 const QList<QByteArray> args = pair[ 1 ].split( ',' );
149 QList<QByteArray>::ConstIterator argIt;
150 QList<QByteArray>::ConstIterator argEnd( args.constEnd() );
151 for ( argIt = args.constBegin(); argIt != argEnd; ++argIt ) {
152 vCardLine.addParameter( cache.fromLatin1( pair[ 0 ].toLower() ),
153 cache.fromLatin1( *argIt ) );
154 }
155 } else {
156 vCardLine.addParameter( cache.fromLatin1( pair[ 0 ].toLower() ),
157 cache.fromLatin1( pair[ 1 ] ) );
158 }
159 }
160 }
161
162 removeEscapes( value );
163
164 QByteArray output;
165 bool wasBase64Encoded = false;
166
167 if ( vCardLine.parameterList().contains( QLatin1String( "encoding" ) ) ) {
168 const QString encoding = vCardLine.parameter( QLatin1String( "encoding" ) ).toLower();
169
170 // have to decode the data
171 if ( encoding == QLatin1String( "b" ) || encoding == QLatin1String( "base64" ) ) {
172 output = QByteArray::fromBase64( value );
173 wasBase64Encoded = true;
174 }
175 else if ( encoding == QLatin1String( "quoted-printable" ) ) {
176 // join any qp-folded lines
177 while ( value.endsWith( '=' ) && it != linesEnd ) {
178 value.chop( 1 ); // remove the '='
179 value.append( *it );
180 ++it;
181 // remove the trailing \r, left from \r\n
182 if ( (*it).endsWith('\r') ) {
183 (*it).chop(1);
184 }
185 }
186 KCodecs::quotedPrintableDecode( value, output );
187 } else if ( encoding == QLatin1String( "8bit" ) ) {
188 output = value;
189 } else {
190 qDebug( "Unknown vcard encoding type!" );
191 }
192 } else {
193 output = value;
194 }
195
196 if ( vCardLine.parameterList().contains( QLatin1String( "charset" ) ) ) {
197 // have to convert the data
198 QTextCodec *codec = QTextCodec::codecForName(
199 vCardLine.parameter( QLatin1String( "charset" ) ).toLatin1() );
200 if ( codec ) {
201 vCardLine.setValue( codec->toUnicode( output ) );
202 } else {
203 vCardLine.setValue( QString::fromUtf8( output ) );
204 }
205 } else if ( wasBase64Encoded ) {
206 vCardLine.setValue( output );
207 } else {
208 vCardLine.setValue( QString::fromUtf8( output ) );
209 }
210
211 currentVCard.addLine( vCardLine );
212 }
213
214 // we do not save the start and end tag as vcardline
215 if ( ( *it ).toLower().startsWith( "begin:vcard" ) ) { //krazy:exclude=strings
216 inVCard = true;
217 currentLine.clear();
218 currentVCard.clear(); // flush vcard
219 continue;
220 }
221
222 if ( ( *it ).toLower().startsWith( "end:vcard" ) ) { //krazy:exclude=strings
223 inVCard = false;
224 vCardList.append( currentVCard );
225 currentLine.clear();
226 currentVCard.clear(); // flush vcard
227 continue;
228 }
229
230 currentLine = ( *it );
231 }
232 }
233
234 return vCardList;
235}
236
237QByteArray VCardParser::createVCards( const VCard::List &list )
238{
239 QByteArray text;
240 QByteArray textLine;
241 QString encodingType;
242 QStringList idents;
243 QStringList params;
244 QStringList values;
245 QStringList::ConstIterator identIt;
246 QStringList::Iterator paramIt;
247 QStringList::ConstIterator valueIt;
248
249 VCardLine::List lines;
250 VCardLine::List::ConstIterator lineIt;
251 VCard::List::ConstIterator cardIt;
252
253 bool hasEncoding;
254
255 text.reserve( list.size() * 300 ); // reserve memory to be more efficient
256
257 // iterate over the cards
258 VCard::List::ConstIterator listEnd( list.end() );
259 for ( cardIt = list.begin(); cardIt != listEnd; ++cardIt ) {
260 text.append( "BEGIN:VCARD\r\n" );
261
262 idents = ( *cardIt ).identifiers();
263 //VERSION must be first
264 if (idents.contains(QLatin1String("VERSION"))) {
265 const QString str = idents.takeAt(idents.indexOf(QLatin1String("VERSION")));
266 idents.prepend(str);
267 }
268
269 for ( identIt = idents.constBegin(); identIt != idents.constEnd(); ++identIt ) {
270 lines = ( *cardIt ).lines( ( *identIt ) );
271
272 // iterate over the lines
273 for ( lineIt = lines.constBegin(); lineIt != lines.constEnd(); ++lineIt ) {
274 QVariant val = ( *lineIt ).value();
275 if ( val.isValid() ) {
276 if ( ( *lineIt ).hasGroup() ) {
277 textLine = ( *lineIt ).group().toLatin1() + '.' + ( *lineIt ).identifier().toLatin1();
278 } else {
279 textLine = ( *lineIt ).identifier().toLatin1();
280 }
281
282 params = ( *lineIt ).parameterList();
283 hasEncoding = false;
284 if ( !params.isEmpty() ) { // we have parameters
285 for ( paramIt = params.begin(); paramIt != params.end(); ++paramIt ) {
286 if ( ( *paramIt ) == QLatin1String( "encoding" ) ) {
287 hasEncoding = true;
288 encodingType = ( *lineIt ).parameter( QLatin1String( "encoding" ) ).toLower();
289 }
290
291 values = ( *lineIt ).parameters( *paramIt );
292 for ( valueIt = values.constBegin(); valueIt != values.constEnd(); ++valueIt ) {
293 textLine.append( ';' + ( *paramIt ).toLatin1().toUpper() );
294 if ( !( *valueIt ).isEmpty() ) {
295 textLine.append( '=' + ( *valueIt ).toLatin1() );
296 }
297 }
298 }
299 }
300 QByteArray input, output;
301 bool checkMultibyte = false; // avoid splitting a multibyte character
302
303 // handle charset
304 if ( ( *lineIt ).parameterList().contains( QLatin1String( "charset" ) ) ) {
305 // have to convert the data
306 const QString value = ( *lineIt ).value().toString();
307 QTextCodec *codec = QTextCodec::codecForName(
308 ( *lineIt ).parameter( QLatin1String( "charset" ) ).toLatin1() );
309 if ( codec ) {
310 input = codec->fromUnicode( value );
311 } else {
312 checkMultibyte = true;
313 input = value.toUtf8();
314 }
315 } else if ( ( *lineIt ).value().type() == QVariant::ByteArray ) {
316 input = ( *lineIt ).value().toByteArray();
317 } else {
318 checkMultibyte = true;
319 input = ( *lineIt ).value().toString().toUtf8();
320 }
321
322 // handle encoding
323 if ( hasEncoding ) { // have to encode the data
324 if ( encodingType == QLatin1String( "b" ) ) {
325 checkMultibyte = false;
326 output = input.toBase64();
327 } else if ( encodingType == QLatin1String( "quoted-printable" ) ) {
328 checkMultibyte = false;
329 KCodecs::quotedPrintableEncode( input, output, false );
330 }
331 } else {
332 output = input;
333 }
334 addEscapes( output, (( *lineIt ).identifier() == QLatin1String( "CATEGORIES" )|| ( *lineIt ).identifier() == QLatin1String( "GEO" )) );
335
336 if ( !output.isEmpty() ) {
337 textLine.append( ':' + output );
338
339 if ( textLine.length() > FOLD_WIDTH ) { // we have to fold the line
340 if ( checkMultibyte ) {
341 // RFC 6350: Multi-octet characters MUST remain contiguous.
342 // we know that textLine contains UTF-8 encoded characters
343 int lineLength = 0;
344 for ( int i = 0; i < textLine.length(); ++i ) {
345 if ( (textLine[i] & 0xC0) == 0xC0 ) { // a multibyte sequence follows
346 int sequenceLength = 2;
347 if ( (textLine[i] & 0xE0) == 0xE0 ) {
348 sequenceLength = 3;
349 } else if ( (textLine[i] & 0xF0) == 0xF0 ) {
350 sequenceLength = 4;
351 }
352 if ( (lineLength + sequenceLength) > FOLD_WIDTH ) {
353 // the current line would be too long. fold it
354 text += "\r\n " + textLine.mid(i, sequenceLength);
355 lineLength = 1 + sequenceLength; // incl. leading space
356 } else {
357 text += textLine.mid(i, sequenceLength);
358 lineLength += sequenceLength;
359 }
360 i += sequenceLength - 1;
361 } else {
362 text += textLine[i];
363 ++lineLength;
364 }
365 if ( (lineLength == FOLD_WIDTH) && (i < (textLine.length() - 1)) ) {
366 text += "\r\n ";
367 lineLength = 1; // leading space
368 }
369 }
370 text += "\r\n";
371 } else {
372 for ( int i = 0; i <= ( textLine.length() / FOLD_WIDTH ); ++i ) {
373 text.append(
374 ( i == 0 ? "" : " " ) + textLine.mid( i * FOLD_WIDTH, FOLD_WIDTH ) + "\r\n" );
375 }
376 }
377 } else {
378 text.append( textLine + "\r\n" );
379 }
380 }
381 }
382 }
383 }
384
385 text.append( "END:VCARD\r\n" );
386 text.append( "\r\n" );
387 }
388
389 return text;
390}
KABC
Class that holds a Calendar Url (FBURL/CALADRURI/CALURI)
Definition: address.h:29
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Thu Jul 21 2022 00:00:00 by doxygen 1.9.5 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kabc

Skip menu "kabc"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Related Pages

kdepimlibs-4.14.10 API Reference

Skip menu "kdepimlibs-4.14.10 API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal