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 <signal.h>
00023 #include <sys/types.h>
00024 #include <sys/stat.h>
00025 #include <unistd.h>
00026 
00027 #include <qfile.h>
00028 #include <qfileinfo.h>
00029 #include <qregexp.h>
00030 #include <qtimer.h>
00031 
00032 #include <kapplication.h>
00033 #include <kconfig.h>
00034 #include <kdebug.h>
00035 #include <kio/scheduler.h>
00036 #include <klocale.h>
00037 #include <ksavefile.h>
00038 #include <kstandarddirs.h>
00039 #include <ktempfile.h>
00040 
00041 #include "formatfactory.h"
00042 #include "resourcefileconfig.h"
00043 #include "stdaddressbook.h"
00044 #include "lock.h"
00045 
00046 #include "resourcefile.h"
00047 
00048 using namespace KABC;
00049 
00050 class ResourceFile::ResourceFilePrivate
00051 {
00052   public:
00053     KIO::Job *mLoadJob;
00054     bool mIsLoading;
00055 
00056     KIO::Job *mSaveJob;
00057     bool mIsSaving;
00058 };
00059 
00060 ResourceFile::ResourceFile( const KConfig *config )
00061   : Resource( config ), mFormat( 0 ), mTempFile( 0 ),
00062     mAsynchronous( false ), d( new ResourceFilePrivate )
00063 {
00064   QString fileName, formatName;
00065 
00066   if ( config ) {
00067     fileName = config->readPathEntry( "FileName", StdAddressBook::fileName() );
00068     formatName = config->readEntry( "FileFormat", "vcard" );
00069   } else {
00070     fileName = StdAddressBook::fileName();
00071     formatName = "vcard";
00072   }
00073 
00074   init( fileName, formatName );
00075 }
00076 
00077 ResourceFile::ResourceFile( const QString &fileName,
00078                             const QString &formatName )
00079   : Resource( 0 ), mFormat( 0 ), mTempFile( 0 ),
00080     mAsynchronous( false ), d( new ResourceFilePrivate )
00081 {
00082   init( fileName, formatName );
00083 }
00084 
00085 void ResourceFile::init( const QString &fileName, const QString &formatName )
00086 {
00087   d->mLoadJob = 0;
00088   d->mIsLoading = false;
00089   d->mSaveJob = 0;
00090   d->mIsSaving = false;
00091 
00092   mFormatName = formatName;
00093 
00094   FormatFactory *factory = FormatFactory::self();
00095   mFormat = factory->format( mFormatName );
00096 
00097   if ( !mFormat ) {
00098     mFormatName = "vcard";
00099     mFormat = factory->format( mFormatName );
00100   }
00101 
00102   connect( &mDirWatch, SIGNAL( dirty(const QString&) ), SLOT( fileChanged() ) );
00103   connect( &mDirWatch, SIGNAL( created(const QString&) ), SLOT( fileChanged() ) );
00104   connect( &mDirWatch, SIGNAL( deleted(const QString&) ), SLOT( fileChanged() ) );
00105 
00106   setFileName( fileName );
00107 
00108   mLock = 0;
00109 }
00110 
00111 ResourceFile::~ResourceFile()
00112 {
00113   if ( d->mIsLoading )
00114     d->mLoadJob->kill();
00115   if ( d->mIsSaving )
00116     d->mSaveJob->kill();
00117 
00118   delete d;
00119   d = 0;
00120   delete mFormat;
00121   mFormat = 0;
00122 
00123   deleteLocalTempFile();
00124 }
00125 
00126 void ResourceFile::writeConfig( KConfig *config )
00127 {
00128   Resource::writeConfig( config );
00129 
00130   if ( mFileName == StdAddressBook::fileName() )
00131     config->deleteEntry( "FileName" );
00132   else
00133     config->writePathEntry( "FileName", mFileName );
00134 
00135   config->writeEntry( "FileFormat", mFormatName );
00136 }
00137 
00138 Ticket *ResourceFile::requestSaveTicket()
00139 {
00140   kdDebug(5700) << "ResourceFile::requestSaveTicket()" << endl;
00141 
00142   if ( !addressBook() ) return 0;
00143 
00144   delete mLock;
00145   mLock = new Lock( mFileName );
00146 
00147   if ( mLock->lock() ) {
00148     addressBook()->emitAddressBookLocked();
00149   } else {
00150     addressBook()->error( mLock->error() );
00151     kdDebug(5700) << "ResourceFile::requestSaveTicket(): Unable to lock file '"
00152                   << mFileName << "': " << mLock->error() << endl;
00153     return 0;
00154   }
00155 
00156   return createTicket( this );
00157 }
00158 
00159 void ResourceFile::releaseSaveTicket( Ticket *ticket )
00160 {
00161   delete ticket;
00162 
00163   delete mLock;
00164   mLock = 0;
00165 
00166   addressBook()->emitAddressBookUnlocked();
00167 }
00168 
00169 bool ResourceFile::doOpen()
00170 {
00171   QFile file( mFileName );
00172 
00173   if ( !file.exists() ) {
00174     // try to create the file
00175     bool ok = file.open( IO_WriteOnly );
00176     if ( ok )
00177       file.close();
00178 
00179     return ok;
00180   } else {
00181     QFileInfo fileInfo( mFileName );
00182     if ( readOnly() || !fileInfo.isWritable() ) {
00183       if ( !file.open( IO_ReadOnly ) )
00184         return false;
00185     } else {
00186       if ( !file.open( IO_ReadWrite ) )
00187         return false;
00188     }
00189 
00190     if ( file.size() == 0 ) {
00191       file.close();
00192       return true;
00193     }
00194 
00195     bool ok = mFormat->checkFormat( &file );
00196     file.close();
00197 
00198     return ok;
00199   }
00200 }
00201 
00202 void ResourceFile::doClose()
00203 {
00204 }
00205 
00206 bool ResourceFile::load()
00207 {
00208   kdDebug(5700) << "ResourceFile::load(): '" << mFileName << "'" << endl;
00209 
00210   if ( d->mIsLoading ) {
00211     abortAsyncLoading();
00212   }
00213 
00214   mAsynchronous = false;
00215 
00216   QFile file( mFileName );
00217   if ( !file.open( IO_ReadOnly ) ) {
00218     addressBook()->error( i18n( "Unable to open file '%1'." ).arg( mFileName ) );
00219     return false;
00220   }
00221 
00222   if ( !clearAndLoad( &file ) ) {
00223       addressBook()->error( i18n( "Problems during parsing file '%1'." ).arg( mFileName ) );
00224     return false;
00225   }
00226 
00227   return true;
00228 }
00229 
00230 bool ResourceFile::clearAndLoad( QFile *file )
00231 {
00232   clear();
00233   return mFormat->loadAll( addressBook(), this, file );
00234 }
00235 
00236 bool ResourceFile::asyncLoad()
00237 {
00238   if ( d->mIsLoading ) {
00239     abortAsyncLoading();
00240   }
00241 
00242   if (d->mIsSaving) {
00243     kdWarning(5700) << "Aborted asyncSave() because we're still asyncSave()ing!" << endl;
00244     return false;
00245   }
00246 
00247   mAsynchronous = true;
00248 
00249   bool ok = createLocalTempFile();
00250   if ( ok )
00251     ok = mTempFile->close(); // we only need the filename
00252 
00253   if ( !ok ) {
00254     emit loadingError( this, i18n( "Unable to open file '%1'." ).arg( mTempFile->name() ) );
00255     deleteLocalTempFile();
00256     return false;
00257   }
00258 
00259   KURL dest, src;
00260   dest.setPath( mTempFile->name() );
00261   src.setPath( mFileName );
00262 
00263   KIO::Scheduler::checkSlaveOnHold( true );
00264   d->mLoadJob = KIO::file_copy( src, dest, -1, true, false, false );
00265   d->mIsLoading = true;
00266   connect( d->mLoadJob, SIGNAL( result( KIO::Job* ) ),
00267            this, SLOT( downloadFinished( KIO::Job* ) ) );
00268 
00269   return true;
00270 }
00271 
00272 void ResourceFile::abortAsyncLoading()
00273 {
00274   kdDebug(5700) << "ResourceFile::abortAsyncLoading()" << endl;
00275 
00276   if ( d->mLoadJob ) {
00277     d->mLoadJob->kill(); // result not emitted
00278     d->mLoadJob = 0;
00279   }
00280 
00281   deleteLocalTempFile();
00282   d->mIsLoading = false;
00283 }
00284 
00285 void ResourceFile::abortAsyncSaving()
00286 {
00287   kdDebug(5700) << "ResourceFile::abortAsyncSaving()" << endl;
00288 
00289   if ( d->mSaveJob ) {
00290     d->mSaveJob->kill(); // result not emitted
00291     d->mSaveJob = 0;
00292   }
00293 
00294   deleteLocalTempFile();
00295   d->mIsSaving = false;
00296 }
00297 
00298 bool ResourceFile::save( Ticket * )
00299 {
00300   kdDebug(5700) << "ResourceFile::save()" << endl;
00301 
00302   if (d->mIsSaving) {
00303     abortAsyncSaving();
00304   }
00305   if ( d->mIsLoading ) {
00306     kdWarning(5700) << "Aborted save() because we're still asyncLoad()ing!" << endl;
00307     return false;
00308   }
00309 
00310   if ( d->mIsLoading ) { 
00311     kdWarning(5700) << "Aborted save() because we're still asyncLoad()ing!" << endl; 
00312     return false; 
00313   } 
00314 
00315   // create backup file
00316   QString extension = "_" + QString::number( QDate::currentDate().dayOfWeek() );
00317   (void) KSaveFile::backupFile( mFileName, QString::null /*directory*/,
00318                                 extension );
00319 
00320   mDirWatch.stopScan();
00321 
00322   KSaveFile saveFile( mFileName );
00323   bool ok = false;
00324 
00325   if ( saveFile.status() == 0 && saveFile.file() ) {
00326     saveToFile( saveFile.file() );
00327     ok = saveFile.close();
00328   }
00329 
00330   if ( !ok ) {
00331     saveFile.abort();
00332     addressBook()->error( i18n( "Unable to save file '%1'." ).arg( mFileName ) );
00333   }
00334 
00335   mDirWatch.startScan();
00336 
00337   return ok;
00338 }
00339 
00340 bool ResourceFile::asyncSave( Ticket * )
00341 {
00342   kdDebug(5700) << "ResourceFile::asyncSave()" << endl;
00343 
00344   if (d->mIsSaving) {
00345     abortAsyncSaving();
00346   }
00347 
00348   if (d->mIsLoading) {
00349     kdWarning(5700) << "Aborted asyncSave() because we're still asyncLoad()ing!" << endl;
00350     return false;
00351   }
00352 
00353   bool ok = createLocalTempFile();
00354   if ( ok ) {
00355     saveToFile( mTempFile->file() );
00356     ok = mTempFile->close();
00357   }
00358 
00359   if ( !ok ) {
00360     emit savingError( this, i18n( "Unable to save file '%1'." ).arg( mTempFile->name() ) );
00361     deleteLocalTempFile();
00362     return false;
00363   }
00364 
00365   KURL src, dest;
00366   src.setPath( mTempFile->name() );
00367   dest.setPath( mFileName );
00368 
00369   KIO::Scheduler::checkSlaveOnHold( true );
00370   d->mIsSaving = true;
00371   mDirWatch.stopScan(); // restarted in uploadFinished()
00372   d->mSaveJob = KIO::file_copy( src, dest, -1, true, false, false );
00373   connect( d->mSaveJob, SIGNAL( result( KIO::Job* ) ),
00374            this, SLOT( uploadFinished( KIO::Job* ) ) );
00375 
00376   return true;
00377 }
00378 
00379 bool ResourceFile::createLocalTempFile()
00380 {
00381   deleteStaleTempFile();
00382   mTempFile = new KTempFile();
00383   mTempFile->setAutoDelete( true );
00384   return mTempFile->status() == 0;
00385 }
00386 
00387 void ResourceFile::deleteStaleTempFile()
00388 {
00389   if ( hasTempFile() ) {
00390     kdDebug(5700) << "stale temp file detected " << mTempFile->name() << endl;
00391     deleteLocalTempFile();
00392   }
00393 }
00394 
00395 void ResourceFile::deleteLocalTempFile()
00396 {
00397   delete mTempFile;
00398   mTempFile = 0;
00399 }
00400 
00401 void ResourceFile::saveToFile( QFile *file )
00402 {
00403   mFormat->saveAll( addressBook(), this, file );
00404 }
00405 
00406 void ResourceFile::setFileName( const QString &fileName )
00407 {
00408   mDirWatch.stopScan();
00409   if ( mDirWatch.contains( mFileName ) )
00410     mDirWatch.removeFile( mFileName );
00411 
00412   mFileName = fileName;
00413 
00414   mDirWatch.addFile( mFileName );
00415   mDirWatch.startScan();
00416 }
00417 
00418 QString ResourceFile::fileName() const
00419 {
00420   return mFileName;
00421 }
00422 
00423 void ResourceFile::setFormat( const QString &format )
00424 {
00425   mFormatName = format;
00426   delete mFormat;
00427 
00428   FormatFactory *factory = FormatFactory::self();
00429   mFormat = factory->format( mFormatName );
00430 }
00431 
00432 QString ResourceFile::format() const
00433 {
00434   return mFormatName;
00435 }
00436 
00437 void ResourceFile::fileChanged()
00438 {
00439     kdDebug(5700) << "ResourceFile::fileChanged(): " << mFileName << endl;
00440 
00441   if ( !addressBook() )
00442     return;
00443 
00444 //  clear(); // moved to clearAndLoad()
00445   if ( mAsynchronous )
00446     asyncLoad();
00447   else {
00448     load();
00449     kdDebug() << "addressBookChanged() " << endl;
00450     addressBook()->emitAddressBookChanged();
00451   }
00452 }
00453 
00454 void ResourceFile::removeAddressee( const Addressee &addr )
00455 {
00456   QFile::remove( QFile::encodeName( locateLocal( "data", "kabc/photos/" ) + addr.uid() ) );
00457   QFile::remove( QFile::encodeName( locateLocal( "data", "kabc/logos/" ) + addr.uid() ) );
00458   QFile::remove( QFile::encodeName( locateLocal( "data", "kabc/sounds/" ) + addr.uid() ) );
00459 
00460   mAddrMap.erase( addr.uid() );
00461 }
00462 
00463 void ResourceFile::downloadFinished( KIO::Job* )
00464 {
00465   kdDebug(5700) << "ResourceFile::downloadFinished()" << endl;
00466 
00467   d->mIsLoading = false;
00468 
00469   if ( !hasTempFile() || mTempFile->status() != 0 ) {
00470     emit loadingError( this, i18n( "Download failed in some way!" ) );
00471     return;
00472   }
00473 
00474   QFile file( mTempFile->name() );
00475   if ( file.open( IO_ReadOnly ) ) {
00476     if ( clearAndLoad( &file ) )
00477       emit loadingFinished( this );
00478     else
00479       emit loadingError( this, i18n( "Problems during parsing file '%1'." ).arg( mTempFile->name() ) );
00480   }
00481   else {
00482     emit loadingError( this, i18n( "Unable to open file '%1'." ).arg( mTempFile->name() ) );
00483   }
00484 
00485   deleteLocalTempFile();
00486 }
00487 
00488 void ResourceFile::uploadFinished( KIO::Job *job )
00489 {
00490   kdDebug(5700) << "ResourceFile::uploadFinished()" << endl;
00491 
00492   d->mIsSaving = false;
00493 
00494   if ( job->error() )
00495     emit savingError( this, job->errorString() );
00496   else
00497     emit savingFinished( this );
00498 
00499   deleteLocalTempFile();
00500   mDirWatch.startScan();
00501 }
00502 
00503 #include "resourcefile.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys