~ubuntu-branches/ubuntu/quantal/kdepim/quantal

« back to all changes in this revision

Viewing changes to kalarm/akonadi/kdepim-runtime/shared/singlefileresourcebase.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2011-12-15 14:17:51 UTC
  • mto: This revision was merged to the branch mainline in revision 193.
  • Revision ID: package-import@ubuntu.com-20111215141751-bmhdpiwl23wd9w26
Tags: upstream-4.7.90
ImportĀ upstreamĀ versionĀ 4.7.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
    Copyright (c) 2008 Bertjan Broeksema <broeksema@kde.org>
3
 
    Copyright (c) 2008 Volker Krause <vkrause@kde.org>
4
 
 
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.
9
 
 
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.
14
 
 
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
18
 
    02110-1301, USA.
19
 
*/
20
 
 
21
 
#include "singlefileresourcebase.h"
22
 
 
23
 
#include <akonadi/changerecorder.h>
24
 
#include <akonadi/entitydisplayattribute.h>
25
 
#include <akonadi/itemfetchscope.h>
26
 
 
27
 
#include <kio/job.h>
28
 
#include <kio/jobuidelegate.h>
29
 
#include <KDebug>
30
 
#include <KDirWatch>
31
 
#include <KLocale>
32
 
#include <KStandardDirs>
33
 
 
34
 
#include <QtCore/QDir>
35
 
#include <QtCore/QCryptographicHash>
36
 
 
37
 
using namespace Akonadi;
38
 
 
39
 
SingleFileResourceBase::SingleFileResourceBase( const QString & id )
40
 
  : ResourceBase( id ), mDownloadJob( 0 ), mUploadJob( 0 )
41
 
{
42
 
  connect( this, SIGNAL( reloadConfiguration() ), SLOT( reloadFile() ) );
43
 
  QTimer::singleShot( 0, this, SLOT( readFile() ) );
44
 
 
45
 
  changeRecorder()->itemFetchScope().fetchFullPayload();
46
 
  changeRecorder()->fetchCollection( true );
47
 
 
48
 
  connect( changeRecorder(), SIGNAL( changesAdded() ), SLOT( scheduleWrite() ) );
49
 
 
50
 
  connect( KDirWatch::self(), SIGNAL( dirty( QString ) ), SLOT( fileChanged( QString ) ) );
51
 
  connect( KDirWatch::self(), SIGNAL( created( QString ) ), SLOT( fileChanged( QString ) ) );
52
 
 
53
 
  KGlobal::locale()->insertCatalog( "akonadi_singlefile_resource" );
54
 
}
55
 
 
56
 
KSharedConfig::Ptr SingleFileResourceBase::runtimeConfig() const
57
 
{
58
 
  return KSharedConfig::openConfig( name() + "rc", KConfig::SimpleConfig, "cache" );
59
 
}
60
 
 
61
 
bool SingleFileResourceBase::readLocalFile( const QString &fileName )
62
 
{
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.
68
 
      handleHashChange();
69
 
    }
70
 
 
71
 
    if ( !readFromFile( fileName ) ) {
72
 
      mCurrentHash.clear();
73
 
      mCurrentUrl = KUrl(); // reset so we don't accidentally overwrite the file
74
 
      return false;
75
 
    }
76
 
 
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).
81
 
      saveHash( newHash );
82
 
    }
83
 
 
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.
88
 
    clearCache();
89
 
    synchronize();
90
 
  } else {
91
 
    // The hash didn't change, notify implementing resources about the
92
 
    // actual file name that should be used when reading the file is
93
 
    // necessary.
94
 
    setLocalFileName( fileName );
95
 
  }
96
 
 
97
 
  mCurrentHash = newHash;
98
 
  return true;
99
 
}
100
 
 
101
 
void SingleFileResourceBase::setLocalFileName( const QString &fileName )
102
 
{
103
 
  // Default implementation.
104
 
  if ( !readFromFile( fileName ) ) {
105
 
    mCurrentHash.clear();
106
 
    mCurrentUrl = KUrl(); // reset so we don't accidentally overwrite the file
107
 
    return;
108
 
  }
109
 
}
110
 
 
111
 
QString SingleFileResourceBase::cacheFile() const
112
 
{
113
 
  return KStandardDirs::locateLocal( "cache", "akonadi/" + identifier() );
114
 
}
115
 
 
116
 
QByteArray SingleFileResourceBase::calculateHash( const QString &fileName ) const
117
 
{
118
 
  QFile file( fileName );
119
 
  if ( !file.exists() )
120
 
    return QByteArray();
121
 
 
122
 
  if ( !file.open( QIODevice::ReadOnly ) )
123
 
    return QByteArray();
124
 
 
125
 
  QCryptographicHash hash( QCryptographicHash::Md5 );
126
 
  qint64 blockSize = 512 * 1024; // Read blocks of 512K
127
 
 
128
 
  while ( !file.atEnd() ) {
129
 
    hash.addData( file.read( blockSize ) );
130
 
  }
131
 
 
132
 
  file.close();
133
 
 
134
 
  return hash.result();
135
 
}
136
 
 
137
 
void SingleFileResourceBase::handleHashChange()
138
 
