~ubuntu-branches/ubuntu/lucid/kdepim-runtime/lucid

« back to all changes in this revision

Viewing changes to resources/imap/imapresource.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jonathan Thomas
  • Date: 2009-12-03 15:38:40 UTC
  • mfrom: (1.1.8 upstream)
  • Revision ID: james.westby@ubuntu.com-20091203153840-x5fxfsfby0czyqu6
Tags: 4:4.3.80-0ubuntu1
* New upstream beta release:
  - Refresh all patches
  - Bump build-depend versions
  - Remove build-depend on libknotificationitem-dev, it's part of
    kdelibs5-dev now
  - Add build-depend on shared-desktop-ontologies for nepomuk support
  - Add build-depend on libstreamanalyzer-dev for strigi support
  - Add build-depend on libx11-dev to prevent FTBFS
  - Update various .install files

Show diffs side-by-side

added added

removed removed

Lines of Context:
30
30
#include <QtCore/QDebug>
31
31
#include <QtDBus/QDBusConnection>
32
32
#include <QtNetwork/QSslSocket>
 
33
#include <QHostInfo>
33
34
 
34
35
#include <kdebug.h>
35
36
#include <klocale.h>
36
 
#include <kpassworddialog.h>
37
37
#include <kmessagebox.h>
38
38
#include <KWindowSystem>
39
39
#include <KAboutData>
40
40
 
 
41
#include <solid/networking.h>
 
42
 
41
43
#include <kimap/session.h>
42
44
#include <kimap/sessionuiproxy.h>
43
45
 
44
46
#include <kimap/appendjob.h>
45
47
#include <kimap/capabilitiesjob.h>
 
48
#include <kimap/copyjob.h>
46
49
#include <kimap/createjob.h>
47
50
#include <kimap/deletejob.h>
48
51
#include <kimap/expungejob.h>
55
58
#include <kimap/logoutjob.h>
56
59
#include <kimap/myrightsjob.h>
57
60
#include <kimap/renamejob.h>
 
61
#include <kimap/rfccodecs.h>
58
62
#include <kimap/selectjob.h>
 
63
#include <kimap/setacljob.h>
 
64
#include <kimap/setmetadatajob.h>
59
65
#include <kimap/storejob.h>
 
66
#include <kimap/subscribejob.h>
60
67
 
61
68
#include <kmime/kmime_message.h>
62
69
 
63
 
typedef boost::shared_ptr<KMime::Message> MessagePtr;
64
 
 
65
70
#include <akonadi/attributefactory.h>
66
71
#include <akonadi/cachepolicy.h>
67
72
#include <akonadi/collectionfetchjob.h>
68
73
#include <akonadi/collectionmodifyjob.h>
 
74
#include <akonadi/collectionquotaattribute.h>
69
75
#include <akonadi/collectionstatisticsjob.h>
70
76
#include <akonadi/collectionstatistics.h>
71
77
#include <akonadi/monitor.h>
72
78
#include <akonadi/changerecorder.h>
73
79
#include <akonadi/collectiondeletejob.h>
 
80
#include <akonadi/entitydisplayattribute.h>
74
81
#include <akonadi/itemdeletejob.h>
75
82
#include <akonadi/itemfetchjob.h>
76
83
#include <akonadi/itemfetchscope.h>
77
84
#include <akonadi/session.h>
78
85
#include <akonadi/transactionsequence.h>
79
 
 
 
86
#include <akonadi/collectionfetchscope.h>
 
87
 
 
88
#include <akonadi/kmime/messageparts.h>
 
89
 
 
90
#include "collectionannotationsattribute.h"
80
91
#include "collectionflagsattribute.h"
81
 
#include "collectionannotationsattribute.h"
 
92
 
82
93
#include "imapaclattribute.h"
83
94
#include "imapquotaattribute.h"
84
95
 
85
96
#include "imapaccount.h"
 
97
#include "imapidlemanager.h"
 
98
 
 
99
#include "resourceadaptor.h"
86
100
 
87
101
using namespace Akonadi;
88
102
 
 
103
static const char AKONADI_COLLECTION[] = "akonadiCollection";
 
104
static const char AKONADI_ITEM[] = "akonadiItem";
 
105
static const char AKONADI_PARTS[] = "akonadiParts";
 
106
static const char REPORTED_COLLECTIONS[] = "reportedCollections";
 
107
static const char PREVIOUS_REMOTEID[] = "previousRemoteId";
 
108
static const char SOURCE_COLLECTION[] = "sourceCollection";
 
109
static const char DESTINATION_COLLECTION[] = "destinationCollection";
 
110
 
89
111
ImapResource::ImapResource( const QString &id )
90
 
        :ResourceBase( id ), m_account( 0 )
 
112
        :ResourceBase( id ), m_account( 0 ), m_idle( 0 )
91
113
{
92
114
  Akonadi::AttributeFactory::registerAttribute<UidValidityAttribute>();
93
115
  Akonadi::AttributeFactory::registerAttribute<UidNextAttribute>();
94
116
  Akonadi::AttributeFactory::registerAttribute<NoSelectAttribute>();
 
117
 
 
118
  Akonadi::AttributeFactory::registerAttribute<CollectionAnnotationsAttribute>();
95
119
  Akonadi::AttributeFactory::registerAttribute<CollectionFlagsAttribute>();
96
 
  Akonadi::AttributeFactory::registerAttribute<CollectionAnnotationsAttribute>();
 
120
 
97
121
  Akonadi::AttributeFactory::registerAttribute<ImapAclAttribute>();
98
122
  Akonadi::AttributeFactory::registerAttribute<ImapQuotaAttribute>();
99
123
 
100
124
  changeRecorder()->fetchCollection( true );
 
125
  changeRecorder()->collectionFetchScope().setAncestorRetrieval( CollectionFetchScope::All );
101
126
  changeRecorder()->itemFetchScope().fetchFullPayload( true );
102
 
 
103
 
  connect( this, SIGNAL(reloadConfiguration()), SLOT(startConnect()) );
104
 
  startConnect();
 
127
  changeRecorder()->itemFetchScope().setAncestorRetrieval( ItemFetchScope::All );
 
128
 
 
129
  setHierarchicalRemoteIdentifiersEnabled( true );
 
130
 
 
131
  connect( this, SIGNAL(reloadConfiguration()), SLOT(reconnect()) );
 
132
 
 
133
  new ResourceAdaptor( this );
105
134
}
106
135
 
107
136
ImapResource::~ImapResource()
110
139
 
111
140
bool ImapResource::retrieveItem( const Akonadi::Item &item, const QSet<QByteArray> &parts )
112
141
{
113
 
    const QString remoteId = item.remoteId();
114
 
    const QStringList temp = remoteId.split( "-+-" );
115
 
    const QString mailBox = mailBoxForRemoteId( temp[0] );
116
 
    const qint64 uid = temp[1].toLongLong();
117
 
 
118
 
    KIMAP::SelectJob *select = new KIMAP::SelectJob( m_account->session() );
 
142
    Q_UNUSED( parts );
 
143
 
 
144
  if ( !isSessionAvailable() ) {
 
145
    kDebug() << "Ignoring this request. Probably there is no connection.";
 
146
    cancelTask( i18n( "There is currently no connection to the IMAP server." ) );
 
147
    return false;
 
148
  }
 
149
 
 
150
    const QString mailBox = mailBoxForCollection( item.parentCollection() );
 
151
    const qint64 uid = item.remoteId().toLongLong();
 
152
 
 
153
    KIMAP::SelectJob *select = new KIMAP::SelectJob( m_account->mainSession() );
119
154
    select->setMailBox( mailBox );
120
155
    select->start();
121
 
    KIMAP::FetchJob *fetch = new KIMAP::FetchJob( m_account->session() );
 
156
    KIMAP::FetchJob *fetch = new KIMAP::FetchJob( m_account->mainSession() );
122
157
    fetch->setProperty( "akonadiItem", QVariant::fromValue( item ) );
123
158
    KIMAP::FetchJob::FetchScope scope;
124
159
    fetch->setUidBased( true );
139
174
void ImapResource::onMessagesReceived( const QString &mailBox, const QMap<qint64, qint64> &uids,
140
175
                                       const QMap<qint64, KIMAP::MessagePtr> &messages )
141
176
{
 
177
  Q_UNUSED( mailBox );
 
178
 
142
179
  KIMAP::FetchJob *fetch = qobject_cast<KIMAP::FetchJob*>( sender() );
143
180
  Q_ASSERT( fetch!=0 );
144
181
  Q_ASSERT( uids.size()==1 );
146
183
 
147
184
  Item i = fetch->property( "akonadiItem" ).value<Item>();
148
185
 
149
 
  kDebug() << "MESSAGE from Imap server" << i.remoteId();
 
186
  kDebug(5327) << "MESSAGE from Imap server" << i.remoteId();
150
187
  Q_ASSERT( i.isValid() );
151
188
 
152
189
  KIMAP::MessagePtr message = messages[messages.keys().first()];
153
190
 
154
191
  i.setMimeType( "message/rfc822" );
155
 
  i.setPayload( MessagePtr( message ) );
 
192
  i.setPayload( KMime::Message::Ptr( message ) );
156
193
 
157
 
  kDebug() << "Has Payload: " << i.hasPayload();
158
 
  kDebug() << message->head().isEmpty() << message->body().isEmpty() << message->contents().isEmpty() << message->hasContent() << message->hasHeader("Message-ID");
 
194
  kDebug(5327) << "Has Payload: " << i.hasPayload();
 
195
  kDebug(5327) << message->head().isEmpty() << message->body().isEmpty() << message->contents().isEmpty() << message->hasContent() << message->hasHeader("Message-ID");
159
196
 
160
197
  itemRetrieved( i );
161
198
}
188
225
    setName( KGlobal::mainComponent().aboutData()->appName() );
189
226
  }
190
227
 
191
 
  startConnect();
 
228
  if ( dlg.result() == QDialog::Accepted ) {
 
229
    Settings::self()->writeConfig();
 
230
    reconnect();
 
231
 
 
232
    emit configurationDialogAccepted();
 
233
  } else {
 
234
    emit configurationDialogRejected();
 
235
  }
192
236
}
193
237
 
194
238
void ImapResource::startConnect( bool forceManualAuth )
195
239
{
196
240
  if ( Settings::self()->imapServer().isEmpty() ) {
197
 
    return;
198
 
  }
199
 
 
200
 
  QString password = Settings::self()->password();
201
 
 
202
 
  if ( password.isEmpty() || forceManualAuth ) {
203
 
    if ( !manualAuth( Settings::self()->userName(), password ) ) {
204
 
      return;
205
 
    }
206
 
  }
207
 
 
208
 
  delete m_account;
 
241
    emit status( Broken, i18n( "No server configured yet." ) );
 
242
    return;
 
243
  }
 
244
 
 
245
  connect( Settings::self(), SIGNAL(passwordRequestCompleted(QString, bool)),
 
246
           this, SLOT(onPasswordRequestCompleted(QString, bool)) );
 
247
  if ( forceManualAuth ) {
 
248
    Settings::self()->requestManualAuth();
 
249
  } else {
 
250
    Settings::self()->requestPassword();
 
251
  }
 
252
}
 
253
 
 
254
void ImapResource::onPasswordRequestCompleted( const QString &password, bool userRejected )
 
255
{
 
256
  disconnect( Settings::self(), SIGNAL(passwordRequestCompleted(QString, bool)),
 
257
              this, SLOT(onPasswordRequestCompleted(QString, bool)) );
 
258
 
 
259
  if ( userRejected ) {
 
260
    emit status( Broken, i18n( "Could not read the password: user rejected wallet access." ) );
 
261
    return;
 
262
  } else if ( password.isEmpty() ) {
 
263
    emit status( Broken, i18n( "Authentication failed." ) );
 
264
    return;
 
265
  } else {
 
266
    Settings::self()->setPassword( password );
 
267
  }
 
268
 
 
269
  if ( m_account!=0 ) {
 
270
    m_account->deleteLater();
 
271
    disconnect( m_account, 0, this, 0 );
 
272
  }
 
273
 
209
274
  m_account = new ImapAccount( Settings::self(), this );
210
275
 
211
 
  connect( m_account, SIGNAL( success() ),
212
 
           this, SLOT( onConnectSuccess() ) );
213
 
  connect( m_account, SIGNAL( error( int, const QString& ) ),
214
 
           this, SLOT( onConnectError( int, const QString& ) ) );
 
276
  connect( m_account, SIGNAL( success( KIMAP::Session* ) ),
 
277
           this, SLOT( onConnectSuccess( KIMAP::Session* ) ) );
 
278
  connect( m_account, SIGNAL( error( KIMAP::Session*, int, const QString& ) ),
 
279
           this, SLOT( onConnectError( KIMAP::Session*, int, const QString& ) ) );
215
280
 
216
281
  m_account->connect( password );
217
282
}
218
283
 
