• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • Contact Us
 

kabc

resourcefile.cpp

00001 /*
00002     This file is part of libkabc.
00003 
00004     Copyright (c) 2001,2003 Cornelius Schumacher <schumacher@kde.org>
00005 
00006     This library is free software; you can redistribute it and/or
00007     modify it under the terms of the GNU Library General Public
00008     License as published by the Free Software Foundation; either
00009     version 2 of the License, or (at your option) any later version.
00010 
00011     This library is distributed in the hope that it will be useful,
00012     but WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014     Library General Public License for more details.
00015 
00016     You should have received a copy of the GNU Library General Public License
00017     along with this library; see the file COPYING.LIB.  If not, write to
00018     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019     Boston, MA 02110-1301, USA.
00020 */
00021 
00022 #include "resourcefile.h"
00023 #include "resourcefileconfig.h"
00024 
00025 #include "kabc/formatfactory.h"
00026 #include "kabc/stdaddressbook.h"
00027 #include "kabc/lock.h"
00028 
00029 #include <kio/scheduler.h>
00030 #include <kconfiggroup.h>
00031 #include <kdebug.h>
00032 #include <klocale.h>
00033 #include <ksavefile.h>
00034 #include <kstandarddirs.h>
00035 #include <ktemporaryfile.h>
00036 
00037 #include <QtCore/QFile>
00038 #include <QtCore/QFileInfo>
00039 
00040 #include <sys/types.h>
00041 #include <sys/stat.h>
00042 #include <signal.h>
00043 #include <unistd.h>
00044 
00045 static const char *s_customFieldName = "DistributionList";
00046 
00047 using namespace KABC;
00048 
00049 typedef QList< QPair<QString, QString> > MissingEntryList;
00050 
00051 static bool isDistributionList( const Addressee &addr )
00052 {
00053   const QString str = addr.custom( "KADDRESSBOOK", s_customFieldName );
00054   return !str.isEmpty();
00055 }
00056 
00057 // Helper function, to parse the contents of the custom field
00058 // Returns a list of { uid, email }
00059 typedef QList<QPair<QString, QString> > ParseList;
00060 static ParseList parseCustom( const Addressee &addr )
00061 {
00062   ParseList res;
00063   const QStringList lst = addr.custom( "KADDRESSBOOK", s_customFieldName ).split( ';', QString::SkipEmptyParts );
00064   for ( QStringList::ConstIterator it = lst.constBegin(); it != lst.constEnd(); ++it ) {
00065     if ( (*it).isEmpty() ) {
00066       continue;
00067     }
00068 
00069     // parse "uid,email"
00070     QStringList helpList = (*it).split( ',', QString::SkipEmptyParts );
00071     Q_ASSERT( !helpList.isEmpty() );
00072     if ( helpList.isEmpty() ) {
00073       continue;
00074     }
00075     const QString uid = helpList.first();
00076     QString email;
00077     Q_ASSERT( helpList.count() < 3 ); // 1 or 2 items, but not more
00078     if ( helpList.count() == 2 ) {
00079       email = helpList.last();
00080     }
00081     res.append( qMakePair( uid, email ) );
00082   }
00083   return res;
00084 }
00085 
00086 static KABC::Addressee::List findByFormattedName( KABC::AddressBook *book,
00087                                                   const QString &name,
00088                                                   bool caseSensitive = true )
00089 {
00090   KABC::Addressee::List res;
00091   KABC::AddressBook::Iterator abIt;
00092   for ( abIt = book->begin(); abIt != book->end(); ++abIt ) {
00093     if ( caseSensitive && (*abIt).formattedName() == name ) {
00094       res.append( *abIt );
00095     }
00096     if ( !caseSensitive && (*abIt).formattedName().toLower() == name.toLower() ) {
00097       res.append( *abIt );
00098     }
00099   }
00100   return res;
00101 }
00102 
00103 static KABC::Addressee findByUidOrName( KABC::AddressBook *book,
00104                                         const QString &uidOrName,
00105                                         const QString &email )
00106 {
00107   KABC::Addressee a = book->findByUid( uidOrName );
00108   if ( a.isEmpty() ) {
00109     // UID not found, maybe it is a name instead.
00110     // If we have an email, let's use that for the lookup.
00111     // [This is used by e.g. the Kolab resource]
00112     if ( !email.isEmpty() ) {
00113       KABC::Addressee::List lst = book->findByEmail( email );
00114       KABC::Addressee::List::ConstIterator listit = lst.constBegin();
00115       for ( ; listit != lst.constEnd(); ++listit ) {
00116         if ( (*listit).formattedName() == uidOrName ) {
00117           a = *listit;
00118           break;
00119         }
00120       }
00121       if ( !lst.isEmpty() && a.isEmpty() ) { // found that email, but no match on the fullname
00122         a = lst.first(); // probably the last name changed
00123       }
00124     }
00125 
00126     // If we don't have an email, or if we didn't find any match for it, look up by full name
00127     if ( a.isEmpty() ) {
00128       // (But this has to be done here, since when loading we might not have the entries yet)
00129       KABC::Addressee::List lst = findByFormattedName( book, uidOrName );
00130       if ( !lst.isEmpty() ) {
00131         a = lst.first();
00132       }
00133     }
00134   }
00135   return a;
00136 }
00137 
00138 class ResourceFile::ResourceFilePrivate
00139 {
00140   public:
00141     QMap< QString, MissingEntryList > mMissingEntries;
00142 };
00143 
00144 ResourceFile::ResourceFile()
00145   : Resource(), mFormat( 0 ), mTempFile( 0 ),
00146     mAsynchronous( false ), d( new ResourceFilePrivate )
00147 {
00148   QString fileName, formatName;
00149 
00150   fileName = StdAddressBook::fileName();
00151   formatName = "vcard";
00152 
00153   init( fileName, formatName );
00154 }
00155 
00156 ResourceFile::ResourceFile( const KConfigGroup &group )
00157   : Resource( group ), mFormat( 0 ), mTempFile( 0 ),
00158     mAsynchronous( false ), d( new ResourceFilePrivate )
00159 {
00160   QString fileName, formatName;
00161 
00162   fileName = group.readPathEntry( "FileName", StdAddressBook::fileName() );
00163   formatName = group.readEntry( "FileFormat", "vcard" );
00164 
00165   init( fileName, formatName );
00166 }
00167 
00168 ResourceFile::ResourceFile( const QString &fileName,
00169                             const QString &formatName )
00170   : Resource(), mFormat( 0 ), mTempFile( 0 ),
00171     mAsynchronous( false ), d( new ResourceFilePrivate )
00172 {
00173   init( fileName, formatName );
00174 }
00175 
00176 void ResourceFile::init( const QString &fileName, const QString &formatName )
00177 {
00178   mFormatName = formatName;
00179 
00180   FormatFactory *factory = FormatFactory::self();
00181   mFormat = factory->format( mFormatName );
00182 
00183   if ( !mFormat ) {
00184     mFormatName = "vcard";
00185     mFormat = factory->format( mFormatName );
00186   }
00187 
00188   connect( &mDirWatch, SIGNAL( dirty(const QString&) ), SLOT( fileChanged(const QString&) ) );
00189   connect( &mDirWatch, SIGNAL( created(const QString&) ), SLOT( fileChanged(const QString&) ) );
00190   connect( &mDirWatch, SIGNAL( deleted(const QString&) ), SLOT( fileChanged(const QString&) ) );
00191 
00192   setFileName( fileName );
00193 
00194   mDirWatch.addFile( KStandardDirs::locateLocal( "data", "kabc/distlists" ) );
00195 
00196   mLock = 0;
00197 }
00198 
00199 ResourceFile::~ResourceFile()
00200 {
00201   delete d;
00202   d = 0;
00203   delete mFormat;
00204   mFormat = 0;
00205 }
00206 
00207 void ResourceFile::writeConfig( KConfigGroup &group )
00208 {
00209   Resource::writeConfig( group );
00210 
00211   if ( mFileName == StdAddressBook::fileName() ) {
00212     group.deleteEntry( "FileName" );
00213   } else {
00214     group.writePathEntry( "FileName", mFileName );
00215   }
00216 
00217   group.writeEntry( "FileFormat", mFormatName );
00218 }
00219 
00220 Ticket *ResourceFile::requestSaveTicket()
00221 {
00222   kDebug();
00223 
00224   if ( !addressBook() ) {
00225     return 0;
00226   }
00227 
00228   delete mLock;
00229   mLock = new Lock( mFileName );
00230 
00231   if ( mLock->lock() ) {
00232     addressBook()->emitAddressBookLocked();
00233   } else {
00234     addressBook()->error( mLock->error() );
00235     kDebug() << "Unable to lock file '" << mFileName
00236              << "':" << mLock->error();
00237     return 0;
00238   }
00239 
00240   return createTicket( this );
00241 }
00242 
00243 void ResourceFile::releaseSaveTicket( Ticket *ticket )
00244 {
00245   delete ticket;
00246 
00247   delete mLock;
00248   mLock = 0;
00249 
00250   addressBook()->emitAddressBookUnlocked();
00251 }
00252 
00253 bool ResourceFile::doOpen()
00254 {
00255   QFile file( mFileName );
00256 
00257   if ( !file.exists() ) {
00258     // try to create the file
00259     bool ok = file.open( QIODevice::WriteOnly );
00260     if ( ok ) {
00261       file.close();
00262     }
00263     return ok;
00264   } else {
00265     QFileInfo fileInfo( mFileName );
00266     if ( readOnly() || !fileInfo.isWritable() ) {
00267       if ( !file.open( QIODevice::ReadOnly ) ) {
00268         return false;
00269       }
00270     } else {
00271       if ( !file.open( QIODevice::ReadWrite ) ) {
00272         return false;
00273       }
00274     }
00275 
00276     if ( file.size() == 0 ) {
00277       file.close();
00278       return true;
00279     }
00280 
00281     bool ok = mFormat->checkFormat( &file );
00282     file.close();
00283 
00284     return ok;
00285   }
00286 }
00287 
00288 void ResourceFile::doClose()
00289 {
00290 }
00291 
00292 bool ResourceFile::load()
00293 {
00294   kDebug() << mFileName << "'";
00295 
00296   mAsynchronous = false;
00297 
00298   QFile file( mFileName );
00299   if ( !file.open( QIODevice::ReadOnly ) ) {
00300     addressBook()->error( i18n( "Unable to open file '%1'.", mFileName ) );
00301     return false;
00302   }
00303 
00304   if ( !clearAndLoad( &file ) ) {
00305       addressBook()->error( i18n( "Problems during parsing file '%1'.", mFileName ) );
00306     return false;
00307   }
00308 
00309   return true;
00310 }
00311 
00312 bool ResourceFile::clearAndLoad( QFile *file )
00313 {
00314   clear();
00315 
00316   bool addresseesOk = mFormat->loadAll( addressBook(), this, file );
00317 
00318   bool listsOk = loadDistributionLists();
00319 
00320   if ( !addresseesOk || !listsOk )
00321     return false;
00322 
00323   bool importedVCardDistLists = false;
00324 
00325   // check if any of the loaded addressees is an old enterprise branch
00326   // workaround distlists
00327   Addressee::Map::Iterator addrIt    = mAddrMap.begin();
00328   Addressee::Map::Iterator addrEndIt = mAddrMap.end();
00329   while ( addrIt != addrEndIt) {
00330     Addressee addr = addrIt.value();
00331     if ( !isDistributionList( addr ) ) {
00332       ++addrIt;
00333       continue;
00334     }
00335 
00336     importedVCardDistLists = true;
00337 
00338     kWarning(5700) << "Addressee uid=" << addr.uid() << "contains the distlist"
00339                    << addr.formattedName();
00340 
00341     // remove the addressee from the map
00342     addrIt = mAddrMap.erase( addrIt );
00343 
00344     // parse the distlist entries
00345     const ParseList parseList = parseCustom( addr );
00346     if ( parseList.isEmpty() )
00347       continue;
00348 
00349     DistributionList *list = mDistListMap.value( addr.uid(), 0 );
00350     if ( list == 0 ) {
00351         list = new DistributionList( this, addr.uid(), addr.formattedName() );
00352         kDebug(5700) << "Created new distlist instance";
00353     } else {
00354         kDebug(5700) << "Adding entries to existing distlist instance";
00355     }
00356 
00357     MissingEntryList missingEntries;
00358     ParseList::ConstIterator it    = parseList.constBegin();
00359     ParseList::ConstIterator endIt = parseList.constEnd();
00360     for ( ; it != endIt; ++it ) {
00361       const QString uid = (*it).first;
00362       const QString email = (*it).second;
00363       // look up contact
00364       KABC::Addressee a = findByUidOrName( addressBook(), uid, email );
00365       if ( a.isEmpty() ) {
00366         missingEntries.append( qMakePair( uid, email ) );
00367       } else {
00368         list->insertEntry( a, email );
00369       }
00370     }
00371     d->mMissingEntries.insert( addr.formattedName(), missingEntries );
00372   }
00373 
00374   if ( importedVCardDistLists ) {
00375     mDirWatch.stopScan();
00376 
00377     KSaveFile saveFile( mFileName );
00378     if ( saveFile.open() ) {
00379         saveToFile( &saveFile );
00380         saveFile.finalize();
00381     }
00382 
00383     mDirWatch.startScan();
00384   }
00385 
00386   return true;
00387 }
00388 
00389 bool ResourceFile::asyncLoad()
00390 {
00391   mAsynchronous = true;
00392 
00393   load();
00394 
00395   QTimer::singleShot( 0, this, SLOT( emitLoadingFinished() ) );
00396 
00397   return true;
00398 }
00399 
00400 bool ResourceFile::save( Ticket *ticket )
00401 {
00402   Q_UNUSED( ticket );
00403   kDebug();
00404 
00405   // create backup file
00406   QString extension = '_' + QString::number( QDate::currentDate().dayOfWeek() );
00407   (void) KSaveFile::simpleBackupFile( mFileName, QString(), extension );
00408 
00409   mDirWatch.stopScan();
00410 
00411   KSaveFile saveFile( mFileName );
00412   bool ok = false;
00413 
00414   if ( saveFile.open() ) {
00415     saveToFile( &saveFile );
00416     ok = saveFile.finalize();
00417   }
00418 
00419   if ( !ok ) {
00420     addressBook()->error( i18n( "Unable to save file '%1'.", mFileName ) );
00421   }
00422 
00423   mDirWatch.startScan();
00424 
00425   return ok;
00426 }
00427 
00428 bool ResourceFile::asyncSave( Ticket *ticket )
00429 {
00430   kDebug();
00431 
00432   save( ticket );
00433 
00434   QTimer::singleShot( 0, this, SLOT( emitSavingFinished() ) );
00435 
00436   return true;
00437 }
00438 
00439 void ResourceFile::emitLoadingFinished()
00440 {
00441   emit loadingFinished( this );
00442 }
00443 
00444 void ResourceFile::emitSavingFinished()
00445 {
00446   emit savingFinished( this );
00447 }
00448 
00449 bool ResourceFile::loadDistributionLists()
00450 {
00451   KConfig cfg( KStandardDirs::locateLocal( "data", "kabc/distlists" ) );
00452 
00453   KConfigGroup cg( &cfg, "DistributionLists" );
00454   KConfigGroup cgId( &cfg, "DistributionLists-Identifiers" );
00455   const QStringList entryList = cg.keyList();
00456 
00457   d->mMissingEntries.clear();
00458 
00459   QStringList::ConstIterator it;
00460   for ( it = entryList.constBegin(); it != entryList.constEnd(); ++it ) {
00461     const QString name = *it;
00462     const QStringList value = cg.readEntry( name, QStringList() );
00463 
00464     kDebug() << name << ":" << value.join( "," );
00465 
00466     DistributionList *list = 0;
00467     if ( cgId.isValid() ) {
00468       const QString identifier = cgId.readEntry( name, QString() );
00469       if ( !identifier.isEmpty() )
00470         list = new DistributionList( this, identifier, name );
00471     }
00472 
00473     if ( list == 0 )
00474         list = new DistributionList( this, name );
00475 
00476     MissingEntryList missingEntries;
00477     QStringList::ConstIterator entryIt = value.constBegin();
00478     while ( entryIt != value.constEnd() ) {
00479       QString id = *entryIt++;
00480       QString email = entryIt != value.constEnd() ? *entryIt : QString();
00481       if ( email.isEmpty() && !email.isNull() ) {
00482         email = QString();
00483       }
00484 
00485       kDebug() << "----- Entry" << id;
00486 
00487       Addressee a = addressBook()->findByUid( id );
00488       if ( !a.isEmpty() ) {
00489         list->insertEntry( a, email );
00490       } else {
00491         missingEntries.append( qMakePair( id, email ) );
00492       }
00493 
00494       if ( entryIt == value.constEnd() ) {
00495         break;
00496       }
00497       ++entryIt;
00498     }
00499 
00500     d->mMissingEntries.insert( name, missingEntries );
00501   }
00502 
00503   return true;
00504 }
00505 
00506 void ResourceFile::saveDistributionLists()
00507 {
00508   kDebug();
00509 
00510   KConfig cfg( KStandardDirs::locateLocal( "data", "kabc/distlists" ) );
00511   KConfigGroup cg( &cfg, "DistributionLists" );
00512   cg.deleteGroup();
00513   KConfigGroup cgId( &cfg, "DistributionLists-Identifiers" );
00514   cgId.deleteGroup();
00515 
00516   QMapIterator<QString, DistributionList*> it( mDistListMap );
00517   while ( it.hasNext() ) {
00518     DistributionList *list = it.next().value();
00519     kDebug() << "  Saving '" << list->name() << "'";
00520 
00521     QStringList value;
00522     const DistributionList::Entry::List entries = list->entries();
00523     DistributionList::Entry::List::ConstIterator it;
00524     for ( it = entries.begin(); it != entries.end(); ++it ) {
00525       value.append( (*it).addressee().uid() );
00526       value.append( (*it).email() );
00527     }
00528 
00529     if ( d->mMissingEntries.find( list->name() ) != d->mMissingEntries.end() ) {
00530       const MissingEntryList missList = d->mMissingEntries[ list->name() ];
00531       MissingEntryList::ConstIterator missIt;
00532       for ( missIt = missList.begin(); missIt != missList.end(); ++missIt ) {
00533         value.append( (*missIt).first );
00534         value.append( (*missIt).second );
00535       }
00536     }
00537 
00538     cg.writeEntry( list->name(), value );
00539     cgId.writeEntry( list->name(), list->identifier() );
00540   }
00541 
00542   cfg.sync();
00543 }
00544 
00545 void ResourceFile::saveToFile( QFile *file )
00546 {
00547   mFormat->saveAll( addressBook(), this, file );
00548 
00549   saveDistributionLists();
00550 }
00551 
00552 void ResourceFile::setFileName( const QString &fileName )
00553 {
00554   mDirWatch.stopScan();
00555   if ( mDirWatch.contains( mFileName ) ) {
00556     mDirWatch.removeFile( mFileName );
00557   }
00558 
00559   mFileName = fileName;
00560 
00561   mDirWatch.addFile( mFileName );
00562   mDirWatch.startScan();
00563 }
00564 
00565 QString ResourceFile::fileName() const
00566 {
00567   return mFileName;
00568 }
00569 
00570 void ResourceFile::setFormat( const QString &format )
00571 {
00572   mFormatName = format;
00573   delete mFormat;
00574 
00575   FormatFactory *factory = FormatFactory::self();
00576   mFormat = factory->format( mFormatName );
00577 }
00578 
00579 QString ResourceFile::format() const
00580 {
00581   return mFormatName;
00582 }
00583 
00584 void ResourceFile::fileChanged( const QString &path )
00585 {
00586   kDebug() << path;
00587 
00588   if ( !addressBook() ) {
00589     return;
00590   }
00591 
00592   if ( path == KStandardDirs::locateLocal( "data", "kabc/distlists" ) ) {
00593     // clear old distribution lists
00594     // take a copy of mDistListMap, then clear it and finally qDeleteAll
00595     // the copy to avoid problems with removeDistributionList() called by
00596     // ~DistributionList().
00597     DistributionListMap tempDistListMap( mDistListMap );
00598     mDistListMap.clear();
00599     qDeleteAll( tempDistListMap );
00600 
00601     loadDistributionLists();
00602 
00603     kDebug() << "addressBookChanged()";
00604     addressBook()->emitAddressBookChanged();
00605 
00606     return;
00607   }
00608 
00609 //  clear(); // moved to clearAndLoad()
00610   if ( mAsynchronous ) {
00611     asyncLoad();
00612   } else {
00613     load();
00614     kDebug() << "addressBookChanged()";
00615     addressBook()->emitAddressBookChanged();
00616   }
00617 }
00618 
00619 void ResourceFile::removeAddressee( const Addressee &addr )
00620 {
00621   QFile::remove(
00622     QFile::encodeName( KStandardDirs::locateLocal( "data", "kabc/photos/" ) + addr.uid() ) );
00623 
00624   QFile::remove(
00625     QFile::encodeName( KStandardDirs::locateLocal( "data", "kabc/logos/" ) + addr.uid() ) );
00626 
00627   QFile::remove(
00628     QFile::encodeName( KStandardDirs::locateLocal( "data", "kabc/sounds/" ) + addr.uid() ) );
00629 
00630   mAddrMap.remove( addr.uid() );
00631 }
00632 
00633 #include "resourcefile.moc"

kabc

Skip menu "kabc"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  • kabc
  • kblog
  • kcal
  • kimap
  • kioslave
  •   imap4
  •   mbox
  • kldap
  • kmime
  • kpimidentities
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.5.7.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal