vcardparser.cpp

00001 /*
00002     This file is part of libkabc.
00003     Copyright (c) 2003 Tobias Koenig <tokoe@kde.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 <qcstring.h>
00022 #include <qregexp.h>
00023 #include <qtextcodec.h>
00024 #include <qvaluelist.h>
00025 
00026 #include <kmdcodec.h>
00027 #include <kdebug.h>
00028 
00029 #include "vcardparser.h"
00030 
00031 #define FOLD_WIDTH 75
00032 
00033 using namespace KABC;
00034 
00035 typedef QValueList<QCString> QCStringList;
00036 
00037 QValueList<QCString> splitCString( const QCString &str, char sep )
00038 {
00039     QValueList<QCString> list;
00040     int start = 0;
00041     int end;
00042     while ((end = str.find(sep, start)) != -1) {
00043         list.append(str.mid(start, end - start));
00044         start = end + 1;
00045     }
00046     list.append(str.mid(start));
00047 
00048     return list;
00049 }
00050 
00051 QValueList<QCString> splitCString( const QCString &str, const QRegExp &exp )
00052 {
00053     QValueList<QCString> list;
00054     int start = 0;
00055     int end;
00056     while ((end = str.find(exp, start)) != -1) {
00057         list.append(str.mid(start, end - start));
00058         start = end + 1;
00059     }
00060     list.append(str.mid(start));
00061 
00062     return list;
00063 }
00064 
00065 bool cStringStartsWith( const QCString &str, const QCString &pattern )
00066 {
00067   const int length = pattern.length();
00068   if ( length == 0 )
00069     return true;
00070 
00071   const QCString part = str.left( length );
00072   return (pattern == part);
00073 }
00074 
00075 static void addEscapes( QCString &str )
00076 {
00077   str.replace( '\\', "\\\\" );
00078   str.replace( ',', "\\," );
00079   str.replace( '\r', "\\r" );
00080   str.replace( '\n', "\\n" );
00081 }
00082 
00083 static void removeEscapes( QCString &str )
00084 {
00085   str.replace( "\\r", "\r" );
00086   str.replace( "\\n", "\n" );
00087   str.replace( "\\,", "," );
00088   str.replace( "\\\\", "\\" );
00089 }
00090 
00091 VCardParser::VCardParser()
00092 {
00093 }
00094 
00095 VCardParser::~VCardParser()
00096 {
00097 }
00098 
00099 VCard::List VCardParser::parseVCards( const QString& text )
00100 {
00101   return parseVCardsRaw( text.utf8() );
00102 }
00103 
00104 VCard::List VCardParser::parseVCardsRaw( const QCString& text )
00105 {
00106 
00107   static QRegExp sep( "[\x0d\x0a]" );
00108 
00109   VCard currentVCard;
00110   VCard::List vCardList;
00111   QCString currentLine;
00112 
00113   const QCStringList lines = splitCString( text, sep );
00114   QCStringList::ConstIterator it;
00115 
00116   bool inVCard = false;
00117   QCStringList::ConstIterator linesEnd( lines.end() );
00118   for ( it = lines.begin(); it != linesEnd; ++it ) {
00119 
00120     if ( (*it).isEmpty() ) // empty line
00121       continue;
00122 
00123     if ( (*it)[ 0 ] == ' ' || (*it)[ 0 ] == '\t' ) { // folded line => append to previous
00124       currentLine.append( (*it).mid( 1 ) );
00125       continue;
00126     } else {
00127       if ( inVCard && !currentLine.isEmpty() ) { // now parse the line
00128         int colon = currentLine.find( ':' );
00129         if ( colon == -1 ) { // invalid line
00130           currentLine = (*it);
00131           continue;
00132         }
00133 
00134         VCardLine vCardLine;
00135         const QCString key = currentLine.left( colon ).stripWhiteSpace();
00136         QCString value = currentLine.mid( colon + 1 );
00137 
00138         QCStringList params = splitCString( key, ';' );
00139 
00140         // check for group
00141         if ( params[0].find( '.' ) != -1 ) {
00142           const QCStringList groupList = splitCString( params[0], '.' );
00143           vCardLine.setGroup( QString::fromLatin1( groupList[0] ) );
00144           vCardLine.setIdentifier( QString::fromLatin1( groupList[1] ) );
00145         } else
00146           vCardLine.setIdentifier( QString::fromLatin1( params[0] ) );
00147 
00148         if ( params.count() > 1 ) { // find all parameters
00149           QCStringList::ConstIterator paramIt = params.begin();
00150           for ( ++paramIt; paramIt != params.end(); ++paramIt ) {
00151             QCStringList pair = splitCString( *paramIt, '=' );
00152             if ( pair.size() == 1 ) {
00153               // correct the fucking 2.1 'standard'
00154               if ( pair[0].lower() == "quoted-printable" ) {
00155                 pair[0] = "encoding";
00156                 pair[1] = "quoted-printable";
00157               } else if ( pair[0].lower() == "base64" ) {
00158                 pair[0] = "encoding";
00159                 pair[1] = "base64";
00160               } else {
00161                 pair.prepend( "type" );
00162               }
00163             }
00164             // This is pretty much a faster pair[1].contains( ',' )...
00165             if ( pair[1].find( ',' ) != -1 ) { // parameter in type=x,y,z format
00166               const QCStringList args = splitCString( pair[ 1 ], ',' );
00167               QCStringList::ConstIterator argIt;
00168               for ( argIt = args.begin(); argIt != args.end(); ++argIt )
00169                 vCardLine.addParameter( QString::fromLatin1( pair[0].lower() ), QString::fromLatin1( *argIt ) );
00170             } else
00171               vCardLine.addParameter( QString::fromLatin1( pair[0].lower() ), QString::fromLatin1( pair[1] ) );
00172           }
00173         }
00174 
00175         removeEscapes( value );
00176 
00177         QByteArray output;
00178         bool wasBase64Encoded = false;
00179 
00180         if ( vCardLine.parameterList().findIndex( "encoding" ) != -1 ) { // have to decode the data
00181           QByteArray input = value;
00182           if ( vCardLine.parameter( "encoding" ).lower() == "b" ||
00183                vCardLine.parameter( "encoding" ).lower() == "base64" ) {
00184             KCodecs::base64Decode( input, output );
00185             wasBase64Encoded = true;
00186           }
00187           else if ( vCardLine.parameter( "encoding" ).lower() == "quoted-printable" ) {
00188             // join any qp-folded lines
00189             while ( value.at( value.length() - 1 ) == '=' && it != linesEnd ) {
00190               value = value.remove( value.length() - 1, 1 ) + (*it);
00191               ++it;
00192             }
00193             input = value;
00194             KCodecs::quotedPrintableDecode( input, output );
00195           }
00196         } else {
00197           output = value;
00198         }
00199 
00200         if ( vCardLine.parameterList().findIndex( "charset" ) != -1 ) { // have to convert the data
00201           QTextCodec *codec =
00202             QTextCodec::codecForName( vCardLine.parameter( "charset" ).latin1() );
00203           if ( codec ) {
00204             vCardLine.setValue( codec->toUnicode( output ) );
00205           } else {
00206 #if defined(KABC_VCARD_ENCODING_FIX)
00207             vCardLine.setValue( QString::fromUtf8( output ) );
00208 #else
00209             vCardLine.setValue( QString::fromLatin1( output ) );
00210 #endif
00211           }
00212         } else if ( wasBase64Encoded ) {
00213             vCardLine.setValue( output );
00214         } else {
00215 #if defined(KABC_VCARD_ENCODING_FIX)
00216             vCardLine.setValue( QString::fromUtf8( output ) );
00217 #else
00218             vCardLine.setValue( QString::fromLatin1( output ) );
00219 #endif
00220         }
00221 
00222         currentVCard.addLine( vCardLine );
00223       }
00224 
00225       // we do not save the start and end tag as vcardline
00226       if ( cStringStartsWith( (*it).lower(), QCString( "begin:vcard" ) ) ) {
00227         inVCard = true;
00228         currentLine = QCString();
00229         currentVCard.clear(); // flush vcard
00230         continue;
00231       }
00232 
00233       if ( cStringStartsWith( (*it).lower(), QCString( "end:vcard" ) ) ) {
00234         inVCard = false;
00235         vCardList.append( currentVCard );
00236         currentLine = QCString();
00237         currentVCard.clear(); // flush vcard
00238         continue;
00239       }
00240 
00241       currentLine = (*it);
00242     }
00243   }
00244 
00245   return vCardList;
00246 }
00247 
00248 QString VCardParser::createVCards( const VCard::List& list )
00249 {
00250   return QString::fromUtf8( createVCardsRaw( list ) );
00251 }
00252 
00253 QCString VCardParser::createVCardsRaw( const VCard::List& list )
00254 {
00255   QCString text;
00256   QCString textLine;
00257   QString encodingType;
00258   QStringList idents;
00259   QStringList params;
00260   QStringList values;
00261   QStringList::ConstIterator identIt;
00262   QStringList::Iterator paramIt;
00263   QStringList::ConstIterator valueIt;
00264 
00265   VCardLine::List lines;
00266   VCardLine::List::ConstIterator lineIt;
00267   VCard::List::ConstIterator cardIt;
00268 
00269   bool hasEncoding;
00270 
00271 //  text.reserve( list.size() * 300 ); // reserve memory to be more efficient
00272 
00273   // iterate over the cards
00274   VCard::List::ConstIterator listEnd( list.end() );
00275   for ( cardIt = list.begin(); cardIt != listEnd; ++cardIt ) {
00276     text.append( "BEGIN:VCARD\r\n" );
00277 
00278     idents = (*cardIt).identifiers();
00279     for ( identIt = idents.constBegin(); identIt != idents.constEnd(); ++identIt ) {
00280       lines = (*cardIt).lines( (*identIt) );
00281 
00282       // iterate over the lines
00283       for ( lineIt = lines.constBegin(); lineIt != lines.constEnd(); ++lineIt ) {
00284         if ( !(*lineIt).value().asString().isEmpty() ) {
00285           if ( (*lineIt).hasGroup() )
00286             textLine = (*lineIt).group().latin1() + QCString( "." ) + (*lineIt).identifier().latin1();
00287           else
00288             textLine = (*lineIt).identifier().latin1();
00289 
00290           params = (*lineIt).parameterList();
00291           hasEncoding = false;
00292           if ( params.count() > 0 ) { // we have parameters
00293             for ( paramIt = params.begin(); paramIt != params.end(); ++paramIt ) {
00294               if ( (*paramIt) == "encoding" ) {
00295                 hasEncoding = true;
00296                 encodingType = (*lineIt).parameter( "encoding" ).lower();
00297               }
00298 
00299               values = (*lineIt).parameters( *paramIt );
00300               for ( valueIt = values.constBegin(); valueIt != values.constEnd(); ++valueIt ) {
00301                 textLine.append( QCString( ";" ) + (*paramIt).upper().latin1() );
00302                 if ( !(*valueIt).isEmpty() )
00303                   textLine.append( QCString( "=" ) + (*valueIt).latin1() );
00304               }
00305             }
00306           }
00307 
00308           if ( hasEncoding ) { // have to encode the data
00309             QByteArray input, output;
00310             if ( encodingType == "b" ) {
00311               input = (*lineIt).value().toByteArray();
00312               KCodecs::base64Encode( input, output );
00313             } else if ( encodingType == "quoted-printable" ) {
00314               input = (*lineIt).value().toString().utf8();
00315               input.resize( input.size() - 1 ); // strip \0
00316               KCodecs::quotedPrintableEncode( input, output, false );
00317             }
00318 
00319             QCString value( output );
00320             addEscapes( value );
00321             textLine.append( ":" + value );
00322           } else {
00323             QCString value( (*lineIt).value().toString().utf8() );
00324             addEscapes( value );
00325             textLine.append( ":" + value );
00326           }
00327 
00328           if ( textLine.length() > FOLD_WIDTH ) { // we have to fold the line
00329             for ( uint i = 0; i <= ( textLine.length() / FOLD_WIDTH ); ++i )
00330               text.append( ( i == 0 ? "" : " " ) + textLine.mid( i * FOLD_WIDTH, FOLD_WIDTH ) + "\r\n" );
00331           } else
00332             text.append( textLine + "\r\n" );
00333         }
00334       }
00335     }
00336 
00337     text.append( "END:VCARD\r\n" );
00338     text.append( "\r\n" );
00339   }
00340 
00341   return text;
00342 }
KDE Home | KDE Accessibility Home | Description of Access Keys