219
284
void ImapResource::itemAdded( const Item &item, const Collection &collection )
220
285
{
221
 
  const QString mailBox = mailBoxForRemoteId( collection.remoteId() );
 
286
  if ( !isSessionAvailable() ) {
 
287
    kDebug() << "Defering this request. Probably there is no connection.";
 
288
    deferTask();
 
289
    return;
 
290
  }
 
291
 
 
292
  if ( !item.hasPayload<KMime::Message::Ptr>() ) {
 
293
    changeProcessed();
 
294
    return;
 
295
  }
 
296
 
 
297
  const QString mailBox = mailBoxForCollection( collection );
 
298
 
 
299
  kDebug(5327) << "Got notification about item added for local id " << item.id() << " and remote id " << item.remoteId();
222
300
 
223
301
  // save message to the server.
224
 
  MessagePtr msg = item.payload<MessagePtr>();
 
302
  KMime::Message::Ptr msg = item.payload<KMime::Message::Ptr>();
225
303
 
226
 
  KIMAP::AppendJob *job = new KIMAP::AppendJob( m_account->session() );
227
 
  job->setProperty( "akonadiCollection", QVariant::fromValue( collection ) );
 
304
  KIMAP::AppendJob *job = new KIMAP::AppendJob( m_account->mainSession() );
 
305
  job->setProperty( AKONADI_COLLECTION, QVariant::fromValue( collection ) );
228
306
  job->setProperty( "akonadiItem", QVariant::fromValue( item ) );
229
307
  job->setMailBox( mailBox );
230
308
  job->setContent( msg->encodedContent( true ) );
236
314
{
237
315
  KIMAP::AppendJob *append = qobject_cast<KIMAP::AppendJob*>( job );
238
316
 
239
 
  const QString collectionRemoteId = remoteIdForMailBox( append->mailBox() );
240
317
  Item item = job->property( "akonadiItem" ).value<Item>();
241
318
 
242
319
  if ( append->error() ) {
 
320
    emit error( append->errorString() );
243
321
    deferTask();
 
322
    return;
244
323
  }
245
324
 
246
325
  qint64 uid = append->uid();
247
326
  Q_ASSERT( uid > 0 );
248
327
 
249
 
  const QString remoteId =  collectionRemoteId + "-+-" + QString::number( uid );
250
 
  kDebug() << "Setting remote ID to " << remoteId;
 
328
  const QString remoteId =  QString::number( uid );
 
329
  kDebug(5327) << "Setting remote ID to " << remoteId << " for item with local id " << item.id();
251
330
  item.setRemoteId( remoteId );
252
331
 
253
332
  changeCommitted( item );
257
336
  qint64 oldUid = job->property( "oldUid" ).toLongLong();
258
337
  if ( oldUid ) {
259
338
    // OK it's indeed a content change, so we've to mark the old version as deleted
260
 
    KIMAP::StoreJob *store = new KIMAP::StoreJob( m_account->session() );
 
339
    KIMAP::StoreJob *store = new KIMAP::StoreJob( m_account->mainSession() );
261
340
    store->setUidBased( true );
262
341
    store->setSequenceSet( KIMAP::ImapSet( oldUid ) );
263
342
    store->setFlags( QList<QByteArray>() << "\\Deleted" );
265
344
    store->start();
266
345
  }
267
346
 
268
 
  Collection collection = job->property( "akonadiCollection" ).value<Collection>();
269
 
  if ( !collection.isValid() ) {
270
 
    collection = collectionFromRemoteId( collectionRemoteId );
271
 
  }
 
347
  Collection collection = job->property( AKONADI_COLLECTION ).value<Collection>();
272
348
 
273
349
  // Get the current uid next value and store it
274
350
  UidNextAttribute *uidAttr = 0;
290
366
      uidAttr->setUidNext( uid+1 );
291
367
    }
292
368
 
293
 
    CollectionModifyJob *modify = new CollectionModifyJob( collection );
 
369
    new CollectionModifyJob( collection );
294
370
  }
295
371
}
296
372
 
297
373
void ImapResource::itemChanged( const Item &item, const QSet<QByteArray> &parts )
298
374
{
299
 
  kDebug() << item.remoteId() << parts;
300
 
 
301
 
  const QString remoteId = item.remoteId();
302
 
  const QStringList temp = remoteId.split( "-+-" );
303
 
  const QString mailBox = mailBoxForRemoteId( temp[0] );
304
 
  const qint64 uid = temp[1].toLongLong();
 
375
  kDebug(5327) << item.remoteId() << parts;
 
376
 
 
377
  if ( !isSessionAvailable() ) {
 
378
    kDebug() << "Defering this request. Probably there is no connection.";
 
379
    deferTask();
 
380
    return;
 
381
  }
 
382
 
 
383
  const QString mailBox = mailBoxForCollection( item.parentCollection() );
 
384
  const qint64 uid = item.remoteId().toLongLong();
305
385
 
306
386
  if ( parts.contains( "PLD:RFC822" ) ) {
 
387
    if ( !item.hasPayload<KMime::Message::Ptr>() ) {
 
388
      changeProcessed();
 
389
      return;
 
390
    }
307
391
    // save message to the server.
308
 
    MessagePtr msg = item.payload<MessagePtr>();
 
392
    KMime::Message::Ptr msg = item.payload<KMime::Message::Ptr>();
309
393
 
310
 
    KIMAP::AppendJob *job = new KIMAP::AppendJob( m_account->session() );
 
394
    KIMAP::AppendJob *job = new KIMAP::AppendJob( m_account->mainSession() );
311
395
    job->setProperty( "akonadiItem", QVariant::fromValue( item ) );
312
396
    job->setProperty( "oldUid", uid ); // Will be used in onAppendMessageDone
313
397
    job->setMailBox( mailBox );
317
401
    job->start();
318
402
 
319
403
  } else if ( parts.contains( "FLAGS" ) ) {
320
 
    KIMAP::SelectJob *select = new KIMAP::SelectJob( m_account->session() );
 
404
    KIMAP::SelectJob *select = new KIMAP::SelectJob( m_account->mainSession() );
321
405
    select->setMailBox( mailBox );
322
406
    select->start();
323
 
    KIMAP::StoreJob *store = new KIMAP::StoreJob( m_account->session() );
 
407
    KIMAP::StoreJob *store = new KIMAP::StoreJob( m_account->mainSession() );
324
408
    store->setProperty( "akonadiItem", QVariant::fromValue( item ) );
325
409
    store->setProperty( "itemUid", uid );
326
410
    store->setUidBased( true );
329
413
    store->setMode( KIMAP::StoreJob::SetFlags );
330
414
    connect( store, SIGNAL( result( KJob* ) ), SLOT( onStoreFlagsDone( KJob* ) ) );
331
415
    store->start();
 
416
  } else {
 
417
    changeProcessed();
332
418
  }
333
419
}
334
420
 
354
440
 
355
441
void ImapResource::itemRemoved( const Akonadi::Item &item )
356
442
{
 
443
  if ( !isSessionAvailable() ) {
 
444
    kDebug() << "Defering this request. Probably there is no connection.";
 
445
    deferTask();
 
446
    return;
 
447
  }
 
448
 
357
449
  // The imap specs do not allow for a single message to be deleted. We can only
358
450
  // set the \Deleted flag. The message will actually be deleted when EXPUNGE will
359
451
  // be issued on the next retrieveItems().
360
452
 
361
 
  const QString remoteId = item.remoteId();
362
 
  const QStringList temp = remoteId.split( "-+-" );
363
 
  const QString mailBox = mailBoxForRemoteId( temp[0] );
364
 
  const qint64 uid = temp[1].toLongLong();
 
453
  const QString mailBox = mailBoxForCollection( item.parentCollection() );
 
454
  const qint64 uid = item.remoteId().toLongLong();
365
455
 
366
 
  KIMAP::SelectJob *select = new KIMAP::SelectJob( m_account->session() );
 
456
  KIMAP::SelectJob *select = new KIMAP::SelectJob( m_account->mainSession() );
367
457
  select->setMailBox( mailBox );
368
458
  select->start();
369
 
  KIMAP::StoreJob *store = new KIMAP::StoreJob( m_account->session() );
 
459
  KIMAP::StoreJob *store = new KIMAP::StoreJob( m_account->mainSession() );
370
460
  store->setProperty( "akonadiItem", QVariant::fromValue( item ) );
371
461
  store->setProperty( "itemRemoval", true );
372
462
  store->setUidBased( true );
377
467
  store->start();
378
468
}
379
469
 
 
470
void ImapResource::itemMoved( const Akonadi::Item &item, const Akonadi::Collection &source,
 
471
                              const Akonadi::Collection &destination )
 
472
{
 
473
  if ( !isSessionAvailable() ) {
 
474
    kDebug() << "Defering this request. Probably there is no connection.";
 
475
    deferTask();
 
476
    return;
 
477
  }
 
478
 
 
479
  if ( item.remoteId().isEmpty() ) {
 
480
    emit error( i18n( "Cannot move message, it does not exist on the server." ) );
 
481
    changeProcessed();
 
482
    return;
 
483
  }
 
484
 
 
485
  if ( source.remoteId().isEmpty() ) {
 
486
    emit error( i18n( "Cannot move message out of '%1', '%1' does not exist on the server.",
 
487
                      source.name() ) );
 
488
    changeProcessed();
 
489
    return;
 
490
  }
 
491
 
 
492
  if ( destination.remoteId().isEmpty() ) {
 
493
    emit error( i18n( "Cannot move message to '%1', '%1' does not exist on the server.",
 
494
                      source.name() ) );
 
495
    changeProcessed();
 
496
    return;
 
497
  }
 
498
 
 
499
  const QString oldMailBox = mailBoxForCollection( source );
 
500
  const QString newMailBox = mailBoxForCollection( destination );
 
501
 
 
502
  if ( oldMailBox != newMailBox ) {
 
503
    KIMAP::SelectJob *select = new KIMAP::SelectJob( m_account->mainSession() );
 
504
    select->setMailBox( oldMailBox );
 
505
    select->setProperty( AKONADI_ITEM, QVariant::fromValue( item ) );
 
506
    select->setProperty( SOURCE_COLLECTION, QVariant::fromValue( source ) );
 
507
    select->setProperty( DESTINATION_COLLECTION, QVariant::fromValue( destination ) );
 
508
    connect( select, SIGNAL( result( KJob* ) ), SLOT( onPreItemMoveSelectDone( KJob* ) ) );
 
509
    select->start();
 
510
  } else {
 
511
    changeProcessed();
 
512
  }
 
513
}
 
514
 
 
515
void ImapResource::onPreItemMoveSelectDone( KJob *job )
 
516
{
 
517
  if ( !job->error() ) {
 
518
    Item item = job->property( AKONADI_ITEM ).value<Item>();
 
519
    const qint64 uid = item.remoteId().toLongLong();
 
520
 
 
521
    Collection destination = job->property( DESTINATION_COLLECTION ).value<Collection>();
 
522
    const QString newMailBox = mailBoxForCollection( destination );
 
523
 
 
524
    KIMAP::CopyJob *copy = new KIMAP::CopyJob( m_account->mainSession() );
 
525
    copy->setProperty( AKONADI_ITEM, job->property( AKONADI_ITEM ) );
 
526
    copy->setProperty( SOURCE_COLLECTION, job->property( SOURCE_COLLECTION ) );
 
527
    copy->setProperty( DESTINATION_COLLECTION, job->property( DESTINATION_COLLECTION ) );
 
528
    copy->setUidBased( true );
 
529
    copy->setSequenceSet( KIMAP::ImapSet( uid ) );
 
530
    copy->setMailBox( newMailBox );
 
531
    connect( copy, SIGNAL( result( KJob* ) ), SLOT( onCopyMessageDone( KJob* ) ) );
 
532
    copy->start();
 
533
 
 
534
  } else {
 
535
    const Collection source = job->property( SOURCE_COLLECTION ).value<Collection>();
 
536
    Q_ASSERT( source.isValid() );
 
537
    emit error( i18n( "Failed to move message out of '%1' on the IMAP server. Could not select '%1'.",
 
538
                      source.name() ) );
 
539
    changeProcessed();
 
540
  }
 
541
}
 
542
 
 
543
void ImapResource::onCopyMessageDone( KJob *job )
 
