resourcefile.cpp
00001 /* 00002 This file is part of libkabc. 00003 00004 Copyright (c) 2001,2003 Cornelius Schumacher <schumacher@kde.org> 00005 Copyright (c) 2006 Tom Abers <tomalbers@kde.nl> 00006 00007 This library is free software; you can redistribute it and/or 00008 modify it under the terms of the GNU Library General Public 00009 License as published by the Free Software Foundation; either 00010 version 2 of the License, or (at your option) any later version. 00011 00012 This library is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 Library General Public License for more details. 00016 00017 You should have received a copy of the GNU Library General Public License 00018 along with this library; see the file COPYING.LIB. If not, write to 00019 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00020 Boston, MA 02110-1301, USA. 00021 */ 00022 00023 #include <signal.h> 00024 #include <sys/types.h> 00025 #include <sys/stat.h> 00026 #include <unistd.h> 00027 00028 #include <qfile.h> 00029 #include <qfileinfo.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 00040 #include "formatfactory.h" 00041 #include "resourcefileconfig.h" 00042 #include "stdaddressbook.h" 00043 #include "lock.h" 00044 00045 #include "resourcefile.h" 00046 00047 using namespace KABC; 00048 00049 ResourceFile::ResourceFile( const KConfig *config ) 00050 : Resource( config ), mFormat( 0 ), 00051 mAsynchronous( false ) 00052 { 00053 QString fileName, formatName; 00054 00055 if ( config ) { 00056 fileName = config->readPathEntry( "FileName", StdAddressBook::fileName() ); 00057 formatName = config->readEntry( "FileFormat", "vcard" ); 00058 } else { 00059 fileName = StdAddressBook::fileName(); 00060 formatName = "vcard"; 00061 } 00062 00063 init( fileName, formatName ); 00064 } 00065 00066 ResourceFile::ResourceFile( const QString &fileName, 00067 const QString &formatName ) 00068 : Resource( 0 ), mFormat( 0 ), 00069 mAsynchronous( false ) 00070 { 00071 init( fileName, formatName ); 00072 } 00073 00074 void ResourceFile::init( const QString &fileName, const QString &formatName ) 00075 { 00076 mFormatName = formatName; 00077 00078 FormatFactory *factory = FormatFactory::self(); 00079 mFormat = factory->format( mFormatName ); 00080 00081 if ( !mFormat ) { 00082 mFormatName = "vcard"; 00083 mFormat = factory->format( mFormatName ); 00084 } 00085 00086 connect( &mDirWatch, SIGNAL( dirty(const QString&) ), SLOT( fileChanged() ) ); 00087 connect( &mDirWatch, SIGNAL( created(const QString&) ), SLOT( fileChanged() ) ); 00088 connect( &mDirWatch, SIGNAL( deleted(const QString&) ), SLOT( fileChanged() ) ); 00089 00090 setFileName( fileName ); 00091 00092 mLock = 0; 00093 } 00094 00095 ResourceFile::~ResourceFile() 00096 { 00097 delete mFormat; 00098 mFormat = 0; 00099 } 00100 00101 void ResourceFile::writeConfig( KConfig *config ) 00102 { 00103 Resource::writeConfig( config ); 00104 00105 if ( mFileName == StdAddressBook::fileName() ) 00106 config->deleteEntry( "FileName" ); 00107 else 00108 config->writePathEntry( "FileName", mFileName ); 00109 00110 config->writeEntry( "FileFormat", mFormatName ); 00111 } 00112 00113 Ticket *ResourceFile::requestSaveTicket() 00114 { 00115 kdDebug(5700) << "ResourceFile::requestSaveTicket()" << endl; 00116 00117 if ( !addressBook() ) return 0; 00118 00119 delete mLock; 00120 mLock = new Lock( mFileName ); 00121 00122 if ( mLock->lock() ) { 00123 addressBook()->emitAddressBookLocked(); 00124 } else { 00125 addressBook()->error( mLock->error() ); 00126 kdDebug(5700) << "ResourceFile::requestSaveTicket(): Unable to lock file '" 00127 << mFileName << "': " << mLock->error() << endl; 00128 return 0; 00129 } 00130 00131 return createTicket( this ); 00132 } 00133 00134 void ResourceFile::releaseSaveTicket( Ticket *ticket ) 00135 { 00136 delete ticket; 00137 00138 delete mLock; 00139 mLock = 0; 00140 00141 addressBook()->emitAddressBookUnlocked(); 00142 } 00143 00144 bool ResourceFile::doOpen() 00145 { 00146 QFile file( mFileName ); 00147 00148 if ( !file.exists() ) { 00149 // try to create the file 00150 bool ok = file.open( IO_WriteOnly ); 00151 if ( ok ) 00152 file.close(); 00153 00154 return ok; 00155 } else { 00156 QFileInfo fileInfo( mFileName ); 00157 if ( readOnly() || !fileInfo.isWritable() ) { 00158 if ( !file.open( IO_ReadOnly ) ) 00159 return false; 00160 } else { 00161 if ( !file.open( IO_ReadWrite ) ) 00162 return false; 00163 } 00164 00165 if ( file.size() == 0 ) { 00166 file.close(); 00167 kdDebug() << "File size is zero. Evaluating backups" << endl; 00168 for (int i=0; i!=20; i++) 00169 { 00170 QFile backup( mFileName + "__" + QString::number(i) ); 00171 kdDebug() << "Evaluating" << backup.name() << " size: " << backup.size() << endl; 00172 if ( backup.size() != 0 ) 00173 { 00174 kdDebug() << "Restoring backup " << i << endl; 00175 const QString src = mFileName + "__" + QString::number(i); 00176 const QString dest = mFileName; 00177 00178 // remove dest 00179 QFile::remove( dest ); 00180 00181 // copy src to dest 00182 if ( backup.open( IO_ReadOnly ) ) { 00183 const QByteArray data = backup.readAll(); 00184 00185 QFile out( dest ); 00186 if ( out.open( IO_WriteOnly ) ) { 00187 out.writeBlock( data ); 00188 out.close(); 00189 } 00190 00191 backup.close(); 00192 } 00193 return true; 00194 } 00195 } 00196 return true; 00197 } 00198 00199 bool ok = mFormat->checkFormat( &file ); 00200 file.close(); 00201 00202 return ok; 00203 } 00204 } 00205 00206 void ResourceFile::doClose() 00207 { 00208 } 00209 00210 bool ResourceFile::load() 00211 { 00212 kdDebug(5700) << "ResourceFile::load(): '" << mFileName << "'" << endl; 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 clear(); 00223 00224 return mFormat->loadAll( addressBook(), this, &file ); 00225 } 00226 00227 bool ResourceFile::asyncLoad() 00228 { 00229 kdDebug(5700) << "ResourceFile::asyncLoad()" << endl; 00230 00231 mAsynchronous = true; 00232 00233 bool ok = load(); 00234 00235 if ( !ok ) 00236 emitLoadingError(); 00237 else 00238 emitLoadingFinished(); 00239 00240 return true; 00241 } 00242 00243 bool ResourceFile::save( Ticket * ) 00244 { 00245 kdDebug(5700) << "ResourceFile::save()" << endl; 00246 00247 // Only do the logrotate dance when the __0 file is not 0 bytes. 00248 QFile file( mFileName + "__0" ); 00249 if ( file.size() != 0 ) { 00250 const QString last = mFileName + "__20"; 00251 kdDebug() << "deleting " << last << endl; 00252 00253 QFile::remove( last ); 00254 00255 for (int i=19; i>=0; i--) 00256 { 00257 const QString src = mFileName + "__" + QString::number(i); 00258 const QString dest = mFileName + "__" + QString::number(i+1); 00259 kdDebug() << "moving " << src << " -> " << dest << endl; 00260 00261 // copy src to dest 00262 QFile in( src ); 00263 if ( in.open( IO_ReadOnly ) ) { 00264 const QByteArray data = in.readAll(); 00265 00266 QFile out( dest ); 00267 if ( out.open( IO_WriteOnly ) ) { 00268 out.writeBlock( data ); 00269 out.close(); 00270 } 00271 00272 in.close(); 00273 } 00274 00275 // remove src 00276 QFile::remove( src ); 00277 } 00278 } else 00279 kdDebug() << "Not starting logrotate __0 is 0 bytes." << endl; 00280 00281 QString extension = "__0"; 00282 (void) KSaveFile::backupFile( mFileName, QString::null /*directory*/, 00283 extension ); 00284 00285 mDirWatch.stopScan(); 00286 00287 KSaveFile saveFile( mFileName ); 00288 bool ok = false; 00289 00290 if ( saveFile.status() == 0 && saveFile.file() ) { 00291 mFormat->saveAll( addressBook(), this, saveFile.file() ); 00292 ok = saveFile.close(); 00293 } 00294 00295 if ( !ok ) { 00296 saveFile.abort(); 00297 addressBook()->error( i18n( "Unable to save file '%1'." ).arg( mFileName ) ); 00298 } 00299 00300 mDirWatch.startScan(); 00301 00302 return ok; 00303 } 00304 00305 bool ResourceFile::asyncSave( Ticket *ticket ) 00306 { 00307 kdDebug(5700) << "ResourceFile::asyncSave()" << endl; 00308 00309 bool ok = save( ticket ); 00310 00311 if ( !ok ) 00312 QTimer::singleShot( 0, this, SLOT( emitSavingError() ) ); 00313 else 00314 QTimer::singleShot( 0, this, SLOT( emitSavingFinished() ) ); 00315 00316 return ok; 00317 } 00318 00319 void ResourceFile::setFileName( const QString &fileName ) 00320 { 00321 mDirWatch.stopScan(); 00322 if ( mDirWatch.contains( mFileName ) ) 00323 mDirWatch.removeFile( mFileName ); 00324 00325 mFileName = fileName; 00326 00327 mDirWatch.addFile( mFileName ); 00328 mDirWatch.startScan(); 00329 } 00330 00331 QString ResourceFile::fileName() const 00332 { 00333 return mFileName; 00334 } 00335 00336 void ResourceFile::setFormat( const QString &format ) 00337 { 00338 mFormatName = format; 00339 delete mFormat; 00340 00341 FormatFactory *factory = FormatFactory::self(); 00342 mFormat = factory->format( mFormatName ); 00343 } 00344 00345 QString ResourceFile::format() const 00346 { 00347 return mFormatName; 00348 } 00349 00350 void ResourceFile::fileChanged() 00351 { 00352 kdDebug(5700) << "ResourceFile::fileChanged(): " << mFileName << endl; 00353 00354 if ( !addressBook() ) 00355 return; 00356 00357 if ( mAsynchronous ) 00358 asyncLoad(); 00359 else { 00360 load(); 00361 kdDebug() << "addressBookChanged() " << endl; 00362 addressBook()->emitAddressBookChanged(); 00363 } 00364 } 00365 00366 void ResourceFile::removeAddressee( const Addressee &addr ) 00367 { 00368 QFile::remove( QFile::encodeName( locateLocal( "data", "kabc/photos/" ) + addr.uid() ) ); 00369 QFile::remove( QFile::encodeName( locateLocal( "data", "kabc/logos/" ) + addr.uid() ) ); 00370 QFile::remove( QFile::encodeName( locateLocal( "data", "kabc/sounds/" ) + addr.uid() ) ); 00371 00372 mAddrMap.erase( addr.uid() ); 00373 } 00374 00375 void ResourceFile::emitSavingFinished() 00376 { 00377 emit savingFinished( this ); 00378 } 00379 00380 void ResourceFile::emitSavingError() 00381 { 00382 emit savingError( this, i18n( "Unable to save file '%1'." ).arg( mFileName ) ); 00383 } 00384 00385 void ResourceFile::emitLoadingFinished() 00386 { 00387 emit loadingFinished( this ); 00388 } 00389 00390 void ResourceFile::emitLoadingError() 00391 { 00392 emit loadingError( this, i18n( "Problems during parsing file '%1'." ).arg( mFileName ) ); 00393 } 00394 00395 #include "resourcefile.moc"