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

kabc

  • sources
  • kde-4.14
  • kdepimlibs
  • 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 
26 class StringCache
27 {
28 public:
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 
45 private:
46  QHash<QByteArray, QString> m_values;
47 };
48 
49 #define FOLD_WIDTH 75
50 
51 using namespace KABC;
52 
53 static 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 
63 static 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 
72 VCardParser::VCardParser()
73  : d(0)
74 {
75 }
76 
77 VCardParser::~VCardParser()
78 {
79 }
80 
81 VCard::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 
237 QByteArray 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 }
QTextCodec::fromUnicode
QByteArray fromUnicode(const QString &str) const
QByteArray::clear
void clear()
QByteArray::split
QList< QByteArray > split(char sep) const
QByteArray::trimmed
QByteArray trimmed() const
QByteArray::reserve
void reserve(int size)
QByteArray
QByteArray::chop
void chop(int n)
QStringList::contains
bool contains(const QString &str, Qt::CaseSensitivity cs) const
QByteArray::isEmpty
bool isEmpty() const
QVariant::value
T value() const
QHash::constFind
const_iterator constFind(const Key &key) const
QList::takeAt
T takeAt(int i)
QByteArray::length
int length() const
QList::size
int size() const
QList::indexOf
int indexOf(const T &value, int from) const
QByteArray::indexOf
int indexOf(char ch, int from) const
QList::count
int count(const T &value) const
QList::append
void append(const T &value)
QString::fromUtf8
QString fromUtf8(const char *str, int size)
QString::insert
QString & insert(int position, QChar ch)
QHash::constEnd
const_iterator constEnd() const
QHash
QList::isEmpty
bool isEmpty() const
QByteArray::replace
QByteArray & replace(int pos, int len, const char *after)
QList::Iterator
typedef Iterator
QString
QList
Definition: contactgrouptool.h:30
QTextCodec
QByteArray::mid
QByteArray mid(int pos, int len) const
QStringList
QByteArray::append
QByteArray & append(char ch)
QList::end
iterator end()
QString::toLower
QString toLower() const
QHash::value
const T value(const Key &key) const
QByteArray::left
QByteArray left(int len) const
QLatin1String
QByteArray::fromBase64
QByteArray fromBase64(const QByteArray &base64)
QTextCodec::codecForName
QTextCodec * codecForName(const QByteArray &name)
QList::ConstIterator
typedef ConstIterator
QList::mid
QList< T > mid(int pos, int length) const
QString::fromLatin1
QString fromLatin1(const char *str, int size)
QVariant::isValid
bool isValid() const
QStringList::indexOf
int indexOf(const QRegExp &rx, int from) const
QList::prepend
void prepend(const T &value)
QByteArray::toBase64
QByteArray toBase64() const
QList::constEnd
const_iterator constEnd() const
QList::constBegin
const_iterator constBegin() const
QByteArray::size
int size() const
QList::begin
iterator begin()
QByteArray::endsWith
bool endsWith(const QByteArray &ba) const
QTextCodec::toUnicode
QString toUnicode(const QByteArray &a) const
QVariant
QString::toUtf8
QByteArray toUtf8() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:38:39 by doxygen 1.8.7 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 API Reference

Skip menu "kdepimlibs 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

Search



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