544
{
 
545
  if ( !job->error() ) {
 
546
    Item item = job->property( AKONADI_ITEM ).value<Item>();
 
547
    Collection destination = job->property( DESTINATION_COLLECTION ).value<Collection>();
 
548
    const qint64 oldUid = item.remoteId().toLongLong();
 
549
 
 
550
    // Go ahead, UIDPLUS is supposed to be supported and we copied a single message
 
551
    KIMAP::CopyJob *copy = static_cast<KIMAP::CopyJob*>( job );
 
552
    const qint64 newUid = copy->resultingUids().intervals().first().begin();
 
553
 
 
554
    // Update the item content with the new UID from the copy
 
555
    item.setRemoteId( QString::number( newUid ) );
 
556
    item.setParentCollection( destination );
 
557
 
 
558
    // Mark the old one ready for deletion
 
559
    KIMAP::StoreJob *store = new KIMAP::StoreJob( m_account->mainSession() );
 
560
    store->setProperty( AKONADI_ITEM, QVariant::fromValue( item ) );
 
561
    store->setProperty( SOURCE_COLLECTION, job->property( SOURCE_COLLECTION ) );
 
562
    store->setProperty( DESTINATION_COLLECTION, job->property( DESTINATION_COLLECTION ) );
 
563
    store->setUidBased( true );
 
564
    store->setSequenceSet( KIMAP::ImapSet( oldUid ) );
 
565
    store->setFlags( QList<QByteArray>() << "\\Deleted" );
 
566
    store->setMode( KIMAP::StoreJob::AppendFlags );
 
567
    connect( store, SIGNAL( result( KJob* ) ), SLOT( onPostItemMoveStoreFlagsDone( KJob* ) ) );
 
568
    store->start();
 
569
 
 
570
  } else {
 
571
    const Collection destination = job->property( DESTINATION_COLLECTION ).value<Collection>();
 
572
    Q_ASSERT( destination.isValid() );
 
573
    emit error( i18n( "Failed to move message to '%1' on the IMAP server. Could not copy into '%1'.",
 
574
                      destination.name() ) );
 
575
    changeProcessed();
 
576
  }
 
577
}
 
578
 
 
579
void ImapResource::onPostItemMoveStoreFlagsDone( KJob *job )
 
580
{
 
581
  Item item = job->property( AKONADI_ITEM ).value<Item>();
 
582
 
 
583
  if ( job->error() ) {
 
584
    const Collection source = job->property( SOURCE_COLLECTION ).value<Collection>();
 
585
    Q_ASSERT( source.isValid() );
 
586
    emit warning( i18n( "Failed to mark the message from '%1' for deletion on the IMAP server. "
 
587
                        "It will reappear on next sync.",
 
588
                        source.name() ) );
 
589
  }
 
590
 
 
591
  changeCommitted( item );
 
592
}
 
593
 
 
594
typedef QHash<QString, Collection> StringCollectionMap;
 
595
Q_DECLARE_METATYPE( StringCollectionMap )
 
596
 