{
139
 
  // Default implementation does nothing.
140
 
  kDebug() << "The hash has changed.";
141
 
}
142
 
 
143
 
QByteArray SingleFileResourceBase::loadHash() const
144
 
{
145
 
  KConfigGroup generalGroup( runtimeConfig(), "General" );
146
 
  return QByteArray::fromHex( generalGroup.readEntry<QByteArray>( "hash", QByteArray() ) );
147
 
}
148
 
 
149
 
void SingleFileResourceBase::saveHash( const QByteArray &hash ) const
150
 
{
151
 
  KSharedConfig::Ptr config = runtimeConfig();
152
 
  KConfigGroup generalGroup( config, "General" );
153
 
  generalGroup.writeEntry( "hash", hash.toHex() );
154
 
  config->sync();
155
 
}
156
 
 
157
 
void SingleFileResourceBase::setSupportedMimetypes( const QStringList & mimeTypes, const QString &icon )
158
 
{
159
 
  mSupportedMimetypes = mimeTypes;
160
 
  mCollectionIcon = icon;
161
 
}
162
 
 
163
 
void SingleFileResourceBase::collectionChanged( const Akonadi::Collection & collection )
164
 
{
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();
170
 
  }
171
 
 
172
 
  if ( newName != name() )
173
 
    setName( newName );
174
 
 
175
 
  changeCommitted( collection );
176
 
}
177
 
 
178
 
void SingleFileResourceBase::reloadFile()
179
 
{
180
 
  // Update the network setting.
181
 
  setNeedsNetwork( !mCurrentUrl.isEmpty() && !mCurrentUrl.isLocalFile() );
182
 
 
183
 
  // if we have something loaded already, make sure we write that back in case
184
 
  // the settings changed
185
 
  if ( !mCurrentUrl.isEmpty() && !readOnly() )
186
 
    writeFile();
187
 
 
188
 
  readFile();
189
 
 
190
 
  // name or rights could have changed
191
 
  synchronizeCollectionTree();
192
 
}
193
 
 
194
 
void SingleFileResourceBase::handleProgress( KJob *, unsigned long pct )
195
 
{
196
 
  emit percent( pct );
197
 
}
198
 
 
199
 
void SingleFileResourceBase::fileChanged( const QString & fileName )
200
 
{
201
 
  if ( fileName != mCurrentUrl.toLocalFile() )
202
 
    return;
203
 
 
204
 
  const QByteArray newHash = calculateHash( fileName );
205
 
 
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 )
210
 
    return;
211
 
 
212
 
  if ( !mCurrentUrl.isEmpty() ) {
213
 
    QString lostFoundFileName;
214
 
    const KUrl prevUrl = mCurrentUrl;
215
 
    int i = 0;
216
 
    do {
217
 
      lostFoundFileName = KStandardDirs::locateLocal( "data", identifier() + QDir::separator()
218
 
          + prevUrl.fileName() + '-' + QString::number( ++i ) );
219
 
    } while ( KStandardDirs::exists( lostFoundFileName ) );
220
 
 
221
 
    // create the directory if it doesn't exist yet
222
 
    QDir dir = QFileInfo(lostFoundFileName).dir();
223
 
    if ( !dir.exists() )
224
 
      dir.mkpath( dir.path() );
225
 
 
226
 
    mCurrentUrl = KUrl( lostFoundFileName );
227
 
    writeFile();
228
 
    mCurrentUrl = prevUrl;
229
 
 
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() ) );
233
 
  }
234
 
 
235
 
  readFile();
236
 
 
237
 
  // Notify resources, so that information bound to the file like indexes etc.
238
 
  // can be updated.
239
 
  handleHashChange();
240
 
  clearCache();
241
 
  synchronize();
242
 
}
243
 
 
244
 
void SingleFileResourceBase::scheduleWrite()
245
 
{
246
 
  scheduleCustomTask(this, "writeFile", QVariant(true), ResourceBase::AfterChangeReplay);
247
 
}
248
 
 
249
 
void SingleFileResourceBase::slotDownloadJobResult( KJob *job )
250
 
{
251
 
  if ( job->error() && job->error() != KIO::ERR_DOES_NOT_EXIST ) {
252
 
    emit status( Broken, i18n( "Could not load file '%1'.", mCurrentUrl.prettyUrl() ) );
253
 
  } else {
254
 
    readLocalFile( KUrl( cacheFile() ).toLocalFile() );
255
 
  }
256
 
 
257
 
  mDownloadJob = 0;
258
 
  KGlobal::deref();
259
 
 
260
 
  emit status( Idle, i18nc( "@info:status", "Ready" ) );
261
 
}
262
 
 
263
 
void SingleFileResourceBase::slotUploadJobResult( KJob *job )
264
 
{
265
 
  if ( job->error() ) {
266
 
    emit status( Broken, i18n( "Could not save file '%1'.", mCurrentUrl.prettyUrl() ) );
267
 
  }
268
 
 
269
 
  mUploadJob = 0;
270
 
  KGlobal::deref();
271
 
 
272
 
  emit status( Idle, i18nc( "@info:status", "Ready" ) );
273
 
}
274
 
 
275
 
#include "singlefileresourcebase.moc"