00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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
00058
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
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 );
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
00110
00111
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() ) {
00122 a = lst.first();
00123 }
00124 }
00125
00126
00127 if ( a.isEmpty() ) {
00128
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
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
00326
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
00342 addrIt = mAddrMap.erase( addrIt );
00343
00344
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
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
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
00594
00595
00596
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
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"