380
597
void ImapResource::retrieveCollections()
381
598
{
382
 
  if ( !m_account || !m_account->session() ) {
383
 
    kDebug() << "Ignoring this request. Probably there is no connection.";
384
 
    cancelTask();
 
599
  if ( !isSessionAvailable() ) {
 
600
    kDebug(5327) << "Ignoring this request. Probably there is no connection.";
 
601
    cancelTask( i18n( "There is currently no connection to the IMAP server." ) );
 
602
    reconnect();
385
603
    return;
386
604
  }
387
605
 
389
607
  root.setName( m_account->server() + '/' + m_account->userName() );
390
608
  root.setRemoteId( rootRemoteId() );
391
609
  root.setContentMimeTypes( QStringList( Collection::mimeType() ) );
 
610
  root.setRights( Collection::ReadOnly );
 
611
  root.setParentCollection( Collection::root() );
 
612
  root.addAttribute( new NoSelectAttribute( true ) );
392
613
 
393
614
  CachePolicy policy;
394
615
  policy.setInheritFromParent( false );
395
616
  policy.setSyncOnDemand( true );
 
617
 
 
618
  QStringList localParts;
 
619
  localParts << Akonadi::MessagePart::Envelope
 
620
             << Akonadi::MessagePart::Header;
 
621
  int cacheTimeout = 60;
 
622
 
 
623
  if ( Settings::self()->disconnectedModeEnabled() ) {
 
624
    // For disconnected mode we also cache the body
 
625
    // and we keep all data indifinitely
 
626
    localParts << Akonadi::MessagePart::Body;
 
627
    cacheTimeout = -1;
 
628
  }
 
629
 
 
630
  policy.setLocalParts( localParts );
 
631
  policy.setCacheTimeout( cacheTimeout );
 
632
 
 
633
  policy.setIntervalCheckTime( Settings::self()->intervalCheckTime() );
 
634
 
396
635
  root.setCachePolicy( policy );
397
636
 
398
637
  setCollectionStreamingEnabled( true );
399
 
  collectionsRetrievedIncremental( Collection::List() << root, Collection::List() );
400
 
 
401
 
  KIMAP::ListJob *listJob = new KIMAP::ListJob( m_account->session() );
 
638
  collectionsRetrieved( Collection::List() << root );
 
639
 
 
640
  QHash<QString, Collection> reportedCollections;
 
641
  reportedCollections.insert( QString(), root );
 
642
 
 
643
  KIMAP::ListJob *listJob = new KIMAP::ListJob( m_account->mainSession() );
402
644
  listJob->setIncludeUnsubscribed( !m_account->isSubscriptionEnabled() );
 
645
  listJob->setQueriedNamespaces( m_account->namespaces() );
403
646
  connect( listJob, SIGNAL( mailBoxesReceived(QList<KIMAP::MailBoxDescriptor>, QList< QList<QByteArray> >) ),
404
647
           this, SLOT( onMailBoxesReceived(QList<KIMAP::MailBoxDescriptor>, QList< QList<QByteArray> >) ) );
405
648
  connect( listJob, SIGNAL(result(KJob*)), SLOT(onMailBoxesReceiveDone(KJob*)) );
 
649
  listJob->setProperty( REPORTED_COLLECTIONS, QVariant::fromValue<StringCollectionMap>( reportedCollections ) );
406
650
  listJob->start();
407
651
}
408
652
 
409
653
void ImapResource::onMailBoxesReceived( const QList< KIMAP::MailBoxDescriptor > &descriptors,
410
654
                                        const QList< QList<QByteArray> > &flags )
411
655
{
412
 
  QStringList reportedPaths = sender()->property("reportedPaths").toStringList();
 
656
  QHash<QString, Collection> reportedCollections = sender()->property( REPORTED_COLLECTIONS ).value< QHash<QString, Collection> >();
413
657
 
414
658
  Collection::List collections;
415
659
  QStringList contentTypes;
418
662
  for ( int i=0; i<descriptors.size(); ++i ) {
419
663
    KIMAP::MailBoxDescriptor descriptor = descriptors[i];
420
664
 
421
 
    QStringList pathParts = descriptor.name.split(descriptor.separator);
422
 
    QString separator = descriptor.separator;
 
665
    const QStringList pathParts = descriptor.name.split(descriptor.separator);
 
666
    const QString separator = descriptor.separator;
 
667
    Q_ASSERT( separator.size() == 1 ); // that's what the spec says
423
668
 
424
669
    QString parentPath;
425
670
    QString currentPath;
426
671
 
427
 
    foreach ( const QString &pathPart, pathParts ) {
428
 
      currentPath+='/'+pathPart;
429
 
      if ( currentPath.startsWith( '/' ) ) {
430
 
        currentPath.remove( 0, 1 );
431
 
      }
 
672
    for ( int j = 0; j < pathParts.size(); ++j ) {
 
673
      const bool isDummy = j != pathParts.size() - 1;
 
674
      const QString pathPart = pathParts.at( j );
 
675
      currentPath += separator + pathPart;
432
676
 
433
 
      if ( reportedPaths.contains( currentPath ) ) {
 
677
      if ( reportedCollections.contains( currentPath ) ) {
 
678
        if ( !isDummy )
 
679
          kWarning() << "Something is wrong here, we already have created a collection for" << currentPath;
434
680
        parentPath = currentPath;
435
681
        continue;
436
 
      } else {
437
 
        reportedPaths << currentPath;
438
682
      }
439
683
 
 
684
      const QList<QByteArray> currentFlags  = isDummy ? (QList<QByteArray>() << "\\NoSelect") : flags[i];
 
685
 
440
686
      Collection c;
441
687
      c.setName( pathPart );
442
 
      c.setRemoteId( remoteIdForMailBox( currentPath ) );
443
 
      c.setParentRemoteId( remoteIdForMailBox( parentPath ) );
444
 
      c.setRights( Collection::AllRights );
 
688
      c.setRemoteId( separator + pathPart );
 
689
      const Collection parentCollection = reportedCollections.value( parentPath );
 
690
      c.setParentCollection( parentCollection );
445
691
      c.setContentMimeTypes( contentTypes );
446
692
 
447
 
      CachePolicy cachePolicy;
448
 
      cachePolicy.setInheritFromParent( false );
449
 
      cachePolicy.setIntervalCheckTime( -1 );
450
 
      cachePolicy.setSyncOnDemand( true );
451
 
 
452
693
      // If the folder is the Inbox, make some special settings.
453
 
      if ( currentPath.compare( QLatin1String("INBOX") , Qt::CaseInsensitive ) == 0 ) {
454
 
        cachePolicy.setIntervalCheckTime( 1 );
455
 
        c.setName( "Inbox" );
 
694
      if ( currentPath.compare( separator + QLatin1String("INBOX") , Qt::CaseInsensitive ) == 0 ) {
 
695
        EntityDisplayAttribute *attr = c.attribute<EntityDisplayAttribute>( Collection::AddIfMissing );
 
696
        attr->setDisplayName( i18n( "Inbox" ) );
 
697
        attr->setIconName( "mail-folder-inbox" );
 
698
        QStringList ridPath;
 
699
        Collection *curCol = &c;
 
700
        while ( (*curCol) != Collection::root() && !curCol->remoteId().isEmpty() ) {
 
701
          ridPath.append( curCol->remoteId() );
 
702
          curCol = &curCol->parentCollection();
 
703
        }
 
704
        Settings::self()->setIdleRidPath( ridPath );
 
705
        Settings::self()->writeConfig();
 
706
        if ( !m_idle )
 
707
          startIdle();
 
708
      }
 
709
 
 
710
      // If the folder is the user top-level folder, mark it as well, even although it is not officially noted in the RFC
 
711
      if ( currentPath == (separator + QLatin1String( "user" )) && currentFlags.contains( "\\NoSelect" ) ) {
 
712
        EntityDisplayAttribute *attr = c.attribute<EntityDisplayAttribute>( Collection::AddIfMissing );
 
713
        attr->setDisplayName( i18n( "Shared Folders" ) );
 
714
        attr->setIconName( "x-mail-distribution-list" );
456
715
      }
457
716
 
458
717
      // If this folder is a noselect folder, make some special settings.
459
 
      if ( flags[i].contains( "\\NoSelect" ) ) {
460
 
        cachePolicy.setSyncOnDemand( false );
 
718
      if ( currentFlags.contains( "\\NoSelect" ) ) {
461
719
        c.addAttribute( new NoSelectAttribute( true ) );
 
720
        c.setContentMimeTypes( QStringList() << Collection::mimeType() );
 
721
        c.setRights( Collection::ReadOnly );
462
722
      }
463
723
 
464
 
      c.setCachePolicy( cachePolicy );
465
 
 
466
724
      collections << c;
 
725
 
 
726
      reportedCollections.insert( currentPath, c );
467
727
      parentPath = currentPath;
468
728
    }
469
729
  }
470
730
 
471
 
  sender()->setProperty("reportedPaths", reportedPaths);
472
 
  collectionsRetrievedIncremental( collections, Collection::List() );
 
731
  sender()->setProperty( REPORTED_COLLECTIONS, QVariant::fromValue<StringCollectionMap>( reportedCollections ) );
 
732
  collectionsRetrieved( collections );
 
733
 
 
734
  if ( Settings::self()->retrieveMetadataOnFolderListing() ) {
 
735
    foreach ( const Collection &c, collections ) {
 
736
      triggerCollectionExtraInfoJobs( c );
 
737
    }
 
738
  }
473
739
}
474
740
 
475
741
void ImapResource::onMailBoxesReceiveDone(KJob* job)
476
742
{
477
743
  // TODO error handling
 
744
  Q_UNUSED( job );
478
745
  collectionsRetrievalDone();
479
746
}
480
747
 
481
748
// ----------------------------------------------------------------------------------
482
749
 
483
 
void ImapResource::retrieveItems( const Collection &col )
 
750
void ImapResource::triggerCollectionExtraInfoJobs( const Collection &collection )
484
751
{
485
 
  kDebug( ) << col.remoteId();
486
 
 
487
 
  // Prevent fetching items from noselect folders.
488
 
  if ( col.hasAttribute( "noselect" ) ) {
489
 
    NoSelectAttribute* noselect = static_cast<NoSelectAttribute*>( col.attribute( "noselect" ) );
490
 
    if ( noselect->noSelect() ) {
491
 
      kDebug() << "No Select folder";
492
 
      itemsRetrievalDone();
493
 
      return;
494
 
    }
495
 
  }
496
 
 
497
 
  const QString mailBox = mailBoxForRemoteId( col.remoteId() );
 
752
  const QString mailBox = mailBoxForCollection( collection );
498
753
  const QStringList capabilities = m_account->capabilities();
499
754
 
500
755
  // First get the annotations from the mailbox if it's supported
501
756
  if ( capabilities.contains( "METADATA" ) || capabilities.contains( "ANNOTATEMORE" ) ) {
502
 
    KIMAP::GetMetaDataJob *meta = new KIMAP::GetMetaDataJob( m_account->session() );
503
 
    meta->setProperty( "akonadiCollection", QVariant::fromValue( col ) );
 
757
    KIMAP::GetMetaDataJob *meta = new KIMAP::GetMetaDataJob( m_account->mainSession() );
 
758
    meta->setProperty( AKONADI_COLLECTION, QVariant::fromValue( collection ) );
504
759
    meta->setMailBox( mailBox );
505
760
    if ( capabilities.contains( "METADATA" ) ) {
506
761
      meta->setServerCapability( KIMAP::MetaDataJobBase::Metadata );
515
770
 
516
771
  // Get the ACLs from the mailbox if it's supported
517
772
  if ( capabilities.contains( "ACL" ) ) {
518
 
    KIMAP::GetAclJob *acl = new KIMAP::GetAclJob( m_account->session() );
519
 
    acl->setProperty( "akonadiCollection", QVariant::fromValue( col ) );
 
773
    KIMAP::GetAclJob *acl = new KIMAP::GetAclJob( m_account->mainSession() );
 
774
    acl->setProperty( AKONADI_COLLECTION, QVariant::fromValue( collection ) );
520
775
    acl->setMailBox( mailBox );
521
776
    connect( acl, SIGNAL( result( KJob* ) ), SLOT( onGetAclDone( KJob* ) ) );
522
777
    acl->start();
523
778
 
524
 
    KIMAP::MyRightsJob *rights = new KIMAP::MyRightsJob( m_account->session() );
525
 
    rights->setProperty( "akonadiCollection", QVariant::fromValue( col ) );
 
779
    KIMAP::MyRightsJob *rights = new KIMAP::MyRightsJob( m_account->mainSession() );
 
780
    rights->setProperty( AKONADI_COLLECTION, QVariant::fromValue( collection ) );
526
781
    rights->setMailBox( mailBox );
527
782
    connect( rights, SIGNAL( result( KJob* ) ), SLOT( onRightsReceived( KJob* ) ) );
528
783
    rights->start();
530
785
 
531
786
  // Get the QUOTA info from the mailbox if it's supported
532
787
  if ( capabilities.contains( "QUOTA" ) ) {
533
 
    KIMAP::GetQuotaRootJob *quota = new KIMAP::GetQuotaRootJob( m_account->session() );
534
 
    quota->setProperty( "akonadiCollection", QVariant::fromValue( col ) );
 
788
    KIMAP::GetQuotaRootJob *quota = new KIMAP::GetQuotaRootJob( m_account->mainSession() );
 
789
    quota->setProperty( AKONADI_COLLECTION, QVariant::fromValue( collection ) );
535
790
    quota->setMailBox( mailBox );
536
791
    connect( quota, SIGNAL( result( KJob* ) ), SLOT( onQuotasReceived( KJob* ) ) );
537
792
    quota->start();
538
793
  }
 
794
}
 
795
 
 
796
void ImapResource::retrieveItems( const Collection &col )
 
797
{
 
798
  if ( !isSessionAvailable() ) {
 
799
    kDebug() << "Ignoring this request. Probably there is no connection.";
 
800
    cancelTask( i18n( "There is currently no connection to the IMAP server." ) );
 
801
    return;
 
802
  }
 
803
 
 
804
  kDebug(5327) << col.remoteId();
 
805
 
 
806
  // Prevent fetching items from noselect folders.
 
807
  if ( col.hasAttribute( "noselect" ) ) {
 
808
    NoSelectAttribute* noselect = static_cast<NoSelectAttribute*>( col.attribute( "noselect" ) );
 
809
    if ( noselect->noSelect() ) {
 
810
      kDebug(5327) << "No Select folder";
 
811
      itemsRetrievalDone();
 
812
      return;
 
813
    }
 
814
  }
 
815
 
 
816
  triggerCollectionExtraInfoJobs( col );
 
817
 
 
818
  const QString mailBox = mailBoxForCollection( col );
539
819
 
540
820
  // Now is the right time to expunge the messages marked \\Deleted from this mailbox.
541
 
  KIMAP::SelectJob *select = new KIMAP::SelectJob( m_account->session() );
542
 
  select->setMailBox( mailBox );
543
 
  select->start();
544
 
  KIMAP::ExpungeJob *expunge = new KIMAP::ExpungeJob( m_account->session() );
545
 
  expunge->start();
 
821
  if ( Settings::self()->automaticExpungeEnabled() ) {
 
822
    triggerExpunge( mailBox );
 
823
  }
546
824
 
547
825
  // Issue another select to get the updated info from the mailbox
548
 
  select = new KIMAP::SelectJob( m_account->session() );
 
826
  KIMAP::SelectJob *select = new KIMAP::SelectJob( m_account->mainSession() );
 
827
  select->setProperty( AKONADI_COLLECTION, QVariant::fromValue( col ) );
549
828
  select->setMailBox( mailBox );
550
829
  connect( select, SIGNAL( result( KJob* ) ),
551
830
           this, SLOT( onSelectDone( KJob* ) ) );
552
831
  select->start();
553
832
}
554
833
 
 
834
void ImapResource::triggerExpunge( const QString &mailBox )
 
835
{
 
836
  kDebug(5327) << mailBox;
 
837
 
 
838
  KIMAP::SelectJob *select = new KIMAP::SelectJob( m_account->mainSession() );
 
839
  select->setMailBox( mailBox );
 
840
  select->start();
 
841
 
 
842
  KIMAP::ExpungeJob *expunge = new KIMAP::ExpungeJob( m_account->mainSession() );
 
843
  expunge->start();
 
844
}
 
845
 
555
846
void ImapResource::onHeadersReceived( const QString &mailBox, const QMap<qint64, qint64> &uids,
556
847
                                      const QMap<qint64, qint64> &sizes,
557
848
                                      const QMap<qint64, KIMAP::MessageFlags> &flags,
558
849
                                      const QMap<qint64, KIMAP::MessagePtr> &messages )
559
850
{
 
851
  Q_UNUSED( mailBox );
 
852
 
560
853
  Item::List addedItems;
561
854
 
562
855
  foreach ( qint64 number, uids.keys() ) {
563
856
    Akonadi::Item i;
564
 
    i.setRemoteId( remoteIdForMailBox( mailBox ) + "-+-" + QString::number( uids[number] ) );
 
857
    i.setRemoteId( QString::number( uids[number] ) );
565
858
    i.setMimeType( "message/rfc822" );
566
 
    i.setPayload( MessagePtr( messages[number] ) );
 
859
    i.setPayload( KMime::Message::Ptr( messages[number] ) );
567
860
    i.setSize( sizes[number] );
568
861
 
569
862
    foreach( const QByteArray &flag, flags[number] ) {
570
863
      i.setFlag( flag );
571
864
    }
572
 
    kDebug() << "Flags: " << i.flags();
 
865
    kDebug(5327) << "Flags: " << i.flags();
573
866
    addedItems << i;
574
867
  }
575
868
 
576
 
  itemsRetrievedIncremental( addedItems, Item::List() );
577
 
}
578
 
 
579
 
void ImapResource::onHeadersFetchDone( KJob */*job*/ )
 
869
  if ( sender()->property( "nonIncremental" ).toBool() ) {
 
870
    itemsRetrieved( addedItems );
 
871
  } else {
 
872
    itemsRetrievedIncremental( addedItems, Item::List() );
 
873
  }
 
874
}
 
875
 
 
876
void ImapResource::onHeadersFetchDone( KJob *job )
 
877
{
 
878
  if ( job->property( "nonIncremental" ).toBool() ) {
 
879
    itemsRetrievalDone();
 
880
  } else {
 
881
 
 
882
    KIMAP::FetchJob *fetch = static_cast<KIMAP::FetchJob*>( job );
 
883
    KIMAP::ImapSet alreadyFetched = fetch->sequenceSet();
 
884
 
 
885
    KIMAP::FetchJob::FetchScope scope;
 
886
    scope.parts.clear();
 
887
    scope.mode = KIMAP::FetchJob::FetchScope::Flags;
 
888
 
 
889
    fetch = new KIMAP::FetchJob( m_account->mainSession() );
 
890
    fetch->setSequenceSet( KIMAP::ImapSet( 1, alreadyFetched.intervals().first().begin()-1 ) );
 
891
    fetch->setScope( scope );
 
892
    connect( fetch, SIGNAL( headersReceived( QString, QMap<qint64, qint64>, QMap<qint64, qint64>,
 
893
                                             QMap<qint64, KIMAP::MessageFlags>, QMap<qint64, KIMAP::MessagePtr> ) ),
 
894
             this, SLOT( onFlagsReceived( QString, QMap<qint64, qint64>, QMap<qint64, qint64>,
 
895
                                          QMap<qint64, KIMAP::MessageFlags>, QMap<qint64, KIMAP::MessagePtr> ) ) );
 
896
    connect( fetch, SIGNAL( result( KJob* ) ),
 
897
             this, SLOT( onFlagsFetchDone( KJob* ) ) );
 
898
    fetch->start();
 
899
  }
 
900
}
 
901
 
 
902
void ImapResource::onFlagsReceived( const QString &mailBox, const QMap<qint64, qint64> &uids,
 
903
                                    const QMap<qint64, qint64> &sizes,
 
904
                                    const QMap<qint64, KIMAP::MessageFlags> &flags,
 
905
                                    const QMap<qint64, KIMAP::MessagePtr> &messages )
 
906
{
 
907
  Q_UNUSED( mailBox );
 
908
 
 
909
  Item::List changedItems;
 
910
 
 
911
  foreach ( qint64 number, uids.keys() ) {
 
912
    Akonadi::Item i;
 
913
    i.setRemoteId( QString::number( uids[number] ) );
 
914
    i.setMimeType( "message/rfc822" );
 
915
    i.setFlags( Akonadi::Item::Flags::fromList( flags[number] ) );
 
916
 
 
917
    kDebug(5327) << "Flags: " << i.flags();
 
918
    changedItems << i;
 
919
  }
 
920
 
 
921
  itemsRetrievedIncremental( changedItems, Item::List() );
 
922
}
 
923
 
 
924
void ImapResource::onFlagsFetchDone( KJob * /*job*/ )
580
925
{
581
926
  itemsRetrievalDone();
582
927
}
583
928
 
584
 
 
585
929
// ----------------------------------------------------------------------------------
586
930
 
587
931
void ImapResource::collectionAdded( const Collection & collection, const Collection &parent )
588
932
{
589
 
  const QString remoteName = parent.remoteId() + '/' + collection.name();
590
 
 
591
 
  kDebug( ) << "New folder: " << remoteName;
 
933
  if ( !isSessionAvailable() ) {
 
934
    kDebug() << "Defering this request. Probably there is no connection.";
 
935
    deferTask();
 
936
    return;
 
937
  }
 
938
 
 
939
  if ( parent.remoteId().isEmpty() ) {
 
940
    emit error( i18n("Cannot add IMAP folder '%1' for a non-existing parent folder '%2'.", collection.name(), parent.name() ) );
 
941
    changeProcessed();
 
942
    return;
 
943
  }
 
944
 
 
945
  QString newMailBox = mailBoxForCollection( parent );
 
946
  if ( !newMailBox.isEmpty() )
 
947
    newMailBox += parent.remoteId().at( 0 ); // separator for non-toplevel mailboxes
 
948
  newMailBox += collection.name();
 
949
 
 
950
  kDebug(5327) << "New folder: " << newMailBox;
592
951
 
593
952
  Collection c = collection;
594
 
  c.setRemoteId( remoteName );
595
 
 
596
 
  const QString mailBox = mailBoxForRemoteId( remoteName );
597
 
 
598
 
  KIMAP::CreateJob *job = new KIMAP::CreateJob( m_account->session() );
599
 
  job->setProperty( "akonadiCollection", QVariant::fromValue( c ) );
600
 
  job->setMailBox( mailBox );
 
953
  c.setRemoteId( parent.remoteId().at( 0 ) + collection.name() );
 
954
 
 
955
  KIMAP::CreateJob *job = new KIMAP::CreateJob( m_account->mainSession() );
 
956
  job->setProperty( AKONADI_COLLECTION, QVariant::fromValue( c ) );
 
957
  job->setMailBox( newMailBox );
601
958
  connect( job, SIGNAL( result( KJob* ) ), SLOT( onCreateMailBoxDone( KJob* ) ) );
602
959
  job->start();
603
960
}
604
961
 
605
962
void ImapResource::onCreateMailBoxDone( KJob *job )
606
963
{
607
 
  Collection collection = job->property( "akonadiCollection" ).value<Collection>();
 
964
  const Collection collection = job->property( AKONADI_COLLECTION ).value<Collection>();
 
965
 
 
966
  // Automatically subscribe to newly created mailbox
 
967
  KIMAP::CreateJob *create = static_cast<KIMAP::CreateJob*>( job );
 
968
  KIMAP::SubscribeJob *subscribe = new KIMAP::SubscribeJob( m_account->mainSession() );
 
969
  subscribe->setMailBox( create->mailBox() );
 
970
  subscribe->start();
608
971
 
609
972
  if ( !job->error() ) {
610
973
    changeCommitted( collection );
611
974
  } else {
612
 
    // remove the collection again.
613
 
    kDebug() << "Failed to create the folder, deleting it in akonadi again";
614
 
    emit warning( i18n( "Failed to create the folder, restoring folder list." ) );
615
 
    new CollectionDeleteJob( collection, this );
616
 
  }
617
 
}
618
 
 
619
 
void ImapResource::collectionChanged( const Collection & collection )
620
 
{
621
 
  QString oldRemoteId = collection.remoteId();
622
 
  QString parentRemoteId = oldRemoteId.mid( 0, oldRemoteId.lastIndexOf('/') );
623
 
 
624
 
  QString newRemoteId = parentRemoteId + '/' + collection.name();
625
 
 
626
 
  Collection c = collection;
627
 
  c.setRemoteId( newRemoteId );
628
 
 
629
 
  const QString oldMailBox = mailBoxForRemoteId( oldRemoteId );
630
 
  const QString newMailBox = mailBoxForRemoteId( newRemoteId );
631
 
 
632
 
  KIMAP::RenameJob *job = new KIMAP::RenameJob( m_account->session() );
633
 
  job->setProperty( "akonadiCollection", QVariant::fromValue( c ) );
634
 
  job->setSourceMailBox( oldMailBox );
635
 
  job->setDestinationMailBox( newMailBox );
636
 
  connect( job, SIGNAL( result( KJob* ) ), SLOT( onRenameMailBoxDone( KJob* ) ) );
637
 
  job->start();
638
 
}
 
975
    emit error( i18n( "Failed to create folder '%1' on the IMAP server.", collection.name() ) );
 
976
    changeProcessed();
 
977
  }
 
978
}
 
979
 
 
980
void ImapResource::collectionChanged( const Collection &collection, const QSet<QByteArray> &parts )
 
981
{
 
982
  if ( !isSessionAvailable() ) {
 
983
    kDebug() << "Defering this request. Probably there is no connection.";
 
984
    deferTask();
 
985
    return;
 
986
  }
 
987
 
 
988
  if ( collection.remoteId().isEmpty() ) {
 
989
    emit error( i18n("Cannot modify IMAP folder '%1', it does not exist on the server.", collection.name() ) );
 
990
    changeProcessed();
 
991
    return;
 
992
  }
 
993
 
 
994
  QStringList encodedParts;
 
995
  foreach ( const QByteArray &part, parts ) {
 
996
    encodedParts << QString::fromUtf8( part );
 
997
  }
 
998
 
 
999
  kDebug(5327) << "parts:" << encodedParts;
 
1000
 
 
1001
  triggerNextCollectionChangeJob( collection, encodedParts );
 
1002
}
 
1003
 
 
1004
void ImapResource::triggerNextCollectionChangeJob( const Akonadi::Collection &collection,
 
1005
                                                   const QStringList &remainingParts )
 
1006
{
 
1007
  if ( remainingParts.isEmpty() ) { // We processed all parts, we're done here
 
1008
    changeCommitted( collection );
 
1009
    return;
 
1010
  }
 
1011
 
 
1012
  QStringList parts = remainingParts;
 
1013
  QString currentPart = parts.takeFirst();
 
1014
 
 
1015
  if ( currentPart == "NAME" ) {
 
1016
    Collection c = collection;
 
1017
    c.setRemoteId( collection.remoteId().at( 0 ) + collection.name() );
 
1018
 
 
1019
    const QString oldMailBox = mailBoxForCollection( collection );
 
1020
    const QString newMailBox = mailBoxForCollection( c );
 
1021
 
 
1022
    if ( oldMailBox != newMailBox ) {
 
1023
      KIMAP::RenameJob *job = new KIMAP::RenameJob( m_account->mainSession() );
 
1024
      job->setProperty( AKONADI_COLLECTION, QVariant::fromValue( c ) );
 
1025
      job->setProperty( AKONADI_PARTS, parts );
 
1026
      job->setProperty( PREVIOUS_REMOTEID, collection.remoteId() );
 
1027
      job->setSourceMailBox( oldMailBox );
 
1028
      job->setDestinationMailBox( newMailBox );
 
1029
      connect( job, SIGNAL( result( KJob* ) ), SLOT( onRenameMailBoxDone( KJob* ) ) );
 
1030
      job->start();
 
1031
    } else {
 
1032
      triggerNextCollectionChangeJob( collection, parts );
 
1033
    }
 
1034
 
 
1035
  } else if ( currentPart == "AccessRights" ) {
 
1036
    ImapAclAttribute *aclAttribute =
 
1037
      collection.attribute<ImapAclAttribute>();
 
1038
 
 
1039
    if ( aclAttribute==0 ) {
 
1040
      emit error( i18n( "ACLs for '%1' need to be retrieved from the IMAP server first. Skipping ACL change",
 
1041
                        collection.name() ) );
 
1042
      triggerNextCollectionChangeJob( collection, parts );
 
1043
      return;
 
1044
    }
 
1045
 
 
1046
    KIMAP::Acl::Rights imapRights = aclAttribute->rights()[m_account->userName().toUtf8()];
 
1047
    Collection::Rights newRights = collection.rights();
 
1048
 
 
1049
    if ( newRights & Collection::CanChangeItem ) {
 
1050
      imapRights|= KIMAP::Acl::Write;
 
1051
    } else {
 
1052
      imapRights&= ~KIMAP::Acl::Write;
 
1053
    }
 
1054
 
 
1055
    if ( newRights & Collection::CanCreateItem ) {
 
1056
      imapRights|= KIMAP::Acl::Insert;
 
1057
    } else {
 
1058
      imapRights&= ~KIMAP::Acl::Insert;
 
1059
    }
 
1060
 
 
1061
    if ( newRights & Collection::CanDeleteItem ) {
 
1062
      imapRights|= KIMAP::Acl::DeleteMessage;
 
1063
    } else {
 
1064
      imapRights&= ~KIMAP::Acl::DeleteMessage;
 
1065
    }
 
1066
 
 
1067
    if ( newRights & ( Collection::CanChangeCollection | Collection::CanCreateCollection ) ) {
 
1068
      imapRights|= KIMAP::Acl::CreateMailbox;
 
1069
      imapRights|= KIMAP::Acl::Create;
 
1070
    } else {
 
1071
      imapRights&= ~KIMAP::Acl::CreateMailbox;
 
1072
      imapRights&= ~KIMAP::Acl::Create;
 
1073
    }
 
1074
 
 
1075
    if ( newRights & Collection::CanDeleteCollection ) {
 
1076
      imapRights|= KIMAP::Acl::DeleteMailbox;
 
1077
    } else {
 
1078
      imapRights&= ~KIMAP::Acl::DeleteMailbox;
 
1079
    }
 
1080
 
 
1081
    if ( ( newRights & Collection::CanDeleteItem )
 
1082
      && ( newRights & Collection::CanDeleteCollection ) ) {
 
1083
      imapRights|= KIMAP::Acl::Delete;
 
1084
    } else {
 
1085
      imapRights&= ~KIMAP::Acl::Delete;
 
1086
    }
 
1087
 
 
1088
    kDebug(5327) << "imapRights:" << imapRights
 
1089
                 << "newRights:" << newRights;
 
1090
 
 
1091
    KIMAP::SetAclJob *job = new KIMAP::SetAclJob( m_account->mainSession() );
 
1092
    job->setProperty( AKONADI_COLLECTION, QVariant::fromValue( collection ) );
 
1093
    job->setProperty( AKONADI_PARTS, parts );
 
1094
    job->setMailBox( mailBoxForCollection( collection ) );
 
1095
    job->setRights( KIMAP::SetAclJob::Change, imapRights );
 
1096
    job->setIdentifier( m_account->userName().toUtf8() );
 
1097
    connect( job, SIGNAL( result( KJob* ) ), SLOT( onSetAclDone( KJob* ) ) );
 
1098
    job->start();
 
1099
 
 
1100
  } else if ( currentPart == "collectionannotations" ) {
 
1101
    CollectionAnnotationsAttribute *annotationsAttribute =
 
1102
      collection.attribute<CollectionAnnotationsAttribute>();
 
1103
 
 
1104
    if ( annotationsAttribute==0 ) { // No annotations it seems... server is lieing to us?
 
1105
      triggerNextCollectionChangeJob( collection, parts );
 
1106
    }
 
1107
 
 
1108
    KIMAP::SetMetaDataJob *job = 0;
 
1109
 
 
1110
    QMap<QByteArray, QByteArray> annotations = annotationsAttribute->annotations();
 
1111
    kDebug(5327) << "All annotations: " << annotations;
 
1112
    foreach ( const QByteArray &entry, annotations.keys() ) {
 
1113
      job = new KIMAP::SetMetaDataJob( m_account->mainSession() );
 
1114
      if ( m_account->capabilities().contains( "METADATA" ) ) {
 
1115
        job->setServerCapability( KIMAP::MetaDataJobBase::Metadata );
 
1116
      } else {
 
1117
        job->setServerCapability( KIMAP::MetaDataJobBase::Annotatemore );
 
1118
      }
 
1119
 
 
1120
      QByteArray attribute = entry;
 
1121
      if ( job->serverCapability()==KIMAP::MetaDataJobBase::Annotatemore ) {
 
1122
        attribute = "value.shared";
 
1123
      }
 
1124
 
 
1125
      job->setMailBox( mailBoxForCollection( collection ) );
 
1126
      job->setEntry( entry );
 
1127
      job->addMetaData( attribute, annotations[entry] );
 
1128
      kDebug(5327) << "Job got entry:" << entry << " attribute:" << attribute << "value:" << annotations[entry];
 
1129
 
 
1130
      job->start();
 
1131
    }
 
1132
 
 
1133
    // We'll get info out of the last job only to trigger the next phase
 
1134
    // of the collection change. The other ones we fire and forget.
 
1135
    // Obviously we assume here that they will all succeed or all fail.
 
1136
    job->setProperty( AKONADI_COLLECTION, QVariant::fromValue( collection ) );
 
1137
    job->setProperty( AKONADI_PARTS, parts );
 
1138
    connect( job, SIGNAL( result( KJob* ) ), SLOT( onSetMetaDataDone( KJob* ) ) );
 
1139
 
 
1140
  } else if ( currentPart == "imapacl" ) {
 
1141
    ImapAclAttribute *aclAttribute = collection.attribute<ImapAclAttribute>();
 
1142
    const QMap<QByteArray, KIMAP::Acl::Rights> rights = aclAttribute->rights();
 
1143
    const QList<QByteArray> ids = rights.keys();
 
1144
 
 
1145
    for ( int i = 0; i<ids.size(); i++ ) {
 
1146
      const QByteArray id = ids[i];
 
1147
 
 
1148
      KIMAP::SetAclJob *job = new KIMAP::SetAclJob( m_account->mainSession() );
 
1149
      job->setMailBox( mailBoxForCollection( collection ) );
 
1150
      job->setIdentifier( id );
 
1151
      job->setRights( KIMAP::SetAclJob::Change, rights[id] );
 
1152
 
 
1153
      if ( i < ids.size()-1 ) {
 
1154
        // Only the last set acl job will trigger the next collection change job
 
1155
        job->setProperty( "dontTriggerNextJob", true );
 
1156
      }
 
1157
 
 
1158
      connect( job, SIGNAL( result( KJob* ) ), SLOT( onSetAclDone( KJob* ) ) );
 
1159
      job->start();
 
1160
    }
 
1161
 
 
1162
  } else {
 
1163
    // unknown part
 
1164
    triggerNextCollectionChangeJob( collection, parts );
 
1165
  }
 
1166
}
 
1167
 
639
1168
 
640
1169
void ImapResource::onRenameMailBoxDone( KJob *job )
641
1170
{
642
 
  Collection collection = job->property( "akonadiCollection" ).value<Collection>();
 
1171
  Collection collection = job->property( AKONADI_COLLECTION ).value<Collection>();
 
1172
  QStringList parts = job->property( AKONADI_PARTS ).toStringList();
643
1173
 
644
1174
  if ( !job->error() ) {
645
 
    changeCommitted( collection );
 
1175
    triggerNextCollectionChangeJob( collection, parts );
646
1176
  } else {
647
 
    KIMAP::RenameJob *rename = qobject_cast<KIMAP::RenameJob*>( job );
648
 
 
649
 
    // rename the collection again.
650
 
    kDebug() << "Failed to rename the folder, resetting it in akonadi again";
651
 
    collection.setName( rename->sourceMailBox().split('/').last() );
652
 
    collection.setRemoteId( remoteIdForMailBox( rename->sourceMailBox() ) );
 
1177
    kDebug(5327) << "Failed to rename the folder, resetting it in akonadi again";
 
1178
    const QString prevRid = job->property( PREVIOUS_REMOTEID ).toString();
 
1179
    Q_ASSERT( !prevRid.isEmpty() );
 
1180
    collection.setName( prevRid.mid( 1 ) );
 
1181
    collection.setRemoteId( prevRid );
653
1182
    emit warning( i18n( "Failed to rename the folder, restoring folder list." ) );
654
1183
    changeCommitted( collection );
655
1184
  }
656
1185
}
657
1186
 
 
1187
void ImapResource::onSetAclDone( KJob *job )
 
1188
{
 
1189
  Collection collection = job->property( AKONADI_COLLECTION ).value<Collection>();
 
1190
  QStringList parts = job->property( AKONADI_PARTS ).toStringList();
 
1191
 
 
1192
  if ( job->error() ) {
 
1193
    emit error( i18n( "Failed to write the new ACLs for '%1' on the IMAP server. %2",
 
1194
                      collection.name(), job->errorText() ) );
 
1195
  }
 
1196
 
 
1197
  if ( !job->property( "dontTriggerNextJob" ).toBool() ) {
 
1198
    triggerNextCollectionChangeJob( collection, parts );
 
1199
  }
 
1200
}
 
1201
 
 
1202
void ImapResource::onSetMetaDataDone( KJob *job )
 
1203
{
 
1204
  Collection collection = job->property( AKONADI_COLLECTION ).value<Collection>();
 
1205
  QStringList parts = job->property( AKONADI_PARTS ).toStringList();
 
1206
 
 
1207
  if ( job->error() ) {
 
1208
    emit error( i18n( "Failed to write the new annotations for '%1' on the IMAP server. %2",
 
1209
                      collection.name(), job->errorText() ) );
 
1210
  }
 
1211
 
 
1212
  triggerNextCollectionChangeJob( collection, parts );
 
1213
}
 
1214
 
658
1215
void ImapResource::collectionRemoved( const Collection &collection )
659
1216
{
660
 
  const QString mailBox = mailBoxForRemoteId( collection.remoteId() );
661
 
 
662
 
  KIMAP::DeleteJob *job = new KIMAP::DeleteJob( m_account->session() );
663
 
  job->setProperty( "akonadiCollection", QVariant::fromValue( collection ) );
 
1217
  if ( !isSessionAvailable() ) {
 
1218
    kDebug() << "Defering this request. Probably there is no connection.";
 
1219
    deferTask();
 
1220
    return;
 
1221
  }
 
1222
 
 
1223
  const QString mailBox = mailBoxForCollection( collection );
 
1224
 
 
1225
  KIMAP::DeleteJob *job = new KIMAP::DeleteJob( m_account->mainSession() );
 
1226
  job->setProperty( AKONADI_COLLECTION, QVariant::fromValue( collection ) );
664
1227
  job->setMailBox( mailBox );
665
1228
  connect( job, SIGNAL( result( KJob* ) ), SLOT( onDeleteMailBoxDone( KJob* ) ) );
666
1229
  job->start();
671
1234
  // finish the task.
672
1235
  changeProcessed();
673
1236
 
674
 
    if ( !job->error() ) {
675
 
        kDebug() << "Failed to delete the folder, resync the folder tree";
 
1237
    if ( job->error() ) {
 
1238
        kDebug(5327) << "Failed to delete the folder, resync the folder tree";
676
1239
        emit warning( i18n( "Failed to delete the folder, restoring folder list." ) );
677
1240
        synchronizeCollectionTree();
678
1241
    }
679
1242
}
680
1243
 
 
1244
 
 
1245
void ImapResource::collectionMoved( const Akonadi::Collection &collection, const Akonadi::Collection &source,
 
1246
                                    const Akonadi::Collection &destination )
 
1247
{
 
1248
  if ( !isSessionAvailable() ) {
 
1249
    kDebug() << "Defering this request. Probably there is no connection.";
 
1250
    deferTask();
 
1251
    return;
 
1252
  }
 
1253
 
 
1254
  if ( collection.remoteId().isEmpty() ) {
 
1255
    emit error( i18n( "Cannot move IMAP folder '%1', it does not exist on the server.",
 
1256
                      collection.name() ) );
 
1257
    changeProcessed();
 
1258
    return;
 
1259
  }
 
1260
 
 
1261
  if ( source.remoteId().isEmpty() ) {
 
1262
    emit error( i18n( "Cannot move IMAP folder '%1' out of '%2', '%2' does not exist on the server.",
 
1263
                      collection.name(),
 
1264
                      source.name() ) );
 
1265
    changeProcessed();
 
1266
    return;
 
1267
  }
 
1268
 
 
1269
  if ( destination.remoteId().isEmpty() ) {
 
1270
    emit error( i18n( "Cannot move IMAP folder '%1' to '%2', '%2' does not exist on the server.",
 
1271
                      collection.name(),
 
1272
                      source.name() ) );
 
1273
    changeProcessed();
 
1274
    return;
 
1275
  }
 
1276
 
 
1277
  // collection.remoteId() already includes the separator
 
1278
  const QString oldMailBox = mailBoxForCollection( source )+collection.remoteId();
 
1279
  const QString newMailBox = mailBoxForCollection( destination )+collection.remoteId();
 
1280
 
 
1281
  if ( oldMailBox != newMailBox ) {
 
1282
    KIMAP::RenameJob *job = new KIMAP::RenameJob( m_account->mainSession() );
 
1283
    job->setProperty( AKONADI_COLLECTION, QVariant::fromValue( collection ) );
 
1284
    job->setProperty( SOURCE_COLLECTION, QVariant::fromValue( source ) );
 
1285
    job->setSourceMailBox( oldMailBox );
 
1286
    job->setDestinationMailBox( newMailBox );
 
1287
    connect( job, SIGNAL( result( KJob* ) ), SLOT( onMailBoxMoveDone( KJob* ) ) );
 
1288
    job->start();
 
1289
  } else {
 
1290
    changeProcessed();
 
1291
  }
 
1292
}
 
1293
 
 
1294
void ImapResource::onMailBoxMoveDone( KJob *job )
 
1295
{
 
1296
  Collection collection = job->property( AKONADI_COLLECTION ).value<Collection>();
 
1297
 
 
1298
  if ( !job->error() ) {
 
1299
    KIMAP::SubscribeJob *subscribe = new KIMAP::SubscribeJob( m_account->mainSession() );
 
1300
    subscribe->setMailBox( static_cast<KIMAP::RenameJob*>( job )->destinationMailBox() );
 
1301
    subscribe->setProperty( AKONADI_COLLECTION, QVariant::fromValue( collection ) );
 
1302
    connect( job, SIGNAL( result( KJob* ) ), SLOT( onSubscribeDone( KJob* ) ) );
 
1303
    subscribe->start();
 
1304
  } else {
 
1305
    const Collection parent = job->property( SOURCE_COLLECTION ).value<Collection>();
 
1306
    Q_ASSERT(  parent.isValid() );
 
1307
    emit error( i18n( "Failed to move folder '%1' out of '%2' on the IMAP server.", collection.name(), parent.name() ) );
 
1308
    changeProcessed();
 
1309
  }
 
1310
}
 
1311
 
 
1312
void ImapResource::onSubscribeDone( KJob *job )
 
1313
{
 
1314
  Collection collection = job->property( AKONADI_COLLECTION ).value<Collection>();
 
1315
 
 
1316
  if ( job->error() ) { // Just warn about the failed subscription
 
1317
    emit warning( i18n( "Failed to subcribe to the newly moved folder '%1' on the IMAP server.",
 
1318
                      collection.name() ) );
 
1319
  }
 
1320
 
 
1321
  changeCommitted( collection );
 
1322
}
 
1323
 
681
1324
/******************* Slots  ***********************************************/
682
1325
 
683
 
void ImapResource::onConnectError( int code, const QString &message )
 
1326
void ImapResource::onConnectError( KIMAP::Session *session, int code, const QString &message )
684
1327
{
 
1328
  if ( m_account->mainSession()!=session ) {
 
1329
    return;
 
1330
  }
 
1331
 
685
1332
  if ( code==ImapAccount::LoginFailError ) {
686
1333
    // the credentials where not ok....
687
1334
    int i = KMessageBox::questionYesNoCancelWId( winIdForDialogs(),
698
1345
      startConnect( true );
699
1346
      return;
700
1347
    } else {
701
 
      KIMAP::LogoutJob *logout = new KIMAP::LogoutJob( m_account->session() );
 
1348
      KIMAP::LogoutJob *logout = new KIMAP::LogoutJob( m_account->mainSession() );
702
1349
      logout->start();
703
 
      emit warning( i18n( "Could not connect to the IMAP-server %1.", m_account->server() ) );
 
1350
      emit status( Broken, i18n( "Could not connect to the IMAP-server %1.", m_account->server() ) );
704
1351
    }
705
1352
  }
706
1353
 
708
1355
  emit error( message );
709
1356
}
710
1357
 
711
 
void ImapResource::onConnectSuccess()
 
1358
void ImapResource::onConnectSuccess( KIMAP::Session *session )
712
1359
{
 
1360
  if ( m_account->mainSession()!=session ) {
 
1361
    return;
 
1362
  }
 
1363
 
 
1364
  startIdle();
 
1365
  emit status( Idle, i18n( "Connection established." ) );
713
1366
  synchronizeCollectionTree();
714
1367
}
715
1368
 
720
1373
  }
721
1374
 
722
1375
  KIMAP::GetAclJob *acl = qobject_cast<KIMAP::GetAclJob*>( job );
723
 
  Collection collection = job->property( "akonadiCollection" ).value<Collection>();
 
1376
  Collection collection = job->property( AKONADI_COLLECTION ).value<Collection>();
724
1377
 
725
1378
  // Store the mailbox ACLs
726
 
  if ( !collection.hasAttribute( "imapacl" ) ) {
727
 
    ImapAclAttribute *aclAttribute  = new ImapAclAttribute( acl->allRights() );
728
 
    collection.addAttribute( aclAttribute );
729
 
  } else {
730
 
    ImapAclAttribute *aclAttribute =
731
 
      static_cast<ImapAclAttribute*>( collection.attribute( "imapacl" ) );
732
 
    const QMap<QByteArray, KIMAP::Acl::Rights> oldRights = aclAttribute->rights();
733
 
    if ( oldRights != acl->allRights() ) {
734
 
      aclAttribute->setRights( acl->allRights() );
735
 
    }
 
1379
  ImapAclAttribute *aclAttribute = collection.attribute<ImapAclAttribute>( Collection::AddIfMissing );
 
1380
  const QMap<QByteArray, KIMAP::Acl::Rights> oldRights = aclAttribute->rights();
 
1381
  if ( oldRights != acl->allRights() ) {
 
1382
    aclAttribute->setRights( acl->allRights() );
 
1383
    new CollectionModifyJob( collection );
736
1384
  }
737
 
 
738
 
  CollectionModifyJob *modify = new CollectionModifyJob( collection );
739
1385
}
740
1386
 
741
1387
void ImapResource::onRightsReceived( KJob *job )
745
1391
  }
746
1392
 
747
1393
  KIMAP::MyRightsJob *rightsJob = qobject_cast<KIMAP::MyRightsJob*>( job );
748
 
  Collection collection = job->property( "akonadiCollection" ).value<Collection>();
 
1394
  Collection collection = job->property( AKONADI_COLLECTION ).value<Collection>();
749
1395
 
750
1396
  KIMAP::Acl::Rights imapRights = rightsJob->rights();
751
1397
  Collection::Rights newRights = Collection::ReadOnly;
758
1404
    newRights|= Collection::CanCreateItem;
759
1405
  }
760
1406
 
761
 
  if ( imapRights & KIMAP::Acl::DeleteMessage ) {
 
1407
  if ( imapRights & ( KIMAP::Acl::DeleteMessage | KIMAP::Acl::Delete ) ) {
762
1408
    newRights|= Collection::CanDeleteItem;
763
1409
  }
764
1410
 
765
 
  if ( imapRights & KIMAP::Acl::CreateMailbox ) {
 
1411
  if ( imapRights & ( KIMAP::Acl::CreateMailbox | KIMAP::Acl::Create ) ) {
766
1412
    newRights|= Collection::CanChangeCollection;
767
1413
    newRights|= Collection::CanCreateCollection;
768
1414
  }
769
1415
 
770
 
  if ( imapRights & KIMAP::Acl::DeleteMailbox ) {
 
1416
  if ( imapRights & ( KIMAP::Acl::DeleteMailbox | KIMAP::Acl::Delete ) ) {
771
1417
    newRights|= Collection::CanDeleteCollection;
772
1418
  }
773
1419
 
 
1420
  kDebug(5327) << "imapRights:" << imapRights
 
1421
               << "newRights:" << newRights
 
1422
               << "oldRights:" << collection.rights();
 
1423
 
774
1424
  if ( newRights != collection.rights() ) {
775
1425
    collection.setRights( newRights );
776
1426
 
777
 
    CollectionModifyJob *modify = new CollectionModifyJob( collection );
 
1427
    new CollectionModifyJob( collection );
778
1428
  }
779
1429
}
780
1430
 
785
1435
  }
786
1436
 
787
1437
  KIMAP::GetQuotaRootJob *quotaJob = qobject_cast<KIMAP::GetQuotaRootJob*>( job );
788
 
  Collection collection = job->property( "akonadiCollection" ).value<Collection>();
 
1438
  Collection collection = job->property( AKONADI_COLLECTION ).value<Collection>();
 
1439
  const QString &mailBox = mailBoxForCollection( collection );
789
1440
 
790
1441
  QList<QByteArray> newRoots = quotaJob->roots();
791
1442
  QList< QMap<QByteArray, qint64> > newLimits;
792
1443
  QList< QMap<QByteArray, qint64> > newUsages;
 
1444
  qint64 newCurrent = -1;
 
1445
  qint64 newMax = -1;
793
1446
 
794
1447
  foreach ( const QByteArray &root, newRoots ) {
795
1448
    newLimits << quotaJob->allLimits( root );
796
1449
    newUsages << quotaJob->allUsages( root );
797
 
  }
798
 
 
799
 
  // Store the mailbox Quotas
800
 
  if ( !collection.hasAttribute( "imapquota" ) ) {
801
 
    ImapQuotaAttribute *quotaAttribute  = new ImapQuotaAttribute( newRoots, newLimits, newUsages );
802
 
    collection.addAttribute( quotaAttribute );
803
 
  } else {
804
 
    ImapQuotaAttribute *quotaAttribute =
805
 
      static_cast<ImapQuotaAttribute*>( collection.attribute( "imapquota" ) );
806
 
    const QList<QByteArray> oldRoots = quotaAttribute->roots();
807
 
    const QList< QMap<QByteArray, qint64> > oldLimits = quotaAttribute->limits();
808
 
    const QList< QMap<QByteArray, qint64> > oldUsages = quotaAttribute->usages();
809
 
 
810
 
    if ( oldRoots != newRoots
811
 
      || oldLimits != newLimits
812
 
      || oldUsages != newUsages ) {
813
 
      quotaAttribute->setQuotas( newRoots, newLimits, newUsages );
 
1450
 
 
1451
    const QString &decodedRoot = QString::fromUtf8( KIMAP::decodeImapFolderName( root ) );
 
1452
 
 
1453
    if ( newRoots.size() == 1 || decodedRoot == mailBox ) {
 
1454
      newCurrent = newUsages.last()["STORAGE"] * 1024;
 
1455
      newMax = newLimits.last()["STORAGE"] * 1024;
814
1456
    }
815
1457
  }
816
1458
 
817
 
  CollectionModifyJob *modify = new CollectionModifyJob( collection );
 
1459
  bool updateNeeded = false;
 
1460
 
 
1461
  // Store the mailbox IMAP Quotas
 
1462
  ImapQuotaAttribute *imapQuotaAttribute = collection.attribute<ImapQuotaAttribute>( Collection::AddIfMissing );
 
1463
  const QList<QByteArray> oldRoots = imapQuotaAttribute->roots();
 
1464
  const QList< QMap<QByteArray, qint64> > oldLimits = imapQuotaAttribute->limits();
 
1465
  const QList< QMap<QByteArray, qint64> > oldUsages = imapQuotaAttribute->usages();
 
1466
 
 
1467
  if ( oldRoots != newRoots
 
1468
    || oldLimits != newLimits
 
1469
    || oldUsages != newUsages )
 
1470
  {
 
1471
    imapQuotaAttribute->setQuotas( newRoots, newLimits, newUsages );
 
1472
    updateNeeded = true;
 
1473
  }
 
1474
 
 
1475
  // Store the collection Quota
 
1476
  CollectionQuotaAttribute *quotaAttribute
 
1477
    = collection.attribute<CollectionQuotaAttribute>( Collection::AddIfMissing );
 
1478
  qint64 oldCurrent = quotaAttribute->currentValue();
 
1479
  qint64 oldMax = quotaAttribute->maximumValue();
 
1480
 
 
1481
  if ( oldCurrent != newCurrent
 
1482
    || oldMax != newMax ) {
 
1483
    quotaAttribute->setCurrentValue( newCurrent );
 
1484
    quotaAttribute->setMaximumValue( newMax );
 
1485
    updateNeeded = true;
 
1486
  }
 
1487
 
 
1488
  if ( updateNeeded ) {
 
1489
    new CollectionModifyJob( collection );
 
1490
  }
818
1491
}
819
1492
 
820
1493
void ImapResource::onGetMetaDataDone( KJob *job )
836
1509
    annotations[entry] = rawAnnotations[entry][attribute];
837
1510
  }
838
1511
 
839
 
  Collection collection = job->property( "akonadiCollection" ).value<Collection>();
 
1512
  // filter out unused and annoying Cyrus annotation /vendor/cmu/cyrus-imapd/lastupdate
 
1513
  // which contains the current date and time and thus constantly changes for no good
 
1514
  // reason which triggers a change notification and thus a bunch of Akonadi operations
 
1515
  annotations.remove( "/vendor/cmu/cyrus-imapd/lastupdate" );
 
1516
 
 
1517
  Collection collection = job->property( AKONADI_COLLECTION ).value<Collection>();
840
1518
 
841
1519
  // Store the mailbox metadata
842
1520
  CollectionAnnotationsAttribute *annotationsAttribute =
844
1522
  const QMap<QByteArray, QByteArray> oldAnnotations = annotationsAttribute->annotations();
845
1523
  if ( oldAnnotations != annotations ) {
846
1524
    annotationsAttribute->setAnnotations( annotations );
 
1525
    new CollectionModifyJob( collection );
847
1526
  }
848
 
 
849
 
  CollectionModifyJob *modify = new CollectionModifyJob( collection );
850
1527
}
851
1528
 
852
1529
void ImapResource::onSelectDone( KJob *job )
873
1550
    processed.append( mailBox );
874
1551
  }
875
1552
 
876
 
  Collection collection = collectionFromRemoteId( remoteIdForMailBox( mailBox ) );
877
 
  Q_ASSERT( collection.isValid() );
 
1553
  Collection collection = job->property( AKONADI_COLLECTION ).value<Collection>();
878
1554
 
879
1555
  // Get the current uid validity value and store it
880
1556
  int oldUidValidity = 0;
917
1593
    }
