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 
KDE Home | KDE Accessibility Home | Description of Access Keys