vcard21parser.cpp
00001 /* 00002 This file is part of libkabc. 00003 Copyright (c) 2001 Mark Westcott <mark@houseoffish.org> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License as published by the Free Software Foundation; either 00008 version 2 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Library General Public License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to 00017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include <qmap.h> 00022 #include <qregexp.h> 00023 #include <kmdcodec.h> 00024 00025 #include "vcard21parser.h" 00026 #include "vcardconverter.h" 00027 00028 using namespace KABC; 00029 00030 bool VCardLineX::isValid() const 00031 { 00032 // Invalid: if it is "begin:vcard" or "end:vcard" 00033 if ( name == VCARD_BEGIN_N || name == VCARD_END_N ) 00034 return false; 00035 00036 if ( name[0] == 'x' && name[1] == '-' ) // A custom x- line 00037 return true; 00038 00039 // This is long but it makes it a bit faster (and saves me from using 00040 // a tree which is probably the ideal situation, but a bit memory heavy) 00041 switch( name[0] ) { 00042 case 'a': 00043 if ( name == VCARD_ADR && qualified && 00044 (qualifiers.contains(VCARD_ADR_DOM) || 00045 qualifiers.contains(VCARD_ADR_INTL) || 00046 qualifiers.contains(VCARD_ADR_POSTAL) || 00047 qualifiers.contains(VCARD_ADR_HOME) || 00048 qualifiers.contains(VCARD_ADR_WORK) || 00049 qualifiers.contains(VCARD_ADR_PREF) 00050 ) ) 00051 return true; 00052 00053 if ( name == VCARD_AGENT ) 00054 return true; 00055 break; 00056 00057 case 'b': 00058 if ( name == VCARD_BDAY ) 00059 return true; 00060 break; 00061 00062 case 'c': 00063 if ( name == VCARD_CATEGORIES ) 00064 return true; 00065 if ( name == VCARD_CLASS && qualified && 00066 (qualifiers.contains(VCARD_CLASS_PUBLIC) || 00067 qualifiers.contains(VCARD_CLASS_PRIVATE) || 00068 qualifiers.contains(VCARD_CLASS_CONFIDENTIAL) 00069 ) ) 00070 return true; 00071 break; 00072 00073 case 'e': 00074 if ( name == VCARD_EMAIL && qualified && 00075 (qualifiers.contains(VCARD_EMAIL_INTERNET) || 00076 qualifiers.contains(VCARD_EMAIL_PREF) || 00077 qualifiers.contains(VCARD_EMAIL_X400) 00078 ) ) 00079 return true; 00080 break; 00081 00082 case 'f': 00083 if ( name == VCARD_FN ) 00084 return true; 00085 break; 00086 00087 case 'g': 00088 if ( name == VCARD_GEO ) 00089 return true; 00090 break; 00091 00092 case 'k': 00093 if ( name == VCARD_KEY && qualified && 00094 (qualifiers.contains(VCARD_KEY_X509) || 00095 qualifiers.contains(VCARD_KEY_PGP) 00096 ) ) 00097 return true; 00098 break; 00099 00100 case 'l': 00101 if ( name == VCARD_LABEL ) 00102 return true; 00103 if ( name == VCARD_LOGO ) 00104 return true; 00105 break; 00106 00107 case 'm': 00108 if ( name == VCARD_MAILER ) 00109 return true; 00110 break; 00111 00112 case 'n': 00113 if ( name == VCARD_N ) 00114 return true; 00115 if ( name == VCARD_NAME ) 00116 return true; 00117 if ( name == VCARD_NICKNAME ) 00118 return true; 00119 if ( name == VCARD_NOTE ) 00120 return true; 00121 break; 00122 00123 case 'o': 00124 if ( name == VCARD_ORG ) 00125 return true; 00126 break; 00127 00128 case 'p': 00129 if ( name == VCARD_PHOTO ) 00130 return true; 00131 if ( name == VCARD_PROFILE ) 00132 return true; 00133 if ( name == VCARD_PRODID ) 00134 return true; 00135 break; 00136 00137 case 'r': 00138 if ( name == VCARD_ROLE ) 00139 return true; 00140 if ( name == VCARD_REV ) 00141 return true; 00142 break; 00143 00144 case 's': 00145 if ( name == VCARD_SOURCE ) 00146 return true; 00147 if ( name == VCARD_SOUND ) 00148 return true; 00149 break; 00150 00151 case 't': 00152 if ( name == VCARD_TEL && qualified && 00153 (qualifiers.contains(VCARD_TEL_HOME) || 00154 qualifiers.contains(VCARD_TEL_WORK) || 00155 qualifiers.contains(VCARD_TEL_PREF) || 00156 qualifiers.contains(VCARD_TEL_VOICE) || 00157 qualifiers.contains(VCARD_TEL_FAX) || 00158 qualifiers.contains(VCARD_TEL_MSG) || 00159 qualifiers.contains(VCARD_TEL_CELL) || 00160 qualifiers.contains(VCARD_TEL_PAGER) || 00161 qualifiers.contains(VCARD_TEL_BBS) || 00162 qualifiers.contains(VCARD_TEL_MODEM) || 00163 qualifiers.contains(VCARD_TEL_CAR) || 00164 qualifiers.contains(VCARD_TEL_ISDN) || 00165 qualifiers.contains(VCARD_TEL_VIDEO) || 00166 qualifiers.contains(VCARD_TEL_PCS) 00167 ) ) 00168 return true; 00169 if ( name == VCARD_TZ ) 00170 return true; 00171 if ( name == VCARD_TITLE ) 00172 return true; 00173 break; 00174 00175 case 'u': 00176 if ( name == VCARD_URL ) 00177 return true; 00178 if ( name == VCARD_UID ) 00179 return true; 00180 break; 00181 00182 case 'v': 00183 if ( name == VCARD_VERSION ) 00184 return true; 00185 break; 00186 default: 00187 break; 00188 } 00189 00190 return false; 00191 } 00192 00193 00194 VCard21Parser::VCard21Parser() 00195 { 00196 } 00197 00198 VCard21Parser::~VCard21Parser() 00199 { 00200 } 00201 00202 void VCard21Parser::readFromString(KABC::AddressBook *addressbook, const QString &data) 00203 { 00204 KABC::Addressee mAddressee = readFromString(data); 00205 addressbook->insertAddressee(mAddressee); 00206 } 00207 00208 KABC::Addressee VCard21Parser::readFromString( const QString &data) 00209 { 00210 KABC::Addressee addressee; 00211 VCard21ParserImpl *vCard = VCard21ParserImpl::parseVCard(data); 00212 QString tmpStr; 00213 00214 // Check if parsing failed 00215 if (vCard == 0) 00216 { 00217 kdDebug() << "Parsing failed" << endl; 00218 return addressee; 00219 } 00220 //set the addressees name and formated name 00221 QStringList tmpList = vCard->getValues(VCARD_N); 00222 QString formattedName = ""; 00223 if (tmpList.count() > 0) 00224 addressee.setFamilyName(tmpList[0]); 00225 if (tmpList.count() > 1) 00226 addressee.setGivenName(tmpList[1]); 00227 if (tmpList.count() > 2) 00228 addressee.setAdditionalName(tmpList[2]); 00229 if (tmpList.count() > 3) 00230 addressee.setPrefix(tmpList[3]); 00231 if (tmpList.count() > 4) 00232 addressee.setSuffix(tmpList[4]); 00233 00234 tmpStr = (vCard->getValue(VCARD_FN)); 00235 if (!tmpStr.isEmpty()) 00236 addressee.setFormattedName(tmpStr); 00237 00238 //set the addressee's nick name 00239 tmpStr = vCard->getValue(VCARD_NICKNAME); 00240 addressee.setNickName(tmpStr); 00241 //set the addressee's organization 00242 tmpStr = vCard->getValue(VCARD_ORG); 00243 addressee.setOrganization(tmpStr); 00244 //set the addressee's title 00245 tmpStr = vCard->getValue(VCARD_TITLE); 00246 addressee.setTitle(tmpStr); 00247 //set the addressee's email - we can only deal with two. The preferenced one and one other. 00248 tmpStr = vCard->getValue(VCARD_EMAIL, VCARD_EMAIL_INTERNET); 00249 addressee.insertEmail(tmpStr, false); 00250 tmpStr = vCard->getValue(VCARD_EMAIL,VCARD_EMAIL_PREF); 00251 addressee.insertEmail(tmpStr, true); 00252 //set the addressee's url 00253 tmpStr = vCard->getValue(VCARD_URL); 00254 if (tmpStr.isEmpty()) tmpStr = vCard->getValue(VCARD_URL, VCARD_ADR_WORK); 00255 if (tmpStr.isEmpty()) tmpStr = vCard->getValue(VCARD_URL, VCARD_ADR_HOME); 00256 if (!tmpStr.isEmpty()) { 00257 addressee.setUrl(KURL(tmpStr)); 00258 } 00259 00260 //set the addressee's birthday 00261 tmpStr = vCard->getValue(VCARD_BDAY); 00262 addressee.setBirthday(VCardStringToDate(tmpStr)); 00263 00264 //set the addressee's phone numbers 00265 for ( QValueListIterator<VCardLineX> i = vCard->_vcdata->begin();i != vCard->_vcdata->end(); ++i ) { 00266 if ( (*i).name == VCARD_TEL ) { 00267 int type = 0; 00268 if ( (*i).qualified ) { 00269 if ( (*i).qualifiers.contains( VCARD_TEL_HOME ) ) 00270 type |= PhoneNumber::Home; 00271 if ( (*i).qualifiers.contains( VCARD_TEL_WORK ) ) 00272 type |= PhoneNumber::Work; 00273 if ( (*i).qualifiers.contains( VCARD_TEL_PREF ) ) 00274 type |= PhoneNumber::Pref; 00275 // if ( (*i).qualifiers.contains( VCARD_TEL_VOICE ) ) 00276 // type |= PhoneNumber::Voice; 00277 if ( (*i).qualifiers.contains( VCARD_TEL_FAX ) ) 00278 type |= PhoneNumber::Fax; 00279 if ( (*i).qualifiers.contains( VCARD_TEL_MSG ) ) 00280 type |= PhoneNumber::Msg; 00281 if ( (*i).qualifiers.contains( VCARD_TEL_CELL ) ) 00282 type |= PhoneNumber::Cell; 00283 if ( (*i).qualifiers.contains( VCARD_TEL_PAGER ) ) 00284 type |= PhoneNumber::Pager; 00285 if ( (*i).qualifiers.contains( VCARD_TEL_BBS ) ) 00286 type |= PhoneNumber::Bbs; 00287 if ( (*i).qualifiers.contains( VCARD_TEL_MODEM ) ) 00288 type |= PhoneNumber::Modem; 00289 if ( (*i).qualifiers.contains( VCARD_TEL_CAR ) ) 00290 type |= PhoneNumber::Car; 00291 if ( (*i).qualifiers.contains( VCARD_TEL_ISDN ) ) 00292 type |= PhoneNumber::Isdn; 00293 if ( (*i).qualifiers.contains( VCARD_TEL_VIDEO ) ) 00294 type |= PhoneNumber::Video; 00295 if ( (*i).qualifiers.contains( VCARD_TEL_PCS ) ) 00296 type |= PhoneNumber::Pcs; 00297 } 00298 addressee.insertPhoneNumber( PhoneNumber( (*i).parameters[ 0 ], type ) ); 00299 } 00300 } 00301 00302 //set the addressee's addresses 00303 for ( QValueListIterator<VCardLineX> i = vCard->_vcdata->begin();i != vCard->_vcdata->end(); ++i ) { 00304 if ( (*i).name == VCARD_ADR ) { 00305 int type = 0; 00306 if ( (*i).qualified ) { 00307 if ( (*i).qualifiers.contains( VCARD_ADR_DOM ) ) 00308 type |= Address::Dom; 00309 if ( (*i).qualifiers.contains( VCARD_ADR_INTL ) ) 00310 type |= Address::Intl; 00311 if ( (*i).qualifiers.contains( VCARD_ADR_POSTAL ) ) 00312 type |= Address::Postal; 00313 if ( (*i).qualifiers.contains( VCARD_ADR_PARCEL ) ) 00314 type |= Address::Parcel; 00315 if ( (*i).qualifiers.contains( VCARD_ADR_HOME ) ) 00316 type |= Address::Home; 00317 if ( (*i).qualifiers.contains( VCARD_ADR_WORK ) ) 00318 type |= Address::Work; 00319 if ( (*i).qualifiers.contains( VCARD_ADR_PREF ) ) 00320 type |= Address::Pref; 00321 } 00322 addressee.insertAddress( readAddressFromQStringList( (*i).parameters, type ) ); 00323 } 00324 } 00325 00326 //set the addressee's delivery label 00327 tmpStr = vCard->getValue(VCARD_LABEL); 00328 if (!tmpStr.isEmpty()) { 00329 tmpStr.replace("\r\n","\n"); 00330 Address tmpAddress; 00331 tmpAddress.setLabel(tmpStr); 00332 addressee.insertAddress(tmpAddress); 00333 } 00334 00335 //set the addressee's notes 00336 tmpStr = vCard->getValue(VCARD_NOTE); 00337 tmpStr.replace("\r\n","\n"); 00338 addressee.setNote(tmpStr); 00339 00340 //set the addressee's timezone 00341 tmpStr = vCard->getValue(VCARD_TZ); 00342 TimeZone tmpZone(tmpStr.toInt()); 00343 addressee.setTimeZone(tmpZone); 00344 00345 //set the addressee's geographical position 00346 tmpList = vCard->getValues(VCARD_GEO); 00347 if (tmpList.count()==2) 00348 { 00349 tmpStr = tmpList[0]; 00350 float glat = tmpStr.toFloat(); 00351 tmpStr = tmpList[1]; 00352 float glong = tmpStr.toFloat(); 00353 Geo tmpGeo(glat,glong); 00354 addressee.setGeo(tmpGeo); 00355 } 00356 00357 //set the last revision date 00358 tmpStr = vCard->getValue(VCARD_REV); 00359 addressee.setRevision(VCardStringToDate(tmpStr)); 00360 00361 //set the role of the addressee 00362 tmpStr = vCard->getValue(VCARD_ROLE); 00363 addressee.setRole(tmpStr); 00364 00365 delete vCard; 00366 00367 return addressee; 00368 } 00369 00370 00371 00372 KABC::Address VCard21Parser::readAddressFromQStringList ( const QStringList &data, const int type ) 00373 { 00374 KABC::Address mAddress; 00375 mAddress.setType( type ); 00376 00377 if ( data.count() > 0 ) 00378 mAddress.setPostOfficeBox( data[0] ); 00379 if ( data.count() > 1 ) 00380 mAddress.setExtended( data[1] ); 00381 if ( data.count() > 2 ) 00382 mAddress.setStreet( data[2] ); 00383 if ( data.count() > 3 ) 00384 mAddress.setLocality( data[3] ); 00385 if ( data.count() > 4 ) 00386 mAddress.setRegion( data[4] ); 00387 if ( data.count() > 5 ) 00388 mAddress.setPostalCode( data[5] ); 00389 if ( data.count() > 6 ) 00390 mAddress.setCountry( data[6] ); 00391 00392 return mAddress; 00393 } 00394 00395 00396 VCard21ParserImpl *VCard21ParserImpl::parseVCard( const QString& vc, int *err ) 00397 { 00398 int _err = 0; 00399 int _state = VC_STATE_BEGIN; 00400 00401 QValueList<VCardLineX> *vcdata; 00402 QValueList<QString> lines; 00403 00404 vcdata = new QValueList<VCardLineX>; 00405 00406 lines = QStringList::split( QRegExp( "[\x0d\x0a]" ), vc ); 00407 00408 // for each line in the vCard 00409 for ( QStringList::Iterator j = lines.begin(); j != lines.end(); ++j ) { 00410 VCardLineX _vcl; 00411 00412 // take spaces off the end - ugly but necessary hack 00413 for ( int g = (*j).length()-1; g > 0 && (*j)[g].isSpace(); --g ) 00414 (*j)[g] = 0; 00415 00416 // first token: 00417 // verify state, update if necessary 00418 if ( _state & VC_STATE_BEGIN) { 00419 if ( !qstricmp( (*j).latin1(), VCARD_BEGIN ) ) { 00420 _state = VC_STATE_BODY; 00421 continue; 00422 } else { 00423 _err = VC_ERR_NO_BEGIN; 00424 break; 00425 } 00426 } else if ( _state & VC_STATE_BODY ) { 00427 if ( !qstricmp( (*j).latin1(), VCARD_END ) ) { 00428 _state |= VC_STATE_END; 00429 break; 00430 } 00431 00432 // split into two tokens 00433 int colon = (*j).find( ':' ); 00434 if ( colon < 0 ) { 00435 _err = VC_ERR_INVALID_LINE; 00436 break; 00437 } 00438 00439 QString key = (*j).left( colon ); 00440 QString value = (*j).mid( colon + 1 ); 00441 00442 // check for qualifiers and 00443 // set name, qualified, qualifier(s) 00444 QStringList keyTokens = QStringList::split( ';', key ); 00445 bool qp = false, first_pass = true; 00446 bool b64 = false; 00447 00448 if ( keyTokens.count() > 0 ) { 00449 _vcl.qualified = false; 00450 _vcl.name = keyTokens[ 0 ].lower(); 00451 00452 for ( QStringList::Iterator z = keyTokens.begin(); z != keyTokens.end(); ++z ) { 00453 QString zz = (*z).lower(); 00454 if ( zz == VCARD_QUOTED_PRINTABLE || zz == VCARD_ENCODING_QUOTED_PRINTABLE ) { 00455 qp = true; 00456 } else if ( zz == VCARD_BASE64 ) { 00457 b64 = true; 00458 } else if ( !first_pass ) { 00459 _vcl.qualified = true; 00460 _vcl.qualifiers.append( zz ); 00461 } 00462 first_pass = false; 00463 } 00464 } else { 00465 _err = VC_ERR_INVALID_LINE; 00466 } 00467 00468 if ( _err != 0 ) 00469 break; 00470 00471 if ( _vcl.name == VCARD_VERSION ) 00472 _state |= VC_STATE_HAVE_VERSION; 00473 00474 if ( _vcl.name == VCARD_N || _vcl.name == VCARD_FN ) 00475 _state |= VC_STATE_HAVE_N; 00476 00477 // second token: 00478 // split into tokens by ; 00479 // add to parameters vector 00480 if ( b64 ) { 00481 if ( value[ value.length() - 1 ] != '=' ) 00482 do { 00483 value += *( ++j ); 00484 } while ( (*j)[ (*j).length() - 1 ] != '=' ); 00485 } else { 00486 if ( qp ) { // join any split lines 00487 while ( value[ value.length() - 1 ] == '=' ) { 00488 value.remove( value.length() - 1, 1 ); 00489 value.append(*( ++j )); 00490 } 00491 } 00492 _vcl.parameters = QStringList::split( ';', value, true ); 00493 if ( qp ) { // decode the quoted printable 00494 for ( QStringList::Iterator z = _vcl.parameters.begin(); z != _vcl.parameters.end(); ++z ) 00495 *z = KCodecs::quotedPrintableDecode( (*z).latin1() ); 00496 } 00497 } 00498 } else { 00499 _err = VC_ERR_INTERNAL; 00500 break; 00501 } 00502 00503 // validate VCardLineX 00504 if ( !_vcl.isValid() ) { 00505 _err = VC_ERR_INVALID_LINE; 00506 break; 00507 } 00508 00509 // add to vector 00510 vcdata->append( _vcl ); 00511 } 00512 00513 // errors to check at the last minute (exit state related) 00514 if ( _err == 0 ) { 00515 if ( !( _state & VC_STATE_END ) ) // we have to have an end!! 00516 _err = VC_ERR_NO_END; 00517 00518 if ( !( _state & VC_STATE_HAVE_N ) || // we have to have the mandatories! 00519 !( _state & VC_STATE_HAVE_VERSION ) ) 00520 _err = VC_ERR_MISSING_MANDATORY; 00521 } 00522 00523 // set the error message if we can, and only return an object 00524 // if the vCard was valid. 00525 if ( err ) 00526 *err = _err; 00527 00528 if ( _err != 0 ) { 00529 delete vcdata; 00530 return 0; 00531 } 00532 00533 return new VCard21ParserImpl( vcdata ); 00534 } 00535 00536 VCard21ParserImpl::VCard21ParserImpl() 00537 : _vcdata( 0 ) 00538 { 00539 } 00540 00541 VCard21ParserImpl::VCard21ParserImpl(QValueList<VCardLineX> *_vcd) 00542 : _vcdata(_vcd) 00543 { 00544 } 00545 00546 VCard21ParserImpl::~VCard21ParserImpl() 00547 { 00548 delete _vcdata; 00549 _vcdata = 0; 00550 } 00551 00552 QString VCard21ParserImpl::getValue(const QString& name, const QString& qualifier) 00553 { 00554 QString failed; 00555 const QString lowname = name.lower(); 00556 const QString lowqualifier = qualifier.lower(); 00557 00558 for (QValueListIterator<VCardLineX> i = _vcdata->begin();i != _vcdata->end();++i) { 00559 if ((*i).name == lowname && (*i).qualified && (*i).qualifiers.contains(lowqualifier)) { 00560 if ((*i).parameters.count() > 0) 00561 return (*i).parameters[0]; 00562 else return failed; 00563 } 00564 } 00565 return failed; 00566 } 00567 00568 00569 QString VCard21ParserImpl::getValue(const QString& name) 00570 { 00571 QString failed; 00572 const QString lowname = name.lower(); 00573 00574 for (QValueListIterator<VCardLineX> i = _vcdata->begin();i != _vcdata->end();++i) { 00575 if ((*i).name == lowname && !(*i).qualified) { 00576 if ((*i).parameters.count() > 0) 00577 return (*i).parameters[0]; 00578 else return failed; 00579 } 00580 } 00581 return failed; 00582 } 00583 00584 00585 QStringList VCard21ParserImpl::getValues(const QString& name) 00586 { 00587 const QString lowname = name.lower(); 00588 for (QValueListIterator<VCardLineX> i = _vcdata->begin();i != _vcdata->end();++i) { 00589 if ((*i).name == lowname && !(*i).qualified) 00590 return (*i).parameters; 00591 } 00592 // failed. 00593 return QStringList(); 00594 } 00595 00596 QStringList VCard21ParserImpl::getValues(const QString& name, const QString& qualifier) 00597 { 00598 const QString lowname = name.lower(); 00599 const QString lowqualifier = qualifier.lower(); 00600 for (QValueListIterator<VCardLineX> i = _vcdata->begin();i != _vcdata->end();++i) { 00601 if ((*i).name == lowname && (*i).qualified && (*i).qualifiers.contains(lowqualifier)) 00602 return (*i).parameters; 00603 } 00604 // failed. 00605 return QStringList(); 00606 } 00607 00608