918
1594
  }
919
1595
 
920
 
  CollectionModifyJob *modify = new CollectionModifyJob( collection );
 
1596
  new CollectionModifyJob( collection );
 
1597
 
 
1598
  KIMAP::FetchJob::FetchScope scope;
 
1599
  scope.parts.clear();
 
1600
  scope.mode = KIMAP::FetchJob::FetchScope::Headers;
 
1601
 
 
1602
  if ( collection.cachePolicy()
 
1603
       .localParts().contains( Akonadi::MessagePart::Body ) ) {
 
1604
    scope.mode = KIMAP::FetchJob::FetchScope::Full;
 
1605
  }
921
1606
 
922
1607
  // First check the uidvalidity, if this has changed, it means the folder
923
1608
  // has been deleted and recreated. So we wipe out the messages and
924
1609
  // retrieve all.
925
1610
  if ( oldUidValidity != uidValidity && !firstTime
926
1611
    && oldUidValidity != 0 ) {
927
 
    kDebug() << "UIDVALIDITY check failed (" << oldUidValidity << "|"
928
 
             << uidValidity <<") refetching "<< mailBox;
 
1612
    kDebug(5327) << "UIDVALIDITY check failed (" << oldUidValidity << "|"
 
1613
                 << uidValidity <<") refetching "<< mailBox;
929
1614
 
930
1615
    setItemStreamingEnabled( true );
931
1616
 
932
 
    KIMAP::FetchJob *fetch = new KIMAP::FetchJob( m_account->session() );
933
 
    KIMAP::FetchJob::FetchScope scope;
 
1617
    KIMAP::FetchJob *fetch = new KIMAP::FetchJob( m_account->mainSession() );
934
1618
    fetch->setSequenceSet( KIMAP::ImapSet( 1, messageCount ) );
935
 
    scope.parts.clear();
936
 
    scope.mode = KIMAP::FetchJob::FetchScope::Headers;
937
1619
    fetch->setScope( scope );
938
1620
    connect( fetch, SIGNAL( headersReceived( QString, QMap<qint64, qint64>, QMap<qint64, qint64>,
939
1621
                                             QMap<qint64, KIMAP::MessageFlags>, QMap<qint64, KIMAP::MessagePtr> ) ),
941
1623
                                            QMap<qint64, KIMAP::MessageFlags>, QMap<qint64, KIMAP::MessagePtr> ) ) );
