kabc
ldifconverter.cpp
00001 /* 00002 This file is part of libkabc. 00003 Copyright (c) 2003 Helge Deller <deller@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 /* 00022 Useful links: 00023 - http://tldp.org/HOWTO/LDAP-Implementation-HOWTO/schemas.html 00024 - http://www.faqs.org/rfcs/rfc2849.html 00025 00026 Not yet handled items: 00027 - objectclass microsoftaddressbook 00028 - info, 00029 - initials, 00030 - otherfacsimiletelephonenumber, 00031 - otherpager, 00032 - physicaldeliveryofficename, 00033 */ 00034 00035 #include "ldifconverter.h" 00036 #include "vcardconverter.h" 00037 #include "address.h" 00038 #include "addressee.h" 00039 00040 #include "ldif_p.h" 00041 00042 #include <kdebug.h> 00043 #include <klocale.h> 00044 00045 #include <QtCore/QRegExp> 00046 #include <QtCore/QStringList> 00047 #include <QtCore/QTextCodec> 00048 #include <QtCore/QTextStream> 00049 00050 using namespace KABC; 00051 00052 /* generate LDIF stream */ 00053 00054 bool LDIFConverter::addresseeToLDIF( const AddresseeList &addrList, QString &str ) 00055 { 00056 AddresseeList::ConstIterator it; 00057 for ( it = addrList.begin(); it != addrList.end(); ++it ) { 00058 addresseeToLDIF( *it, str ); 00059 } 00060 return true; 00061 } 00062 00063 static void ldif_out( QTextStream &t, const QString &formatStr, 00064 const QString &value ) 00065 { 00066 if ( value.isEmpty() ) { 00067 return; 00068 } 00069 00070 QByteArray txt = Ldif::assembleLine( formatStr, value, 72 ); 00071 00072 // write the string 00073 t << QString::fromUtf8( txt ) << "\n"; 00074 } 00075 00076 bool LDIFConverter::addresseeToLDIF( const Addressee &addr, QString &str ) 00077 { 00078 if ( addr.isEmpty() ) { 00079 return false; 00080 } 00081 00082 QTextStream t( &str, QIODevice::WriteOnly|QIODevice::Append ); 00083 t.setCodec( QTextCodec::codecForName( "UTF-8" ) ); 00084 00085 const Address homeAddr = addr.address( Address::Home ); 00086 const Address workAddr = addr.address( Address::Work ); 00087 00088 ldif_out( t, QLatin1String( "dn" ), QString::fromLatin1( "cn=%1,mail=%2" ). 00089 arg( addr.formattedName().simplified() ). 00090 arg( addr.preferredEmail() ) ); 00091 ldif_out( t, QLatin1String( "givenname" ), addr.givenName() ); 00092 ldif_out( t, QLatin1String( "sn" ), addr.familyName() ); 00093 ldif_out( t, QLatin1String( "cn" ), addr.formattedName().simplified() ); 00094 ldif_out( t, QLatin1String( "uid" ), addr.uid() ); 00095 ldif_out( t, QLatin1String( "nickname" ), addr.nickName() ); 00096 ldif_out( t, QLatin1String( "xmozillanickname" ), addr.nickName() ); 00097 00098 ldif_out( t, QLatin1String( "mail" ), addr.preferredEmail() ); 00099 if ( addr.emails().count() > 1 ) { 00100 ldif_out( t, QLatin1String( "mozillasecondemail" ), addr.emails()[ 1 ] ); 00101 } 00102 //ldif_out( t, "mozilla_AIMScreenName: %1\n", "screen_name" ); 00103 00104 ldif_out( t, QLatin1String( "telephonenumber" ), 00105 addr.phoneNumber( PhoneNumber::Work ).number() ); 00106 ldif_out( t, QLatin1String( "facsimiletelephonenumber" ), 00107 addr.phoneNumber( PhoneNumber::Fax ).number() ); 00108 ldif_out( t, QLatin1String( "homephone" ), 00109 addr.phoneNumber( PhoneNumber::Home ).number() ); 00110 ldif_out( t, QLatin1String( "mobile" ), 00111 addr.phoneNumber( PhoneNumber::Cell ).number() ); // Netscape 7 00112 ldif_out( t, QLatin1String( "cellphone" ), 00113 addr.phoneNumber( PhoneNumber::Cell ).number() ); // Netscape 4.x 00114 ldif_out( t, QLatin1String( "pager" ), 00115 addr.phoneNumber( PhoneNumber::Pager ).number() ); 00116 ldif_out( t, QLatin1String( "pagerphone" ), 00117 addr.phoneNumber( PhoneNumber::Pager ).number() ); 00118 00119 ldif_out( t, QLatin1String( "streethomeaddress" ), homeAddr.street() ); 00120 ldif_out( t, QLatin1String( "postalcode" ), workAddr.postalCode() ); 00121 ldif_out( t, QLatin1String( "postofficebox" ), workAddr.postOfficeBox() ); 00122 00123 QStringList streets = homeAddr.street().split( QLatin1Char( '\n' ) ); 00124 if ( streets.count() > 0 ) { 00125 ldif_out( t, QLatin1String( "homepostaladdress" ), streets[ 0 ] ); // Netscape 7 00126 } 00127 if ( streets.count() > 1 ) { 00128 ldif_out( t, QLatin1String( "mozillahomepostaladdress2" ), streets[ 1 ] ); // Netscape 7 00129 } 00130 ldif_out( t, QLatin1String( "mozillahomelocalityname" ), homeAddr.locality() ); // Netscape 7 00131 ldif_out( t, QLatin1String( "mozillahomestate" ), homeAddr.region() ); 00132 ldif_out( t, QLatin1String( "mozillahomepostalcode" ), homeAddr.postalCode() ); 00133 ldif_out( t, QLatin1String( "mozillahomecountryname" ), 00134 Address::ISOtoCountry( homeAddr.country() ) ); 00135 ldif_out( t, QLatin1String( "locality" ), workAddr.locality() ); 00136 ldif_out( t, QLatin1String( "streetaddress" ), workAddr.street() ); // Netscape 4.x 00137 00138 streets = workAddr.street().split( QLatin1Char( '\n' ) ); 00139 if ( streets.count() > 0 ) { 00140 ldif_out( t, QLatin1String( "postaladdress" ), streets[ 0 ] ); 00141 } 00142 if ( streets.count() > 1 ) { 00143 ldif_out( t, QLatin1String( "mozillapostaladdress2" ), streets[ 1 ] ); 00144 } 00145 ldif_out( t, QLatin1String( "countryname" ), Address::ISOtoCountry( workAddr.country() ) ); 00146 ldif_out( t, QLatin1String( "l" ), workAddr.locality() ); 00147 ldif_out( t, QLatin1String( "c" ), Address::ISOtoCountry( workAddr.country() ) ); 00148 ldif_out( t, QLatin1String( "st" ), workAddr.region() ); 00149 00150 ldif_out( t, QLatin1String( "title" ), addr.title() ); 00151 ldif_out( t, QLatin1String( "vocation" ), addr.prefix() ); 00152 ldif_out( t, QLatin1String( "ou" ), addr.role() ); 00153 ldif_out( t, QLatin1String( "o" ), addr.organization() ); 00154 ldif_out( t, QLatin1String( "organization" ), addr.organization() ); 00155 ldif_out( t, QLatin1String( "organizationname" ), addr.organization() ); 00156 00157 // Compatibility with older kabc versions. 00158 if ( !addr.department().isEmpty() ) { 00159 ldif_out( t, QLatin1String( "department" ), addr.department() ); 00160 } else { 00161 ldif_out( t, QLatin1String( "department" ), addr.custom( QLatin1String( "KADDRESSBOOK" ), 00162 QLatin1String( "X-Department" ) ) ); 00163 } 00164 00165 ldif_out( t, QLatin1String( "workurl" ), addr.url().prettyUrl() ); 00166 ldif_out( t, QLatin1String( "homeurl" ), addr.url().prettyUrl() ); 00167 ldif_out( t, QLatin1String( "description" ), addr.note() ); 00168 if ( addr.revision().isValid() ) { 00169 ldif_out( t, QLatin1String( "modifytimestamp" ), dateToVCardString( addr.revision() ) ); 00170 } 00171 00172 t << "objectclass: top\n"; 00173 t << "objectclass: person\n"; 00174 t << "objectclass: organizationalPerson\n"; 00175 00176 t << "\n"; 00177 00178 return true; 00179 } 00180 00181 /* convert from LDIF stream */ 00182 00183 bool LDIFConverter::LDIFToAddressee( const QString &str, AddresseeList &addrList, 00184 const QDateTime &dt ) 00185 { 00186 if ( str.isEmpty() ) { 00187 return true; 00188 } 00189 00190 bool endldif = false, end = false; 00191 Ldif ldif; 00192 Ldif::ParseValue ret; 00193 Addressee a; 00194 Address homeAddr, workAddr; 00195 00196 ldif.setLdif( str.toLatin1() ); 00197 QDateTime qdt = dt; 00198 if ( !qdt.isValid() ) { 00199 qdt = QDateTime::currentDateTime(); 00200 } 00201 a.setRevision( qdt ); 00202 homeAddr = Address( Address::Home ); 00203 workAddr = Address( Address::Work ); 00204 00205 do { 00206 ret = ldif.nextItem(); 00207 switch ( ret ) { 00208 case Ldif::Item: 00209 { 00210 QString fieldname = ldif.attr().toLower(); 00211 QString value = QString::fromUtf8( ldif.value(), ldif.value().size() ); 00212 evaluatePair( a, homeAddr, workAddr, fieldname, value ); 00213 break; 00214 } 00215 case Ldif::EndEntry: 00216 // if the new address is not empty, append it 00217 if ( !a.formattedName().isEmpty() || !a.name().isEmpty() || 00218 !a.familyName().isEmpty() ) { 00219 if ( !homeAddr.isEmpty() ) { 00220 a.insertAddress( homeAddr ); 00221 } 00222 if ( !workAddr.isEmpty() ) { 00223 a.insertAddress( workAddr ); 00224 } 00225 addrList.append( a ); 00226 } 00227 a = Addressee(); 00228 a.setRevision( qdt ); 00229 homeAddr = Address( Address::Home ); 00230 workAddr = Address( Address::Work ); 00231 break; 00232 case Ldif::MoreData: 00233 { 00234 if ( endldif ) { 00235 end = true; 00236 } else { 00237 ldif.endLdif(); 00238 endldif = true; 00239 break; 00240 } 00241 } 00242 default: 00243 break; 00244 } 00245 } while ( !end ); 00246 00247 return true; 00248 } 00249 00250 bool LDIFConverter::evaluatePair( Addressee &a, Address &homeAddr, 00251 Address &workAddr, 00252 QString &fieldname, QString &value ) 00253 { 00254 if ( fieldname == QLatin1String( "dn" ) ) { // ignore & return false! 00255 return false; 00256 } 00257 00258 if ( fieldname.startsWith( QLatin1Char( '#' ) ) ) { 00259 return true; 00260 } 00261 00262 if ( fieldname.isEmpty() && !a.note().isEmpty() ) { 00263 // some LDIF export filters are borken and add additional 00264 // comments on stand-alone lines. Just add them to the notes for now. 00265 a.setNote( a.note() + QLatin1Char( '\n' ) + value ); 00266 return true; 00267 } 00268 00269 if ( fieldname == QLatin1String( "givenname" ) ) { 00270 a.setGivenName( value ); 00271 return true; 00272 } 00273 00274 if ( fieldname == QLatin1String( "xmozillanickname" ) || 00275 fieldname == QLatin1String( "nickname" ) ) { 00276 a.setNickName( value ); 00277 return true; 00278 } 00279 00280 if ( fieldname == QLatin1String( "sn" ) ) { 00281 a.setFamilyName( value ); 00282 return true; 00283 } 00284 00285 if ( fieldname == QLatin1String( "uid" ) ) { 00286 a.setUid( value ); 00287 return true; 00288 } 00289 if ( fieldname == QLatin1String( "mail" ) || 00290 fieldname == QLatin1String( "mozillasecondemail" ) ) { // mozilla 00291 if ( a.emails().indexOf( value ) == -1 ) { 00292 a.insertEmail( value ); 00293 } 00294 return true; 00295 } 00296 00297 if ( fieldname == QLatin1String( "title" ) ) { 00298 a.setTitle( value ); 00299 return true; 00300 } 00301 00302 if ( fieldname == QLatin1String( "vocation" ) ) { 00303 a.setPrefix( value ); 00304 return true; 00305 } 00306 00307 if ( fieldname == QLatin1String( "cn" ) ) { 00308 a.setFormattedName( value ); 00309 return true; 00310 } 00311 00312 if ( fieldname == QLatin1String( "o" ) || 00313 fieldname == QLatin1String( "organization" ) || // Exchange 00314 fieldname == QLatin1String( "organizationname" ) ) { // Exchange 00315 a.setOrganization( value ); 00316 return true; 00317 } 00318 00319 if ( fieldname == QLatin1String( "description" ) ) { 00320 addComment: 00321 if ( !a.note().isEmpty() ) { 00322 a.setNote( a.note() + QLatin1Char( '\n' ) ); 00323 } 00324 a.setNote( a.note() + value ); 00325 return true; 00326 } 00327 00328 if ( fieldname == QLatin1String( "custom1" ) || 00329 fieldname == QLatin1String( "custom2" ) || 00330 fieldname == QLatin1String( "custom3" ) || 00331 fieldname == QLatin1String( "custom4" ) ) { 00332 goto addComment; 00333 } 00334 00335 if ( fieldname == QLatin1String( "homeurl" ) || 00336 fieldname == QLatin1String( "workurl" ) ) { 00337 if ( a.url().isEmpty() ) { 00338 a.setUrl( KUrl( value ) ); 00339 return true; 00340 } 00341 if ( a.url().prettyUrl() == KUrl( value ).prettyUrl() ) { 00342 return true; 00343 } 00344 // TODO: current version of kabc only supports one URL. 00345 // TODO: change this with KDE 4 00346 } 00347 00348 if ( fieldname == QLatin1String( "homephone" ) ) { 00349 a.insertPhoneNumber( PhoneNumber( value, PhoneNumber::Home ) ); 00350 return true; 00351 } 00352 00353 if ( fieldname == QLatin1String( "telephonenumber" ) ) { 00354 a.insertPhoneNumber( PhoneNumber( value, PhoneNumber::Work ) ); 00355 return true; 00356 } 00357 00358 if ( fieldname == QLatin1String( "mobile" ) ) { // mozilla/Netscape 7 00359 a.insertPhoneNumber( PhoneNumber( value, PhoneNumber::Cell ) ); 00360 return true; 00361 } 00362 00363 if ( fieldname == QLatin1String( "cellphone" ) ) { 00364 a.insertPhoneNumber( PhoneNumber( value, PhoneNumber::Cell ) ); 00365 return true; 00366 } 00367 00368 if ( fieldname == QLatin1String( "pager" ) || // mozilla 00369 fieldname == QLatin1String( "pagerphone" ) ) { // mozilla 00370 a.insertPhoneNumber( PhoneNumber( value, PhoneNumber::Pager ) ); 00371 return true; 00372 } 00373 00374 if ( fieldname == QLatin1String( "facsimiletelephonenumber" ) ) { 00375 a.insertPhoneNumber( PhoneNumber( value, PhoneNumber::Fax ) ); 00376 return true; 00377 } 00378 00379 if ( fieldname == QLatin1String( "xmozillaanyphone" ) ) { // mozilla 00380 a.insertPhoneNumber( PhoneNumber( value, PhoneNumber::Work ) ); 00381 return true; 00382 } 00383 00384 if ( fieldname == QLatin1String( "street" ) || 00385 fieldname == QLatin1String( "streethomeaddress" ) ) { 00386 homeAddr.setStreet( value ); 00387 return true; 00388 } 00389 00390 if ( fieldname == QLatin1String( "postaladdress" ) ) { // mozilla 00391 workAddr.setStreet( value ); 00392 return true; 00393 } 00394 00395 if ( fieldname == QLatin1String( "mozillapostaladdress2" ) ) { // mozilla 00396 workAddr.setStreet( workAddr.street() + QLatin1String( "\n" ) + value ); 00397 return true; 00398 } 00399 00400 if ( fieldname == QLatin1String( "postalcode" ) ) { 00401 workAddr.setPostalCode( value ); 00402 return true; 00403 } 00404 00405 if ( fieldname == QLatin1String( "postofficebox" ) ) { 00406 workAddr.setPostOfficeBox( value ); 00407 return true; 00408 } 00409 00410 if ( fieldname == QLatin1String( "homepostaladdress" ) ) { // Netscape 7 00411 homeAddr.setStreet( value ); 00412 return true; 00413 } 00414 00415 if ( fieldname == QLatin1String( "mozillahomepostaladdress2" ) ) { // mozilla 00416 homeAddr.setStreet( homeAddr.street() + QLatin1String( "\n" ) + value ); 00417 return true; 00418 } 00419 00420 if ( fieldname == QLatin1String( "mozillahomelocalityname" ) ) { // mozilla 00421 homeAddr.setLocality( value ); 00422 return true; 00423 } 00424 00425 if ( fieldname == QLatin1String( "mozillahomestate" ) ) { // mozilla 00426 homeAddr.setRegion( value ); 00427 return true; 00428 } 00429 00430 if ( fieldname == QLatin1String( "mozillahomepostalcode" ) ) { // mozilla 00431 homeAddr.setPostalCode( value ); 00432 return true; 00433 } 00434 00435 if ( fieldname == QLatin1String( "mozillahomecountryname" ) ) { // mozilla 00436 if ( value.length() <= 2 ) { 00437 value = Address::ISOtoCountry( value ); 00438 } 00439 homeAddr.setCountry( value ); 00440 return true; 00441 } 00442 00443 if ( fieldname == QLatin1String( "locality" ) ) { 00444 workAddr.setLocality( value ); 00445 return true; 00446 } 00447 00448 if ( fieldname == QLatin1String( "streetaddress" ) ) { // Netscape 4.x 00449 workAddr.setStreet( value ); 00450 return true; 00451 } 00452 00453 if ( fieldname == QLatin1String( "countryname" ) || 00454 fieldname == QLatin1String( "c" ) ) { // mozilla 00455 if ( value.length() <= 2 ) { 00456 value = Address::ISOtoCountry( value ); 00457 } 00458 workAddr.setCountry( value ); 00459 return true; 00460 } 00461 00462 if ( fieldname == QLatin1String( "l" ) ) { // mozilla 00463 workAddr.setLocality( value ); 00464 return true; 00465 } 00466 00467 if ( fieldname == QLatin1String( "st" ) ) { 00468 workAddr.setRegion( value ); 00469 return true; 00470 } 00471 00472 if ( fieldname == QLatin1String( "ou" ) ) { 00473 a.setRole( value ); 00474 return true; 00475 } 00476 00477 if ( fieldname == QLatin1String( "department" ) ) { 00478 a.setDepartment( value ); 00479 return true; 00480 } 00481 00482 if ( fieldname == QLatin1String( "member" ) ) { 00483 // this is a mozilla list member (cn=xxx, mail=yyy) 00484 QStringList list = value.split( QLatin1Char( ',' ) ); 00485 QString name, email; 00486 00487 QStringList::Iterator it; 00488 for ( it = list.begin(); it != list.end(); ++it ) { 00489 if ( (*it).startsWith( QLatin1String( "cn=" ) ) ) { 00490 name = (*it).mid( 3 ).trimmed(); 00491 } 00492 if ( (*it).startsWith( QLatin1String( "mail=" ) ) ) { 00493 email = (*it).mid( 5 ).trimmed(); 00494 } 00495 } 00496 if ( !name.isEmpty() && !email.isEmpty() ) { 00497 email = QLatin1String( " <" ) + email + QLatin1Char( '>' ); 00498 } 00499 a.insertEmail( name + email ); 00500 a.insertCategory( i18n( "List of Emails" ) ); 00501 return true; 00502 } 00503 00504 if ( fieldname == QLatin1String( "modifytimestamp" ) ) { 00505 if ( value == QLatin1String( "0Z" ) ) { // ignore 00506 return true; 00507 } 00508 QDateTime dt = VCardStringToDate( value ); 00509 if ( dt.isValid() ) { 00510 a.setRevision( dt ); 00511 return true; 00512 } 00513 } 00514 00515 if ( fieldname == QLatin1String( "objectclass" ) ) { // ignore 00516 return true; 00517 } 00518 00519 kWarning(5700) << QString::fromLatin1( "LDIFConverter: Unknown field for '%1': '%2=%3'\n" ). 00520 arg( a.formattedName() ).arg( fieldname ).arg( value ); 00521 00522 return true; 00523 }