summaryrefslogtreecommitdiffabout
path: root/kabc/vcard21parser.cpp
Unidiff
Diffstat (limited to 'kabc/vcard21parser.cpp') (more/less context) (show whitespace changes)
-rw-r--r--kabc/vcard21parser.cpp606
1 files changed, 606 insertions, 0 deletions
diff --git a/kabc/vcard21parser.cpp b/kabc/vcard21parser.cpp
new file mode 100644
index 0000000..b02aac4
--- a/dev/null
+++ b/kabc/vcard21parser.cpp
@@ -0,0 +1,606 @@
1/*
2 This file is part of libkabc.
3 Copyright (c) 2001 Mark Westcott <mark@houseoffish.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., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/*
22Enhanced Version of the file for platform independent KDE tools.
23Copyright (c) 2004 Ulf Schenk
24
25$Id$
26*/
27
28#include <qmap.h>
29#include <qregexp.h>
30#include <kmdcodec.h>
31
32#include "vcard21parser.h"
33#include "vcardconverter.h"
34
35using namespace KABC;
36
37bool VCardLineX::isValid() const
38{
39 // Invalid: if it is "begin:vcard" or "end:vcard"
40 if ( name == VCARD_BEGIN_N || name == VCARD_END_N )
41 return false;
42
43 if ( name[0] == 'x' && name[1] == '-' ) // A custom x- line
44 return true;
45
46 // This is long but it makes it a bit faster (and saves me from using
47 // a tree which is probably the ideal situation, but a bit memory heavy)
48 switch( name[0] ) {
49 case 'a':
50 if ( name == VCARD_ADR && qualified &&
51 (qualifiers.contains(VCARD_ADR_DOM) ||
52 qualifiers.contains(VCARD_ADR_INTL) ||
53 qualifiers.contains(VCARD_ADR_POSTAL) ||
54 qualifiers.contains(VCARD_ADR_HOME) ||
55 qualifiers.contains(VCARD_ADR_WORK) ||
56 qualifiers.contains(VCARD_ADR_PREF)
57 ) )
58 return true;
59
60 if ( name == VCARD_AGENT )
61 return true;
62 break;
63
64 case 'b':
65 if ( name == VCARD_BDAY )
66 return true;
67 break;
68
69 case 'c':
70 if ( name == VCARD_CATEGORIES )
71 return true;
72 if ( name == VCARD_CLASS && qualified &&
73 (qualifiers.contains(VCARD_CLASS_PUBLIC) ||
74 qualifiers.contains(VCARD_CLASS_PRIVATE) ||
75 qualifiers.contains(VCARD_CLASS_CONFIDENTIAL)
76 ) )
77 return true;
78 break;
79
80 case 'e':
81 if ( name == VCARD_EMAIL && qualified &&
82 (qualifiers.contains(VCARD_EMAIL_INTERNET) ||
83 qualifiers.contains(VCARD_EMAIL_PREF) ||
84 qualifiers.contains(VCARD_EMAIL_X400)
85 ) )
86 return true;
87 break;
88
89 case 'f':
90 if ( name == VCARD_FN )
91 return true;
92 break;
93
94 case 'g':
95 if ( name == VCARD_GEO )
96 return true;
97 break;
98
99 case 'k':
100 if ( name == VCARD_KEY && qualified &&
101 (qualifiers.contains(VCARD_KEY_X509) ||
102 qualifiers.contains(VCARD_KEY_PGP)
103 ) )
104 return true;
105 break;
106
107 case 'l':
108 if ( name == VCARD_LABEL )
109 return true;
110 if ( name == VCARD_LOGO )
111 return true;
112 break;
113
114 case 'm':
115 if ( name == VCARD_MAILER )
116 return true;
117 break;
118
119 case 'n':
120 if ( name == VCARD_N )
121 return true;
122 if ( name == VCARD_NAME )
123 return true;
124 if ( name == VCARD_NICKNAME )
125 return true;
126 if ( name == VCARD_NOTE )
127 return true;
128 break;
129
130 case 'o':
131 if ( name == VCARD_ORG )
132 return true;
133 break;
134
135 case 'p':
136 if ( name == VCARD_PHOTO )
137 return true;
138 if ( name == VCARD_PROFILE )
139 return true;
140 if ( name == VCARD_PRODID )
141 return true;
142 break;
143
144 case 'r':
145 if ( name == VCARD_ROLE )
146 return true;
147 if ( name == VCARD_REV )
148 return true;
149 break;
150
151 case 's':
152 if ( name == VCARD_SOURCE )
153 return true;
154 if ( name == VCARD_SOUND )
155 return true;
156 break;
157
158 case 't':
159 if ( name == VCARD_TEL && qualified &&
160 (qualifiers.contains(VCARD_TEL_HOME) ||
161 qualifiers.contains(VCARD_TEL_WORK) ||
162 qualifiers.contains(VCARD_TEL_PREF) ||
163 qualifiers.contains(VCARD_TEL_VOICE) ||
164 qualifiers.contains(VCARD_TEL_FAX) ||
165 qualifiers.contains(VCARD_TEL_MSG) ||
166 qualifiers.contains(VCARD_TEL_CELL) ||
167 qualifiers.contains(VCARD_TEL_PAGER) ||
168 qualifiers.contains(VCARD_TEL_BBS) ||
169 qualifiers.contains(VCARD_TEL_MODEM) ||
170 qualifiers.contains(VCARD_TEL_CAR) ||
171 qualifiers.contains(VCARD_TEL_ISDN) ||
172 qualifiers.contains(VCARD_TEL_VIDEO) ||
173 qualifiers.contains(VCARD_TEL_PCS)
174 ) )
175 return true;
176 if ( name == VCARD_TZ )
177 return true;
178 if ( name == VCARD_TITLE )
179 return true;
180 break;
181
182 case 'u':
183 if ( name == VCARD_URL )
184 return true;
185 if ( name == VCARD_UID )
186 return true;
187 break;
188
189 case 'v':
190 if ( name == VCARD_VERSION )
191 return true;
192 break;
193 default:
194 break;
195 }
196
197 return false;
198}
199
200
201VCard21Parser::VCard21Parser()
202{
203}
204
205VCard21Parser::~VCard21Parser()
206{
207}
208
209void VCard21Parser::readFromString(KABC::AddressBook *addressbook, const QString &data)
210{
211 KABC::Addressee mAddressee = readFromString(data);
212 addressbook->insertAddressee(mAddressee);
213}
214
215KABC::Addressee VCard21Parser::readFromString( const QString &data)
216{
217 KABC::Addressee addressee;
218 VCard21ParserImpl *mVCard = VCard21ParserImpl::parseVCard(data);
219 QString tmpStr;
220
221 // Check if parsing failed
222 if (mVCard == 0)
223 {
224 kdDebug() << "Parsing failed" << endl;
225 return addressee;
226 }
227 //set the addressees name and formated name
228 QStringList tmpList = mVCard->getValues(VCARD_N);
229 QString formattedName = "";
230 if (tmpList.count() > 0)
231 addressee.setFamilyName(tmpList[0]);
232 if (tmpList.count() > 1)
233 addressee.setGivenName(tmpList[1]);
234 if (tmpList.count() > 2)
235 addressee.setAdditionalName(tmpList[2]);
236 if (tmpList.count() > 3)
237 addressee.setPrefix(tmpList[3]);
238 if (tmpList.count() > 4)
239 addressee.setSuffix(tmpList[4]);
240
241 tmpStr = (mVCard->getValue(VCARD_FN));
242 if (!tmpStr.isEmpty())
243 addressee.setFormattedName(tmpStr);
244
245 //set the addressee's nick name
246 tmpStr = mVCard->getValue(VCARD_NICKNAME);
247 addressee.setNickName(tmpStr);
248 //set the addressee's organisation
249 tmpStr = mVCard->getValue(VCARD_ORG);
250 addressee.setOrganization(tmpStr);
251 //set the addressee's title
252 tmpStr = mVCard->getValue(VCARD_TITLE);
253 addressee.setTitle(tmpStr);
254 //set the addressee's email - we can only deal with two. The preferenced one and one other.
255 tmpStr = mVCard->getValue(VCARD_EMAIL, VCARD_EMAIL_INTERNET);
256 addressee.insertEmail(tmpStr, false);
257 tmpStr = mVCard->getValue(VCARD_EMAIL,VCARD_EMAIL_PREF);
258 addressee.insertEmail(tmpStr, true);
259 //set the addressee's url
260 tmpStr = mVCard->getValue(VCARD_URL);
261 if (tmpStr.isEmpty()) tmpStr = mVCard->getValue(VCARD_URL, VCARD_ADR_WORK);
262 if (tmpStr.isEmpty()) tmpStr = mVCard->getValue(VCARD_URL, VCARD_ADR_HOME);
263 if (!tmpStr.isEmpty()) {
264 addressee.setUrl(KURL(tmpStr));
265 }
266
267 //set the addressee's birthday
268 tmpStr = mVCard->getValue(VCARD_BDAY);
269 addressee.setBirthday(VCardStringToDate(tmpStr));
270
271 //set the addressee's phone numbers
272 for ( QValueListIterator<VCardLineX> i = mVCard->_vcdata->begin();i != mVCard->_vcdata->end(); ++i ) {
273 if ( (*i).name == VCARD_TEL ) {
274 int type = 0;
275 if ( (*i).qualified ) {
276 if ( (*i).qualifiers.contains( VCARD_TEL_HOME ) )
277 type |= PhoneNumber::Home;
278 if ( (*i).qualifiers.contains( VCARD_TEL_WORK ) )
279 type |= PhoneNumber::Work;
280 if ( (*i).qualifiers.contains( VCARD_TEL_PREF ) )
281 type |= PhoneNumber::Pref;
282 // if ( (*i).qualifiers.contains( VCARD_TEL_VOICE ) )
283 // type |= PhoneNumber::Voice;
284 if ( (*i).qualifiers.contains( VCARD_TEL_FAX ) )
285 type |= PhoneNumber::Fax;
286 if ( (*i).qualifiers.contains( VCARD_TEL_MSG ) )
287 type |= PhoneNumber::Msg;
288 if ( (*i).qualifiers.contains( VCARD_TEL_CELL ) )
289 type |= PhoneNumber::Cell;
290 if ( (*i).qualifiers.contains( VCARD_TEL_PAGER ) )
291 type |= PhoneNumber::Pager;
292 if ( (*i).qualifiers.contains( VCARD_TEL_BBS ) )
293 type |= PhoneNumber::Bbs;
294 if ( (*i).qualifiers.contains( VCARD_TEL_MODEM ) )
295 type |= PhoneNumber::Modem;
296 if ( (*i).qualifiers.contains( VCARD_TEL_CAR ) )
297 type |= PhoneNumber::Car;
298 if ( (*i).qualifiers.contains( VCARD_TEL_ISDN ) )
299 type |= PhoneNumber::Isdn;
300 if ( (*i).qualifiers.contains( VCARD_TEL_VIDEO ) )
301 type |= PhoneNumber::Video;
302 if ( (*i).qualifiers.contains( VCARD_TEL_PCS ) )
303 type |= PhoneNumber::Pcs;
304 }
305 addressee.insertPhoneNumber( PhoneNumber( (*i).parameters[ 0 ], type ) );
306 }
307 }
308
309 //set the addressee's addresses
310 for ( QValueListIterator<VCardLineX> i = mVCard->_vcdata->begin();i != mVCard->_vcdata->end(); ++i ) {
311 if ( (*i).name == VCARD_ADR ) {
312 int type = 0;
313 if ( (*i).qualified ) {
314 if ( (*i).qualifiers.contains( VCARD_ADR_DOM ) )
315 type |= Address::Dom;
316 if ( (*i).qualifiers.contains( VCARD_ADR_INTL ) )
317 type |= Address::Intl;
318 if ( (*i).qualifiers.contains( VCARD_ADR_POSTAL ) )
319 type |= Address::Postal;
320 if ( (*i).qualifiers.contains( VCARD_ADR_PARCEL ) )
321 type |= Address::Parcel;
322 if ( (*i).qualifiers.contains( VCARD_ADR_HOME ) )
323 type |= Address::Home;
324 if ( (*i).qualifiers.contains( VCARD_ADR_WORK ) )
325 type |= Address::Work;
326 if ( (*i).qualifiers.contains( VCARD_ADR_PREF ) )
327 type |= Address::Pref;
328 }
329 addressee.insertAddress( readAddressFromQStringList( (*i).parameters, type ) );
330 }
331 }
332
333 //set the addressee's delivery label
334 tmpStr = mVCard->getValue(VCARD_LABEL);
335 if (!tmpStr.isEmpty()) {
336qDebug("VCard21Parser::readFromString please verify if replace is correct");
337//US tmpStr.replace("\r\n","\n");
338 tmpStr.replace( QRegExp("\r\n"), "\n" );
339 Address tmpAddress;
340 tmpAddress.setLabel(tmpStr);
341 addressee.insertAddress(tmpAddress);
342 }
343
344 //set the addressee's notes
345 tmpStr = mVCard->getValue(VCARD_NOTE);
346qDebug("VCard21Parser::readFromString please verify if correct");
347//US tmpStr.replace("\r\n","\n");
348 tmpStr.replace( QRegExp("\r\n"), "\n" );
349 addressee.setNote(tmpStr);
350
351 //set the addressee's timezone
352 tmpStr = mVCard->getValue(VCARD_TZ);
353 TimeZone tmpZone(tmpStr.toInt());
354 addressee.setTimeZone(tmpZone);
355
356 //set the addressee's geographical position
357 tmpList = mVCard->getValues(VCARD_GEO);
358 if (tmpList.count()==2)
359 {
360 tmpStr = tmpList[0];
361 float glat = tmpStr.toFloat();
362 tmpStr = tmpList[1];
363 float glong = tmpStr.toFloat();
364 Geo tmpGeo(glat,glong);
365 addressee.setGeo(tmpGeo);
366 }
367
368 //set the last revision date
369 tmpStr = mVCard->getValue(VCARD_REV);
370 addressee.setRevision(VCardStringToDate(tmpStr));
371
372 //set the role of the addressee
373 tmpStr = mVCard->getValue(VCARD_ROLE);
374 addressee.setRole(tmpStr);
375
376 return addressee;
377}
378
379
380
381KABC::Address VCard21Parser::readAddressFromQStringList ( const QStringList &data, const int type )
382{
383 KABC::Address mAddress;
384 mAddress.setType( type );
385
386 if ( data.count() > 0 )
387 mAddress.setPostOfficeBox( data[0] );
388 if ( data.count() > 1 )
389 mAddress.setExtended( data[1] );
390 if ( data.count() > 2 )
391 mAddress.setStreet( data[2] );
392 if ( data.count() > 3 )
393 mAddress.setLocality( data[3] );
394 if ( data.count() > 4 )
395 mAddress.setRegion( data[4] );
396 if ( data.count() > 5 )
397 mAddress.setPostalCode( data[5] );
398 if ( data.count() > 6 )
399 mAddress.setCountry( data[6] );
400
401 return mAddress;
402}
403
404
405VCard21ParserImpl *VCard21ParserImpl::parseVCard( const QString& vc, int *err )
406{
407 int _err = 0;
408 int _state = VC_STATE_BEGIN;
409
410 QValueList<VCardLineX> *_vcdata;
411 QValueList<QString> lines;
412
413 _vcdata = new QValueList<VCardLineX>;
414
415 lines = QStringList::split( QRegExp( "[\x0d\x0a]" ), vc );
416
417 // for each line in the vCard
418 for ( QStringList::Iterator j = lines.begin(); j != lines.end(); ++j ) {
419 VCardLineX _vcl;
420
421 // take spaces off the end - ugly but necessary hack
422 for ( int g = (*j).length()-1; g > 0 && (*j)[g].isSpace(); --g )
423 (*j)[g] = 0;
424
425 // first token:
426 // verify state, update if necessary
427 if ( _state & VC_STATE_BEGIN) {
428 if ( !qstricmp( (*j).latin1(), VCARD_BEGIN ) ) {
429 _state = VC_STATE_BODY;
430 continue;
431 } else {
432 _err = VC_ERR_NO_BEGIN;
433 break;
434 }
435 } else if ( _state & VC_STATE_BODY ) {
436 if ( !qstricmp( (*j).latin1(), VCARD_END ) ) {
437 _state |= VC_STATE_END;
438 break;
439 }
440
441 // split into two tokens
442 int colon = (*j).find( ':' );
443 if ( colon < 0 ) {
444 _err = VC_ERR_INVALID_LINE;
445 break;
446 }
447
448 QString key = (*j).left( colon );
449 QString value = (*j).mid( colon + 1 );
450
451 // check for qualifiers and
452 // set name, qualified, qualifier(s)
453 QStringList keyTokens = QStringList::split( ';', key );
454 bool qp = false, first_pass = true;
455 bool b64 = false;
456
457 if ( keyTokens.count() > 0 ) {
458 _vcl.qualified = false;
459 _vcl.name = keyTokens[ 0 ].lower();
460
461 for ( QStringList::Iterator z = keyTokens.begin(); z != keyTokens.end(); ++z ) {
462 QString zz = (*z).lower();
463 if ( zz == VCARD_QUOTED_PRINTABLE || zz == VCARD_ENCODING_QUOTED_PRINTABLE ) {
464 qp = true;
465 } else if ( zz == VCARD_BASE64 ) {
466 b64 = true;
467 } else if ( !first_pass ) {
468 _vcl.qualified = true;
469 _vcl.qualifiers.append( zz );
470 }
471 first_pass = false;
472 }
473 } else {
474 _err = VC_ERR_INVALID_LINE;
475 }
476
477 if ( _err != 0 )
478 break;
479
480 if ( _vcl.name == VCARD_VERSION )
481 _state |= VC_STATE_HAVE_VERSION;
482
483 if ( _vcl.name == VCARD_N || _vcl.name == VCARD_FN )
484 _state |= VC_STATE_HAVE_N;
485
486 // second token:
487 // split into tokens by ;
488 // add to parameters vector
489 if ( b64 ) {
490 if ( value.at( value.length() - 1 ) != '=' )
491 do {
492 value += *( ++j );
493 } while ( (*j).at( (*j).length() - 1 ) != '=' );
494 } else {
495 if ( qp ) { // join any split lines
496 while ( value.at( value.length() - 1 ) == '=' ) {
497 value.remove( value.length() - 1, 1 );
498 value.append(*( ++j ));
499 }
500 }
501 _vcl.parameters = QStringList::split( ';', value, true );
502 if ( qp ) { // decode the quoted printable
503 for ( QStringList::Iterator z = _vcl.parameters.begin(); z != _vcl.parameters.end(); ++z )
504 *z = KCodecs::quotedPrintableDecode( (*z).latin1() );
505 }
506 }
507 } else {
508 _err = VC_ERR_INTERNAL;
509 break;
510 }
511
512 // validate VCardLineX
513 if ( !_vcl.isValid() ) {
514 _err = VC_ERR_INVALID_LINE;
515 break;
516 }
517
518 // add to vector
519 _vcdata->append( _vcl );
520 }
521
522 // errors to check at the last minute (exit state related)
523 if ( _err == 0 ) {
524 if ( !( _state & VC_STATE_END ) ) // we have to have an end!!
525 _err = VC_ERR_NO_END;
526
527 if ( !( _state & VC_STATE_HAVE_N ) || // we have to have the mandatories!
528 !( _state & VC_STATE_HAVE_VERSION ) )
529 _err = VC_ERR_MISSING_MANDATORY;
530 }
531
532 // set the error message if we can, and only return an object
533 // if the vCard was valid.
534 if ( err )
535 *err = _err;
536
537 if ( _err != 0 ) {
538 delete _vcdata;
539 return 0;
540 }
541
542 return new VCard21ParserImpl( _vcdata );
543}
544
545VCard21ParserImpl::VCard21ParserImpl(QValueList<VCardLineX> *_vcd) : _vcdata(_vcd)
546{
547}
548
549
550QString VCard21ParserImpl::getValue(const QString& name, const QString& qualifier)
551{
552 QString failed;
553 const QString lowname = name.lower();
554 const QString lowqualifier = qualifier.lower();
555
556 for (QValueListIterator<VCardLineX> i = _vcdata->begin();i != _vcdata->end();++i) {
557 if ((*i).name == lowname && (*i).qualified && (*i).qualifiers.contains(lowqualifier)) {
558 if ((*i).parameters.count() > 0)
559 return (*i).parameters[0];
560 else return failed;
561 }
562 }
563 return failed;
564}
565
566
567QString VCard21ParserImpl::getValue(const QString& name)
568{
569 QString failed;
570 const QString lowname = name.lower();
571
572 for (QValueListIterator<VCardLineX> i = _vcdata->begin();i != _vcdata->end();++i) {
573 if ((*i).name == lowname && !(*i).qualified) {
574 if ((*i).parameters.count() > 0)
575 return (*i).parameters[0];
576 else return failed;
577 }
578 }
579 return failed;
580}
581
582
583QStringList VCard21ParserImpl::getValues(const QString& name)
584{
585 const QString lowname = name.lower();
586 for (QValueListIterator<VCardLineX> i = _vcdata->begin();i != _vcdata->end();++i) {
587 if ((*i).name == lowname && !(*i).qualified)
588 return (*i).parameters;
589 }
590 // failed.
591 return QStringList();
592}
593
594QStringList VCard21ParserImpl::getValues(const QString& name, const QString& qualifier)
595{
596 const QString lowname = name.lower();
597 const QString lowqualifier = qualifier.lower();
598 for (QValueListIterator<VCardLineX> i = _vcdata->begin();i != _vcdata->end();++i) {
599 if ((*i).name == lowname && (*i).qualified && (*i).qualifiers.contains(lowqualifier))
600 return (*i).parameters;
601 }
602 // failed.
603 return QStringList();
604}
605
606