942
1624
    connect( fetch, SIGNAL( result( KJob* ) ),
943
1625
             this, SLOT( onHeadersFetchDone( KJob* ) ) );
 
1626
    fetch->setProperty( "nonIncremental", true );
944
1627
    fetch->start();
945
1628
    return;
946
1629
  }
955
1638
    }
956
1639
  }
957
1640
 
958
 
  kDebug() << "integrity: " << mailBox << " should be: " << messageCount << " current: " << realMessageCount;
 
1641
  kDebug(5327) << "integrity: " << mailBox << " should be: " << messageCount << " current: " << realMessageCount;
959
1642
 
960
1643
  if ( messageCount > realMessageCount ) {
961
1644
    // The amount on the server is bigger than that we have in the cache
962
1645
    // that probably means that there is new mail. Fetch missing.
963
 
    kDebug() << "Fetch missing: " << messageCount << " But: " << realMessageCount;
 
1646
    kDebug(5327) << "Fetch missing: " << messageCount << " But: " << realMessageCount;
964
1647
 
965
1648
    setItemStreamingEnabled( true );
966
1649
 
967
 
    KIMAP::FetchJob *fetch = new KIMAP::FetchJob( m_account->session() );
968
 
    KIMAP::FetchJob::FetchScope scope;
 
1650
    KIMAP::FetchJob *fetch = new KIMAP::FetchJob( m_account->mainSession() );
969
1651
    fetch->setSequenceSet( KIMAP::ImapSet( realMessageCount+1, messageCount ) );
970
 
    scope.parts.clear();
971
 
    scope.mode = KIMAP::FetchJob::FetchScope::Headers;
972
1652
    fetch->setScope( scope );
973
1653
    connect( fetch, SIGNAL( headersReceived( QString, QMap<qint64, qint64>, QMap<qint64, qint64>,
974
1654
                                             QMap<qint64, KIMAP::MessageFlags>, QMap<qint64, KIMAP::MessagePtr> ) ),
980
1660
    return;
981
1661
  } else if ( messageCount != realMessageCount ) {
982
1662
    // The amount on the server does not match the amount in the cache.
983
 
    // that means we need reget the catch completely.
984
 
    kDebug() << "O OH: " << messageCount << " But: " << realMessageCount;
 
1663
    // that means we need reget the cache completely.
 
1664
    kDebug(5327) << "O OH: " << messageCount << " But: " << realMessageCount;
985
1665
 
986
 
    itemsClear( collection );
987
1666
    setItemStreamingEnabled( true );
988
1667
 
989
 
    KIMAP::FetchJob *fetch = new KIMAP::FetchJob( m_account->session() );
990
 
    KIMAP::FetchJob::FetchScope scope;
 
1668
    KIMAP::FetchJob *fetch = new KIMAP::FetchJob( m_account->mainSession() );
991
1669
    fetch->setSequenceSet( KIMAP::ImapSet( 1, messageCount ) );
992
 
    scope.parts.clear();
993
 
    scope.mode = KIMAP::FetchJob::FetchScope::Headers;
994
1670
    fetch->setScope( scope );
995
1671
    connect( fetch, SIGNAL( headersReceived( QString, QMap<qint64, qint64>, QMap<qint64, qint64>,
996
1672
                                             QMap<qint64, KIMAP::MessageFlags>, QMap<qint64, KIMAP::MessagePtr> ) ),
998
1674
                                            QMap<qint64, KIMAP::MessageFlags>, QMap<qint64, KIMAP::MessagePtr> ) ) );
