2
Copyright (c) 2008 Bertjan Broeksema <broeksema@kde.org>
3
Copyright (c) 2008 Volker Krause <vkrause@kde.org>
5
This library is free software; you can redistribute it and/or modify it
6
under the terms of the GNU Library General Public License as published by
7
the Free Software Foundation; either version 2 of the License, or (at your
8
option) any later version.
10
This library is distributed in the hope that it will be useful, but WITHOUT
11
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
13
License for more details.
15
You should have received a copy of the GNU Library General Public License
16
along with this library; see the file COPYING.LIB. If not, write to the
17
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21
#include "singlefileresourcebase.h"
23
#include <akonadi/changerecorder.h>
24
#include <akonadi/entitydisplayattribute.h>
25
#include <akonadi/itemfetchscope.h>
28
#include <kio/jobuidelegate.h>
32
#include <KStandardDirs>
34
#include <QtCore/QDir>
35
#include <QtCore/QCryptographicHash>
37
using namespace Akonadi;
39
SingleFileResourceBase::SingleFileResourceBase( const QString & id )
40
: ResourceBase( id ), mDownloadJob( 0 ), mUploadJob( 0 )
42
connect( this, SIGNAL( reloadConfiguration() ), SLOT( reloadFile() ) );
43
QTimer::singleShot( 0, this, SLOT( readFile() ) );
45
changeRecorder()->itemFetchScope().fetchFullPayload();
46
changeRecorder()->fetchCollection( true );
48
connect( changeRecorder(), SIGNAL( changesAdded() ), SLOT( scheduleWrite() ) );
50
connect( KDirWatch::self(), SIGNAL( dirty( QString ) ), SLOT( fileChanged( QString ) ) );
51
connect( KDirWatch::self(), SIGNAL( created( QString ) ), SLOT( fileChanged( QString ) ) );
53
KGlobal::locale()->insertCatalog( "akonadi_singlefile_resource" );
56
KSharedConfig::Ptr SingleFileResourceBase::runtimeConfig() const
58
return KSharedConfig::openConfig( name() + "rc", KConfig::SimpleConfig, "cache" );
61
bool SingleFileResourceBase::readLocalFile( const QString &fileName )
63
const QByteArray newHash = calculateHash( fileName );
64
if ( mCurrentHash != newHash ) {
65
if ( !mCurrentHash.isEmpty() ) {
66
// There was a hash stored in the config file or a chached one from
67
// a previous read and it is different from the hash we just read.
71
if ( !readFromFile( fileName ) ) {
73
mCurrentUrl = KUrl(); // reset so we don't accidentally overwrite the file
77
if ( mCurrentHash.isEmpty() ) {
78
// This is the very first time we read the file so make sure to store
79
// the hash as writeFile() might not be called at all (e.g in case of
80
// read only resources).
84
// Only synchronize when the contents of the file have changed wrt to
85
// the last time this file was read. Before we synchronize first
86
// clearCache is called to make sure that the cached items get the
87
// actual values as present in the file.
91
// The hash didn't change, notify implementing resources about the
92
// actual file name that should be used when reading the file is
94
setLocalFileName( fileName );
97
mCurrentHash = newHash;
101
void SingleFileResourceBase::setLocalFileName( const QString &fileName )
103
// Default implementation.
104
if ( !readFromFile( fileName ) ) {
105
mCurrentHash.clear();
106
mCurrentUrl = KUrl(); // reset so we don't accidentally overwrite the file
111
QString SingleFileResourceBase::cacheFile() const
113
return KStandardDirs::locateLocal( "cache", "akonadi/" + identifier() );
116
QByteArray SingleFileResourceBase::calculateHash( const QString &fileName ) const
118
QFile file( fileName );
119
if ( !file.exists() )
122
if ( !file.open( QIODevice::ReadOnly ) )
125
QCryptographicHash hash( QCryptographicHash::Md5 );
126
qint64 blockSize = 512 * 1024; // Read blocks of 512K
128
while ( !file.atEnd() ) {
129
hash.addData( file.read( blockSize ) );
134
return hash.result();
137
void SingleFileResourceBase::handleHashChange()
139
// Default implementation does nothing.
140
kDebug() << "The hash has changed.";
143
QByteArray SingleFileResourceBase::loadHash() const
145
KConfigGroup generalGroup( runtimeConfig(), "General" );
146
return QByteArray::fromHex( generalGroup.readEntry<QByteArray>( "hash", QByteArray() ) );
149
void SingleFileResourceBase::saveHash( const QByteArray &hash ) const
151
KSharedConfig::Ptr config = runtimeConfig();
152
KConfigGroup generalGroup( config, "General" );
153
generalGroup.writeEntry( "hash", hash.toHex() );
157
void SingleFileResourceBase::setSupportedMimetypes( const QStringList & mimeTypes, const QString &icon )
159
mSupportedMimetypes = mimeTypes;
160
mCollectionIcon = icon;
163
void SingleFileResourceBase::collectionChanged( const Akonadi::Collection & collection )
165
QString newName = collection.name();
166
if ( collection.hasAttribute<EntityDisplayAttribute>() ) {
167
EntityDisplayAttribute *attr = collection.attribute<EntityDisplayAttribute>();
168
if ( !attr->displayName().isEmpty() )
169
newName = attr->displayName();
172
if ( newName != name() )
175
changeCommitted( collection );
178
void SingleFileResourceBase::reloadFile()
180
// Update the network setting.
181
setNeedsNetwork( !mCurrentUrl.isEmpty() && !mCurrentUrl.isLocalFile() );
183
// if we have something loaded already, make sure we write that back in case
184
// the settings changed
185
if ( !mCurrentUrl.isEmpty() && !readOnly() )
190
// name or rights could have changed
191
synchronizeCollectionTree();
194
void SingleFileResourceBase::handleProgress( KJob *, unsigned long pct )
199
void SingleFileResourceBase::fileChanged( const QString & fileName )
201
if ( fileName != mCurrentUrl.toLocalFile() )
204
const QByteArray newHash = calculateHash( fileName );
206
// There is only a need to synchronize when the file was changed by another
207
// process. At this point we're sure that it is the file that the resource
208
// was configured for because of the check at the beginning of this function.
209
if ( newHash == mCurrentHash )
212
if ( !mCurrentUrl.isEmpty() ) {
213
QString lostFoundFileName;
214
const KUrl prevUrl = mCurrentUrl;
217
lostFoundFileName = KStandardDirs::locateLocal( "data", identifier() + QDir::separator()
218
+ prevUrl.fileName() + '-' + QString::number( ++i ) );
219
} while ( KStandardDirs::exists( lostFoundFileName ) );
221
// create the directory if it doesn't exist yet
222
QDir dir = QFileInfo(lostFoundFileName).dir();
224
dir.mkpath( dir.path() );
226
mCurrentUrl = KUrl( lostFoundFileName );
228
mCurrentUrl = prevUrl;
230
emit warning( i18n( "The file '%1' was changed on disk while there were still pending changes in Akonadi. "
231
"To avoid data loss, a backup of the internal changes has been created at '%2'.",
232
prevUrl.prettyUrl(), KUrl( lostFoundFileName ).prettyUrl() ) );
237
// Notify resources, so that information bound to the file like indexes etc.
244
void SingleFileResourceBase::scheduleWrite()
246
scheduleCustomTask(this, "writeFile", QVariant(true), ResourceBase::AfterChangeReplay);
249
void SingleFileResourceBase::slotDownloadJobResult( KJob *job )
251
if ( job->error() && job->error() != KIO::ERR_DOES_NOT_EXIST ) {
252
emit status( Broken, i18n( "Could not load file '%1'.", mCurrentUrl.prettyUrl() ) );
254
readLocalFile( KUrl( cacheFile() ).toLocalFile() );
260
emit status( Idle, i18nc( "@info:status", "Ready" ) );
263
void SingleFileResourceBase::slotUploadJobResult( KJob *job )
265
if ( job->error() ) {
266
emit status( Broken, i18n( "Could not save file '%1'.", mCurrentUrl.prettyUrl() ) );
272
emit status( Idle, i18nc( "@info:status", "Ready" ) );
275
#include "singlefileresourcebase.moc"