00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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
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();
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();
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();
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
00316 QString extension = "_" + QString::number( QDate::currentDate().dayOfWeek() );
00317 (void) KSaveFile::backupFile( mFileName, QString::null ,
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();
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
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"