999
1675
    connect( fetch, SIGNAL( result( KJob* ) ),
1000
1676
             this, SLOT( onHeadersFetchDone( KJob* ) ) );
 
1677
    fetch->setProperty( "nonIncremental", true );
1001
1678
    fetch->start();
1002
1679
    return;
1003
1680
  } else if ( messageCount == realMessageCount && oldNextUid != nextUid
1004
1681
           && oldNextUid != 0 && !firstTime ) {
1005
1682
    // amount is right but uidnext is different.... something happened
1006
1683
    // behind our back...
1007
 
    kDebug() << "UIDNEXT check failed, refetching mailbox";
 
1684
    kDebug(5327) << "UIDNEXT check failed, refetching mailbox";
1008
1685
 
1009
 
    itemsClear( collection );
1010
1686
    setItemStreamingEnabled( true );
1011
1687
 
1012
 
    KIMAP::FetchJob *fetch = new KIMAP::FetchJob( m_account->session() );
1013
 
    KIMAP::FetchJob::FetchScope scope;
 
1688
    KIMAP::FetchJob *fetch = new KIMAP::FetchJob( m_account->mainSession() );
1014
1689
    fetch->setSequenceSet( KIMAP::ImapSet( 1, messageCount ) );
1015
 
    scope.parts.clear();
1016
 
    scope.mode = KIMAP::FetchJob::FetchScope::Headers;
1017
1690
    fetch->setScope( scope );
1018
1691
    connect( fetch, SIGNAL( headersReceived( QString, QMap<qint64, qint64>, QMap<qint64, qint64>,
1019
1692
                                             QMap<qint64, KIMAP::MessageFlags>, QMap<qint64, KIMAP::MessagePtr> ) ),
1021
1694
                                            QMap<qint64, KIMAP::MessageFlags>, QMap<qint64, KIMAP::MessagePtr> ) ) );
