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