1022
1695
    connect( fetch, SIGNAL( result( KJob* ) ),
1023
1696
             this, SLOT( onHeadersFetchDone( KJob* ) ) );
 
1697
    fetch->setProperty( "nonIncremental", true );
1024
1698
    fetch->start();
1025
1699
    return;
1026
1700
  }
1027
1701
 
1028
 
  kDebug() << "All fine, nothing to do";
1029
 
  itemsRetrievalDone();
 
1702
  kDebug(5327) << "All fine, asking for all message flags looking for changes";
 
1703
 
 
1704
  setItemStreamingEnabled( true );
 
1705
 
 
1706
  scope.parts.clear();
 
1707
  scope.mode = KIMAP::FetchJob::FetchScope::Flags;
 
1708
 
 
1709
  KIMAP::FetchJob *fetch = new KIMAP::FetchJob( m_account->mainSession() );
 
1710
  fetch->setSequenceSet( KIMAP::ImapSet( 1, messageCount ) );
 
1711
  fetch->setScope( scope );
 
1712
  connect( fetch, SIGNAL( headersReceived( QString, QMap<qint64, qint64>, QMap<qint64, qint64>,
 
1713
                                           QMap<qint64, KIMAP::MessageFlags>, QMap<qint64, KIMAP::MessagePtr> ) ),
 
1714
           this, SLOT( onFlagsReceived( QString, QMap<qint64, qint64>, QMap<qint64, qint64>,
 
1715
                                        QMap<qint64, KIMAP::MessageFlags>, QMap<qint64, KIMAP::MessagePtr> ) ) );
 
1716
  connect( fetch, SIGNAL( result( KJob* ) ),
 
1717
           this, SLOT( onFlagsFetchDone( KJob* ) ) );
 
1718
  fetch->start();
1030
1719
}
1031
1720
 
1032
1721
 
1033
1722
/******************* Private ***********************************************/
1034
1723
 
1035
 
bool ImapResource::manualAuth( const QString& username, QString &password )
1036
 
{
1037
 
  KPasswordDialog dlg( 0 );
1038
 
  dlg.setPrompt( i18n( "Could not find a valid password, please enter it here." ) );
1039
 
  if ( dlg.exec() == QDialog::Accepted ) {
1040
 
    password = dlg.password();
1041
 
    return true;
1042
 
  } else {
1043
 
    password = QString();
1044
 
    return false;
1045
 
  }
1046
 
}
1047
 
 
1048
1724
QString ImapResource::rootRemoteId() const
1049
1725
{
1050
1726
  return "imap://"+m_account->userName()+'@'+m_account->server()+'/';
1051
1727
}
1052
1728
 
1053
 
QString ImapResource::remoteIdForMailBox( const QString &path ) const
1054
 
{
1055
 
  return rootRemoteId()+path;
1056
 
}
1057
 
 
1058
 
QString ImapResource::mailBoxForRemoteId( const QString &remoteId ) const
1059
 
{
1060
 
  QString path = remoteId;
1061
 
  path.replace( rootRemoteId(), "" );
1062
 
  return path;
1063
 
}
1064
 
 
1065
 
Collection ImapResource::collectionFromRemoteId( const QString &remoteId )
1066
 
{
1067
 
  CollectionFetchJob *fetch = new CollectionFetchJob( Collection::root(), CollectionFetchJob::Recursive );
1068
 
  fetch->setResource( identifier() );
1069
 
  fetch->exec();
1070
 
 
1071
 
  Collection::List collections = fetch->collections();
1072
 
  foreach ( const Collection &collection, collections ) {
1073
 
    if ( collection.remoteId()==remoteId ) {
1074
 
      return collection;
1075
 
    }
1076
 
  }
1077
 
 
1078
 
  return Collection();
1079
 
}
1080
 
 
1081
 
Item ImapResource::itemFromRemoteId( const Akonadi::Collection &collection, const QString &remoteId )
1082
 
{
1083
 
  ItemFetchJob *fetch = new ItemFetchJob( collection );
1084
 
  fetch->exec();
1085
 
 
1086
 
  Item::List items = fetch->items();
1087
 
  foreach ( const Item &item, items ) {
1088
 
    if ( item.remoteId()==remoteId ) {
1089
 
      return item;
1090
 
    }
1091
 
  }
1092
 
 
1093
 
  return Item();
 
1729
QString ImapResource::mailBoxForCollection( const Collection& col ) const
 
1730
{
 
1731
  if ( col.remoteId().isEmpty() ) {
 
1732
    kWarning() << "Got incomplete ancestor chain:" << col;
 
1733
    return QString();
 
1734
  }
 
1735
 
 
1736
  if ( col.parentCollection() == Collection::root() ) {
 
1737
    kWarning( col.remoteId() != rootRemoteId() ) << "RID mismatch, is " << col.remoteId() << " expected " << rootRemoteId();
 
1738
    return QString( "" );
 
1739
  }
 
1740
  const QString parentMailbox = mailBoxForCollection( col.parentCollection() );
 
1741
  if ( parentMailbox.isNull() ) // invalid, != isEmpty() here!
 
1742
    return QString();
 
1743
 
 
1744
  const QString mailbox =  parentMailbox + col.remoteId();
 
1745
  if ( parentMailbox.isEmpty() )
 
1746
    return mailbox.mid( 1 ); // strip of the separator on top-level mailboxes
 
1747
  return mailbox;
1094
1748
}
1095
1749
 
1096
1750
void ImapResource::itemsClear( const Collection &collection )
1108
1762
  transaction->exec();
1109
1763
}
1110
1764
 
 
1765
void ImapResource::doSetOnline(bool online)
 
1766
{
 
1767
  if ( !online && isSessionAvailable() ) {
 
1768
    m_account->disconnect();
 
1769
  } else if ( online ) {
 
1770
    startConnect();
 
1771
  }
 
1772
  ResourceBase::doSetOnline( online );
 
1773
}
 
1774
 
 
1775
bool ImapResource::needsNetwork() const
 
1776
{
 
1777
  const QString hostName = Settings::self()->imapServer().section( ':', 0, 0 );
 
1778
  // ### is there a better way to do this?
 
1779
  if ( hostName == QLatin1String( "127.0.0.1" ) ||
 
1780
       hostName == QLatin1String( "localhost" ) ||
 
1781
       hostName == QHostInfo::localHostName() ) {
 
1782
    return false;
 
1783
  }
 
1784
  return true;
 
1785
}
 
1786
 
 
1787
bool ImapResource::isSessionAvailable() const
 
1788
{
 
1789
  return m_account && m_account->mainSession()
 
1790
      && m_account->mainSession()->state() != KIMAP::Session::Disconnected;
 
1791
}
 
1792
 
 
1793
void ImapResource::reconnect()
 
1794
{
 
1795
  setNeedsNetwork( needsNetwork() );
 
1796
  setOnline( false ); // we are not connected initially
 
1797
  setOnline( !needsNetwork() ||
 
1798
             Solid::Networking::status() == Solid::Networking::Unknown ||
 
1799
             Solid::Networking::status() == Solid::Networking::Connected );
 
1800
}
 
1801
 
 
1802
void ImapResource::startIdle()
 
1803
{
 
1804
  delete m_idle;
 
1805
  m_idle = 0;
 
1806
 
 
1807
  if ( !m_account || !m_account->capabilities().contains( "IDLE" ) )
 
1808
    return;
 
1809
 
 
1810
  const QStringList ridPath = Settings::self()->idleRidPath();
 
1811
  if ( ridPath.size() < 2 )
 
1812
    return;
 
1813
 
 
1814
  Collection c, p;
 
1815
  p.setParentCollection( Collection::root() );
 
1816
  for ( int i = ridPath.size() - 1; i > 0; --i ) {
 
1817
    p.setRemoteId( ridPath.at( i ) );
 
1818
    c.setParentCollection( p );
 
1819
    p = c;
 
1820
  }
 
1821
  c.setRemoteId( ridPath.first() );
 
1822
 
 
1823
  Akonadi::CollectionFetchScope scope;
 
1824
  scope.setResource( identifier() );
 
1825
 
 
1826
  Akonadi::CollectionFetchJob *fetch
 
1827
    = new Akonadi::CollectionFetchJob( c, Akonadi::CollectionFetchJob::Base, this );
 
1828
  fetch->setFetchScope( scope );
 
1829
  fetch->setProperty( "mailBox", mailBoxForCollection( c ) );
 
1830
 
 
1831
  connect( fetch, SIGNAL(result(KJob*)),
 
1832
           this, SLOT(onIdleCollectionFetchDone(KJob*)) );
 
1833
}
 
1834
 
 
1835
void ImapResource::onIdleCollectionFetchDone( KJob *job )
 
1836
{
 
1837
  const QString mailBox = job->property( "mailBox" ).toString();
 
1838
 
 
1839
  if ( job->error() == 0 ) {
 
1840
    Akonadi::CollectionFetchJob *fetch = static_cast<Akonadi::CollectionFetchJob*>( job );
 
1841
    Akonadi::Collection c = fetch->collections().first();
 
1842
 
 
1843
    const QString password = Settings::self()->password();
 
1844
    if ( password.isEmpty() )
 
1845
      return;
 
1846
 
 
1847
    m_idle = new ImapIdleManager( c, mailBox,
 
1848
                                  m_account->extraSession( "idle", password ),
 
1849
                                  this );
 
1850
 
 
1851
  } else {
 
1852
    kWarning() << "CollectionFetch for mail box "
 
1853
               << mailBox << "failed. error="
 
1854
               << job->error() << ", errorString=" << job->errorString();
 
1855
  }
 
1856
}
 
1857
 
 
1858
void ImapResource::requestManualExpunge( qint64 collectionId )
 
1859
{
 
1860
  if ( !Settings::self()->automaticExpungeEnabled() ) {
 
1861
    scheduleCustomTask( this, "expungeRequested",
 
1862
                        QVariant::fromValue( Collection( collectionId ) ) );
 
1863
  }
 
1864
}
 
1865
 
 
1866
void ImapResource::expungeRequested( const QVariant &collectionArgument )
 
1867
{
 
1868
  const Collection collection = collectionArgument.value<Collection>();
 
1869
 
 
1870
  if ( collection.isValid() ) {
 
1871
    Akonadi::CollectionFetchScope scope;
 
1872
    scope.setResource( identifier() );
 
1873
    scope.setAncestorRetrieval( Akonadi::CollectionFetchScope::All );
 
1874
 
 
1875
    Akonadi::CollectionFetchJob *fetch
 
1876
      = new Akonadi::CollectionFetchJob( collection,
 
1877
                                         Akonadi::CollectionFetchJob::Base,
 
1878
                                         this );
 
1879
    fetch->setFetchScope( scope );
 
1880
    fetch->setProperty( AKONADI_COLLECTION, collection.id() );
 
1881
 
 
1882
    connect( fetch, SIGNAL(result(KJob*)),
 
1883
             this, SLOT(onExpungeCollectionFetchDone(KJob*)) );
 
1884
  } else {
 
1885
    changeProcessed();
 
1886
  }
 
1887
}
 
1888
 
 
1889
void ImapResource::onExpungeCollectionFetchDone( KJob *job )
 
1890
{
 
1891
  const Collection::Id collectionId = job->property( AKONADI_COLLECTION ).toLongLong();
 
1892
 
 
1893
  if ( job->error() == 0 ) {
 
1894
    Akonadi::CollectionFetchJob *fetch = static_cast<Akonadi::CollectionFetchJob*>( job );
 
1895
 
 
1896
    foreach ( const Akonadi::Collection &c, fetch->collections() ) {
 
1897
      if ( c.id() == collectionId ) {
 
1898
        const QString mailBox = mailBoxForCollection( c );
 
1899
 
 
1900
        if ( !mailBox.isEmpty() ) {
 
1901
          triggerExpunge( mailBox );
 
1902
        }
 
1903
        break;
 
1904
      }
 
1905
    }
 
1906
  } else {
 
1907
    kWarning() << "CollectionFetch for collection "
 
1908
               << collectionId << "failed. error="
 
1909
               << job->error() << ", errorString=" << job->errorString();
 
1910
  }
 
1911
 
 
1912
  changeProcessed();
 
1913
}
 
1914
 
1111
1915
AKONADI_RESOURCE_MAIN( ImapResource )
1112
1916
 
1113
1917
#include "imapresource.moc"