kolabproxyresource.cpp 26.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
    Copyright (c) 2009 Andras Mantia <amantia@kde.org>

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Library General Public License as published by
    the Free Software Foundation; either version 2 of the License, or (at your
    option) any later version.

    This library is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
    License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to the
    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
*/

#include "kolabproxyresource.h"

#include "settings.h"
23
#include "setupkolab.h"
24
25
#include "settingsadaptor.h"
#include "collectionannotationsattribute.h"
26
#include "addressbookhandler.h"
27
#include "collectiontreebuilder.h"
28

29
#include <akonadi/attributefactory.h>
30
#include <akonadi/cachepolicy.h>
31
32
#include <akonadi/collectioncreatejob.h>
#include <akonadi/collectiondeletejob.h>
33
34
35
36
37
#include <akonadi/collectionfetchjob.h>
#include <akonadi/itemcreatejob.h>
#include <akonadi/itemdeletejob.h>
#include <akonadi/itemfetchjob.h>
#include <akonadi/itemfetchscope.h>
38
#include <akonadi/itemmodifyjob.h>
39
40
#include <akonadi/monitor.h>
#include <akonadi/item.h>
41
#include <akonadi/changerecorder.h>
42
#include <akonadi/entitydisplayattribute.h>
Tobias Koenig's avatar
Tobias Koenig committed
43
#include <akonadi/entityhiddenattribute.h>
44
#include <akonadi/session.h>
45
46
47
#include <akonadi/collectionmodifyjob.h>
#include <akonadi/collectionmovejob.h>
#include <akonadi/itemmovejob.h>
48

49
#include <KLocale>
50

51
#include <QtDBus/QDBusConnection>
52
#include <QSet>
53

54
55
56
57
58
59
60
61
62
#ifdef RUNTIME_PLUGINS_STATIC
#include <QtPlugin>

Q_IMPORT_PLUGIN(akonadi_serializer_mail)
Q_IMPORT_PLUGIN(akonadi_serializer_addressee)
Q_IMPORT_PLUGIN(akonadi_serializer_kcalcore)
Q_IMPORT_PLUGIN(akonadi_serializer_contactgroup)
#endif

63
64
using namespace Akonadi;

Volker Krause's avatar
Volker Krause committed
65
66
67
68
static const char KOLAB_COLLECTION[] = "KolabCollection";
static const char KOLAB_ITEM[] = "KolabItem";
static const char IMAP_COLLECTION[] = "ImapCollection";

69
70
71
72
73
74
75
76
77
78
79
80
81
82
template <typename T>
static inline T kolabToImap( const T &kolabObject )
{
  return T( kolabObject.remoteId().toLongLong() );
}

template <typename T>
static inline T imapToKolab( const T &imapObject )
{
  T kolabObject;
  kolabObject.setRemoteId( QString::number( imapObject.id() ) );
  return kolabObject;
}

83
KolabProxyResource::KolabProxyResource( const QString &id )
84
  : ResourceBase( id )
85
{
86
  AttributeFactory::registerAttribute<CollectionAnnotationsAttribute>();
87
88
89
90
91

  new SettingsAdaptor( Settings::self() );
  QDBusConnection::sessionBus().registerObject( QLatin1String( "/Settings" ),
                            Settings::self(), QDBusConnection::ExportAdaptors );

92
93
94
  changeRecorder()->fetchCollection( true );
  changeRecorder()->itemFetchScope().fetchFullPayload();

95
  m_monitor = new Monitor( this );
96
  m_monitor->itemFetchScope().fetchFullPayload();
97

98
99
100
101
  m_collectionMonitor = new Monitor( this );
  m_collectionMonitor->fetchCollection( true );
  m_collectionMonitor->setCollectionMonitored(Collection::root());
  m_collectionMonitor->ignoreSession( Session::defaultSession() );
102

103
  connect(m_monitor, SIGNAL(itemAdded(const Akonadi::Item & , const Akonadi::Collection &)), this, SLOT(imapItemAdded(const Akonadi::Item & , const Akonadi::Collection &)));
104
  connect(m_monitor, SIGNAL(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection)), this, SLOT(imapItemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection)));
105
  connect(m_monitor, SIGNAL(itemRemoved(const Akonadi::Item &)), this, SLOT(imapItemRemoved(const Akonadi::Item &)));
106
107
108
  connect(m_collectionMonitor, SIGNAL(collectionAdded(const Akonadi::Collection &, const Akonadi::Collection &)), this, SLOT(imapCollectionAdded(const Akonadi::Collection &, const Akonadi::Collection &)));
  connect(m_collectionMonitor, SIGNAL(collectionRemoved(const Akonadi::Collection &)), this, SLOT(imapCollectionRemoved(const Akonadi::Collection &)));
  connect(m_collectionMonitor, SIGNAL(collectionChanged(const Akonadi::Collection &)), this, SLOT(imapCollectionChanged(const Akonadi::Collection &)));
109
  connect(m_collectionMonitor, SIGNAL(collectionMoved(Akonadi::Collection,Akonadi::Collection,Akonadi::Collection)), this, SLOT(imapCollectionMoved(Akonadi::Collection,Akonadi::Collection,Akonadi::Collection)) );
110

111
  setName( i18n("Kolab") );
112
113
114

  // among other things, this ensures that m_root actually exists when a new imap folder is added
  synchronizeCollectionTree();
115
}
116

117
118
119
120
121
122
123
KolabProxyResource::~KolabProxyResource()
{
}

void KolabProxyResource::retrieveCollections()
{
  kDebug() << "RETRIEVECOLLECTIONS ";
124
125
  CollectionTreeBuilder *job = new CollectionTreeBuilder( this );
  connect(job, SIGNAL(result(KJob*)), this, SLOT(retrieveCollectionsTreeDone(KJob *)));
Andras Mantia's avatar
Andras Mantia committed
126
127
}

128
void KolabProxyResource::retrieveCollectionsTreeDone(KJob* job)
Andras Mantia's avatar
Andras Mantia committed
129
130
131
{
  if ( job->error() ) {
    kWarning( ) << "Error on collection fetch:" << job->errorText();
132
    cancelTask( job->errorText() );
Andras Mantia's avatar
Andras Mantia committed
133
  } else {
134
    Collection::List imapCollections = qobject_cast<CollectionTreeBuilder*>( job )->allCollections();
135

136
137
138
139
    Collection::List kolabCollections;
    Q_FOREACH(const Collection &collection, imapCollections)
      kolabCollections.append( createCollection(collection) );
    collectionsRetrieved( kolabCollections );
140
  }
141

142
143
}

144
void KolabProxyResource::retrieveItems( const Collection &collection )
145
{
146
  kDebug() << "RETRIEVEITEMS";
Andras Mantia's avatar
Andras Mantia committed
147
  m_retrieveState = RetrieveItems;
148
  ItemFetchJob *job = new ItemFetchJob( kolabToImap( collection ) );
149
  job->fetchScope().fetchFullPayload();
Andras Mantia's avatar
Andras Mantia committed
150
  connect(job, SIGNAL(result(KJob*)), this, SLOT(retrieveItemFetchDone(KJob *)));
151
152
}

153
bool KolabProxyResource::retrieveItem( const Item &item, const QSet<QByteArray> &parts )
154
{
155
  kDebug() << "RETRIEVEITEM";
Andras Mantia's avatar
Andras Mantia committed
156
  m_retrieveState = RetrieveItem;
157
  ItemFetchJob *job = new ItemFetchJob( kolabToImap( item ) );
158
  job->fetchScope().fetchFullPayload();
159
160
161
162
163
164
165
166
  connect(job, SIGNAL(result(KJob*)), this, SLOT(retrieveItemFetchDone(KJob *)));
  return true;
}

void KolabProxyResource::retrieveItemFetchDone(KJob *job)
{
  if ( job->error() ) {
    kWarning( ) << "Error on item fetch:" << job->errorText();
Andras Mantia's avatar
Andras Mantia committed
167
    cancelTask();
168
169
170
  } else {
    Item::Id collectionId = -1;
    Item::List items = qobject_cast<ItemFetchJob*>(job)->items();
171
172
173
174
175
    if (items.size() < 1) {
      kWarning() << "Items is emtpy";
      cancelTask();
      return;
    }
176
    collectionId = items[0].storageCollectionId();
177
178
    KolabHandler *handler = m_monitoredCollections.value(collectionId);
    if (handler) {
179
180
181
182
183
184
185
186
187
188
      if (m_retrieveState == DeleteItem) {
        kDebug() << "m_retrieveState = DeleteItem";
        handler->itemDeleted(items[0]);
      } else {
        Item::List newItems = handler->translateItems(items);
        if (m_retrieveState == RetrieveItems) {
          itemsRetrieved(newItems);
        } else
          itemRetrieved(newItems[0]);
      }
Andras Mantia's avatar
Andras Mantia committed
189
      kDebug() << "RETRIEVEITEM DONE";
190
191
    } else {
      cancelTask();
192
    }
193
  }
194
195
196
197
}

void KolabProxyResource::aboutToQuit()
{
198
  qDeleteAll(m_monitoredCollections);
199
200
201
202
203
204
205
206
207
208
209
210
}

void KolabProxyResource::configure( WId windowId )
{
  Q_UNUSED( windowId );

  // TODO: this method is usually called when a new resource is being
  // added to the Akonadi setup. You can do any kind of user interaction here,
  // e.g. showing dialogs.
  // The given window ID is usually useful to get the correct
  // "on top of parent" behavior if the running window manager applies any kind
  // of focus stealing prevention technique
211

212
213
214
215
216
217
218
219
220
  QPointer<SetupKolab> kolabConfigDialog( new SetupKolab( this, windowId ) );
  if ( kolabConfigDialog->exec() == QDialog::Accepted ) {
    emit configurationDialogAccepted();
  }
  else {
    emit configurationDialogRejected();
  }

  delete kolabConfigDialog;
221
222
}

223
void KolabProxyResource::itemAdded( const Item &item, const Collection &collection )
224
{
225
  kDebug() << "ITEMADDED";
226

227
  Item kolabItem( item );
228
//   kDebug() << "Item added " << item.id() << collection.remoteId() << collection.id();
229

230
  const Collection imapCollection = kolabToImap( collection );
231

232
  KolabHandler *handler  = m_monitoredCollections.value(imapCollection.id());
Volker Krause's avatar
Volker Krause committed
233
  if ( !handler ) {
234
    kWarning() << "No handler found for collection" << collection << ", available handlers: " << m_monitoredCollections;
235
    cancelTask();
236
    return;
237
  }
238
  Item imapItem(handler->contentMimeTypes()[0]);
239
  handler->toKolabFormat( kolabItem, imapItem );
240

241
  ItemCreateJob *cjob = new ItemCreateJob(imapItem, imapCollection);
Volker Krause's avatar
Volker Krause committed
242
243
244
245
246
247
248
249
250
251
  cjob->setProperty( KOLAB_ITEM, QVariant::fromValue( kolabItem ) );
  cjob->setProperty( IMAP_COLLECTION, QVariant::fromValue( imapCollection ) );
  connect( cjob, SIGNAL(result(KJob*)), SLOT(imapItemCreationResult(KJob*)) );
}

void KolabProxyResource::imapItemCreationResult(KJob* job)
{
  if ( job->error() ) {
    cancelTask( job->errorText() );
    return;
252
253
  }

Volker Krause's avatar
Volker Krause committed
254
255
256
257
258
259
260
261
  ItemCreateJob *cjob = qobject_cast<ItemCreateJob*>( job );
  const Item imapItem = cjob->item();
  Item kolabItem = cjob->property( KOLAB_ITEM ).value<Item>();
  // TODO add accessor to ItemCreateJob for the parent collection
  const Collection imapCollection = cjob->property( IMAP_COLLECTION ).value<Collection>();

  KolabHandler *handler  = m_monitoredCollections.value(imapCollection.id());
  Q_ASSERT( handler );
262
  handler->itemAdded(imapItem);
263
  m_excludeAppend << imapItem.id();
264

Volker Krause's avatar
Volker Krause committed
265
  kolabItem.setRemoteId( QString::number( imapItem.id() ) );
266
  changeCommitted( kolabItem );
267
268
}

269
void KolabProxyResource::itemChanged( const Item &kolabItem, const QSet<QByteArray> &parts )
270
{
Casey Link's avatar
Casey Link committed
271
  Q_UNUSED( parts );
272
  kDebug() << "ITEMCHANGED" << kolabItem.id() << kolabItem.remoteId();
273

274
  ItemFetchJob* job = new ItemFetchJob( kolabToImap( kolabItem ), this );
Volker Krause's avatar
Volker Krause committed
275
276
277
278
279
280
281
282
283
  job->setProperty( KOLAB_ITEM, QVariant::fromValue( kolabItem ) );
  connect( job, SIGNAL(result(KJob*)), SLOT(imapItemUpdateFetchResult(KJob*)) );
}

void KolabProxyResource::imapItemUpdateFetchResult(KJob* job)
{
  if ( job->error() ) {
    cancelTask( job->errorText() );
    return;
284
  }
285

286
287
  const Item kolabItem = job->property( KOLAB_ITEM ).value<Item>();

Volker Krause's avatar
Volker Krause committed
288
  ItemFetchJob *fetchJob = qobject_cast<ItemFetchJob*>( job );
289
290
291
  Q_ASSERT( fetchJob->items().size() <= 1 );
  if ( fetchJob->items().size() == 1 ) {
    Item imapItem = fetchJob->items().first();
Volker Krause's avatar
Volker Krause committed
292

293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
    KolabHandler *handler = m_monitoredCollections.value( imapItem.storageCollectionId() );
    if (!handler) {
      kWarning() << "No handler found";
      cancelTask();
      return;
    }

    handler->toKolabFormat( kolabItem , imapItem );
    ItemModifyJob *mjob = new ItemModifyJob( imapItem );
    mjob->setProperty( KOLAB_ITEM, fetchJob->property( KOLAB_ITEM ) );
    connect( mjob, SIGNAL(result(KJob*)), SLOT(imapItemUpdateResult(KJob*)) );
  } else {
    // HACK FIXME how can that happen at all?
    CollectionFetchJob *fetch = new CollectionFetchJob( Collection( kolabItem.storageCollectionId() ), CollectionFetchJob::Base, this );
    fetch->setProperty( KOLAB_ITEM, QVariant::fromValue( kolabItem ) );
Tobias Koenig's avatar
Tobias Koenig committed
308
    connect( fetch, SIGNAL(result(KJob*)), SLOT(imapItemUpdateCollectionFetchResult(KJob*)) );
309
310
311
312
313
314
315
316
317
318
319
320
321
  }
}

void KolabProxyResource::imapItemUpdateCollectionFetchResult( KJob* job )
{
  CollectionFetchJob *fetchJob = qobject_cast<CollectionFetchJob*>( job );
  if ( job->error() || fetchJob->collections().size() != 1 ) {
    cancelTask( job->errorText() );
    return;
  }

  const Item kolabItem = job->property( KOLAB_ITEM ).value<Item>();
  const Collection kolabCollection = fetchJob->collections().first();
322
  const Collection imapCollection = kolabToImap( kolabCollection );
323
324
325

  KolabHandler *handler  = m_monitoredCollections.value(imapCollection.id());
  if ( !handler ) {
326
    kWarning() << "No handler found";
327
    cancelTask();
328
329
    return;
  }
330
331
  Item imapItem(handler->contentMimeTypes()[0]);
  handler->toKolabFormat( kolabItem, imapItem );
332

333
334
335
336
  ItemCreateJob *cjob = new ItemCreateJob(imapItem, imapCollection);
  cjob->setProperty( KOLAB_ITEM, QVariant::fromValue( kolabItem ) );
  cjob->setProperty( IMAP_COLLECTION, QVariant::fromValue( imapCollection ) );
  connect( cjob, SIGNAL(result(KJob*)), SLOT(imapItemCreationResult(KJob*)) );
Volker Krause's avatar
Volker Krause committed
337
}
338

Volker Krause's avatar
Volker Krause committed
339
340
341
342
343
344
345
void KolabProxyResource::imapItemUpdateResult(KJob* job)
{
  if ( job->error() ) {
    cancelTask( job->errorText() );
    return;
  }
  const Item kolabItem = job->property( KOLAB_ITEM ).value<Item>();
346
  changeCommitted( kolabItem );
347
348
}

349
350
351
352
353
354
355
void KolabProxyResource::itemMoved(const Akonadi::Item& item, const Akonadi::Collection& collectionSource, const Akonadi::Collection& collectionDestination)
{
  Q_UNUSED( collectionSource );
  new ItemMoveJob( kolabToImap( item ), kolabToImap( collectionDestination ), this );
  changeCommitted( item );
}

356
void KolabProxyResource::itemRemoved( const Item &item )
357
{
358
  kDebug() << "ITEMREMOVED";
359
  kDebug() << "Item removed " << item.id() << item.remoteId();
360
  const Item imapItem( item.remoteId().toUInt() );
361
362
363
  ItemDeleteJob *djob = new ItemDeleteJob( imapItem );
  changeCommitted( item );
  Q_UNUSED(djob);
364
365
}

366
367
void KolabProxyResource::collectionAdded(const Akonadi::Collection& collection, const Akonadi::Collection& parent)
{
368
  if ( KolabHandler::kolabTypeForCollection( collection ).isEmpty() ) {
369
370
371
372
    kWarning() << "Collection " << collection.name() << collection.id() << collection.isValid()
               << "doesn't have kolab type set. isValid = "
               << "; parent is " << parent.name() << parent.id() << parent.isValid();
    cancelTask( QLatin1String( "Collection doesn't have kolab type." ) );
373
374
    Q_ASSERT_X( false, "collectionAdded", "Colection doesn't have kolab type set. Crashing..." );
  }
375

376
377
378
  Collection imapCollection( collection );
  imapCollection.setId( -1 );
  imapCollection.setRemoteId( QString() );
Volker Krause's avatar
Volker Krause committed
379
  imapCollection.setContentMimeTypes( QStringList() << Collection::mimeType() << QLatin1String( "message/rfc822" ) );
380
  const Collection imapParent = kolabToImap( parent );
381
  imapCollection.setParentCollection( imapParent );
382
383
384
385
386
  CollectionAnnotationsAttribute* attr =
    imapCollection.attribute<CollectionAnnotationsAttribute>( Collection::AddIfMissing );
  QMap<QByteArray, QByteArray> annotations = attr->annotations();
  annotations["/vendor/kolab/folder-type"] = KolabHandler::kolabTypeForCollection( collection );
  attr->setAnnotations( annotations );
387
388

  CollectionCreateJob *job = new CollectionCreateJob( imapCollection, this );
Volker Krause's avatar
Volker Krause committed
389
390
391
392
393
394
395
396
397
398
399
400
401
402
  job->setProperty( KOLAB_COLLECTION, QVariant::fromValue( collection ) );
  connect( job, SIGNAL(result(KJob*)), SLOT(imapFolderCreateResult(KJob*)) );
}

void KolabProxyResource::imapFolderCreateResult(KJob* job)
{
  if ( job->error() ) {
    cancelTask( job->errorText() );
  } else {
    const Collection imapCollection = qobject_cast<CollectionCreateJob*>( job )->collection();
    Collection kolabCollection = job->property( KOLAB_COLLECTION ).value<Collection>();
    kolabCollection.setRemoteId( QString::number( imapCollection.id() ) );
    changeCommitted( kolabCollection );
  }
403
404
}

Volker Krause's avatar
Volker Krause committed
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
void KolabProxyResource::applyAttributesToImap( Collection &imapCollection, const Akonadi::Collection &kolabCollection )
{
  static const EntityDisplayAttribute eda;
  static const EntityHiddenAttribute hidden;
  foreach( const Akonadi::Attribute *attr, kolabCollection.attributes() )
  {
    if ( attr->type() == hidden.type() )
      // Don't propagate HIDDEN because that would hide collections in korg, kab too.
      continue;

    if ( attr->type() == eda.type() )
      // Don't propagate DISPLAYATTRIBUTE because that would cause icons from the imap resource to use kolab icons.
      continue;

    if ( attr->type() == "AccessRights" )
      continue;

Till Adam's avatar
Till Adam committed
422
    //kDebug() << "cloning" << attr->type();
Volker Krause's avatar
Volker Krause committed
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
    imapCollection.addAttribute( attr->clone() );
  }
}

void KolabProxyResource::applyAttributesFromImap( Collection &kolabCollection, const Akonadi::Collection &imapCollection )
{
  static const EntityDisplayAttribute eda;
  static const EntityHiddenAttribute hidden;
  foreach( const Akonadi::Attribute *attr, imapCollection.attributes() )
  {
    if ( attr->type() == hidden.type() )
      continue;

    if ( attr->type() == eda.type() )
      continue;

    if ( attr->type() == "AccessRights" )
      continue;

Till Adam's avatar
Till Adam committed
442
    //kDebug() << "cloning" << attr->type();
Volker Krause's avatar
Volker Krause committed
443
444
445
446
447
    kolabCollection.addAttribute( attr->clone() );
  }
}


448
449
450
451
452
453
454
455
void KolabProxyResource::collectionChanged(const Akonadi::Collection& collection)
{
  Collection imapCollection;
  imapCollection.setId( collection.remoteId().toLongLong() );
  imapCollection.setName( collection.name() );
  imapCollection.setCachePolicy( collection.cachePolicy() );
  imapCollection.setRights( collection.rights() );

Volker Krause's avatar
Volker Krause committed
456
457
  applyAttributesToImap( imapCollection, collection );

458
  CollectionModifyJob *job = new CollectionModifyJob( imapCollection, this );
459
  Q_UNUSED( job );
460
461
462
463
  // TODO wait for the result
  changeCommitted( collection );
}

464
465
466
467
468
469
470
void KolabProxyResource::collectionMoved(const Akonadi::Collection& collection, const Akonadi::Collection& source, const Akonadi::Collection& destination)
{
  Q_UNUSED( source );
  new CollectionMoveJob( kolabToImap( collection ), kolabToImap( destination ), this );
  changeCommitted( collection );
}

471
472
void KolabProxyResource::collectionRemoved(const Akonadi::Collection& collection)
{
473
  Collection imapCollection = kolabToImap( collection );
474
475

  CollectionDeleteJob *job = new CollectionDeleteJob( imapCollection, this );
476
  Q_UNUSED( job );
477
478
479
480
  // TODO wait for result
  changeCommitted( collection );
}

481
482
483
484
485
486
487
488
489
490
491
492
493
void KolabProxyResource::deleteImapItem(const Item& item)
{
  kDebug() << "DELETEIMAPITEM";
  ItemDeleteJob *djob = new ItemDeleteJob( item );
  Q_UNUSED(djob);
}

void KolabProxyResource::addImapItem(const Item& item, Akonadi::Entity::Id collectionId)
{
  kDebug() << "ADDITEMTOIMAP";
  new ItemCreateJob( item, Collection(collectionId) );
}

494
void KolabProxyResource::imapItemAdded(const Item& item, const Collection &collection)
495
{
496
  kDebug() << "imapItemAdded " << item.id() << collection.id() << Collection::root().id();
497
498
499
500
501
  if (m_excludeAppend.contains(item.id()))   {
    kDebug() << "item already present";
    m_excludeAppend.removeAll(item.id());
    return;
  }
502
//TODO: slow, would be nice if ItemCreateJob would work with a Collection having only the remoteId set
503
504
  const Collection kolabCol = imapToKolab( collection );
  CollectionFetchJob *job = new CollectionFetchJob( kolabCol, CollectionFetchJob::Base, this );
505
  connect(job, SIGNAL(result(KJob*)), this, SLOT(collectionFetchDone(KJob *)));
506
507
  m_ids[job] = QString::number(collection.id());
  m_items[job] = item;
508
509
510
511
512
513
514
515
516
517
}

void KolabProxyResource::collectionFetchDone(KJob *job)
{
  if ( job->error() ) {
    kWarning( ) << "Error on collection fetch:" << job->errorText();
  } else {
    Collection c;
    Collection::List collections = qobject_cast<CollectionFetchJob*>(job)->collections();
    foreach( const Collection &col, collections ) {
518
      if (col.remoteId() == m_ids[job]) {
519
520
521
522
        c = col;
        break;
      }
    }
523

524
525
526
527
528
529
530
531
532
533
    KolabHandler *handler = m_monitoredCollections.value(c.remoteId().toUInt());
    if (!handler) {
      kWarning() << "No handler found";
      return;
    }

    Item::List newItems = handler->translateItems(Item::List() << m_items[job]);
    if (!newItems.isEmpty()) {
      ItemCreateJob *cjob = new ItemCreateJob( newItems[0],  c );
      connect(cjob, SIGNAL(result(KJob*)), this, SLOT(itemCreatedDone(KJob *)));
534
535
    }
  }
536
537
  m_ids.remove(job);
  m_items.remove(job);
538
539
540
541
542
543
544
545
546
547
}

void KolabProxyResource::itemCreatedDone(KJob *job)
{
  if ( job->error() ) {
    kWarning( ) << "Error on creating item:" << job->errorText();
  } else {
  }
}

548
void KolabProxyResource::imapItemRemoved(const Item& item)
549
{
550
  kDebug() << "IMAPITEMREMOVED";
551
  const Item kolabItem = imapToKolab( item );
552
  Q_FOREACH(KolabHandler *handler, m_monitoredCollections) {
553
554
    handler->itemDeleted(item);
  }
555
  ItemDeleteJob *job = new ItemDeleteJob( kolabItem, this );
556
  Q_UNUSED( job );
557
558
559
560
561
562
563
}

void KolabProxyResource::imapItemMoved(const Akonadi::Item& item, const Akonadi::Collection& collectionSource, const Akonadi::Collection& collectionDestination)
{
  kDebug();
  Q_UNUSED( collectionSource );
  new ItemMoveJob( imapToKolab( item ), imapToKolab( collectionDestination ), this );
564
565
}

566
void KolabProxyResource::imapCollectionAdded(const Collection &collection, const Collection &parent)
567
{
Casey Link's avatar
Casey Link committed
568
  Q_UNUSED( parent );
569
570
571
  if ( collection.resource() == identifier() ) // just to be sure...
    return;

572
  kDebug() << "IMAPCOLLECTIONADDED";
573
574
  if ( m_monitoredCollections.contains(collection.id()) ) {
    // something is wrong, so better reload out collection tree
575
    kDebug() << "IMAPCOLLECTIONADDED ABORT";
576
    synchronizeCollectionTree();
577
    return;
578
  }
579

580
581
582
  if ( registerHandlerForCollection( collection ) ) {
    const Collection kolabCollection = createCollection( collection );
    CollectionCreateJob *job = new CollectionCreateJob( kolabCollection, this );
Volker Krause's avatar
Volker Krause committed
583
    connect( job, SIGNAL(result(KJob*)), SLOT(kolabFolderChangeResult(KJob*)) );
584
  }
585
586
}

587
void KolabProxyResource::imapCollectionChanged(const Collection &collection)
588
{
589
  if ( collection.resource() == identifier() ) // just to be sure...
590
    return;
591

592
  //kDebug() << "IMAPCOLLECTIONCHANGED";
593
  if ( !m_monitoredCollections.contains(collection.id()) ) {
Volker Krause's avatar
Volker Krause committed
594
595
596
597
598
599
    // check if this is a Kolab folder at all, if yet something is wrong
    CollectionAnnotationsAttribute *annotationsAttribute =
     collection.attribute<CollectionAnnotationsAttribute>();
    bool isKolabFolder = false;
    if ( annotationsAttribute ) {
      const QMap<QByteArray, QByteArray> annotations = annotationsAttribute->annotations();
600
601
      QByteArray folderType = annotations[ "/vendor/kolab/folder-type" ];
      isKolabFolder = !folderType.isEmpty() && folderType != "mail";
Volker Krause's avatar
Volker Krause committed
602
    }
603

Volker Krause's avatar
Volker Krause committed
604
605
606
    if ( isKolabFolder ) {
      synchronizeCollectionTree();
      return;
607
    }
Volker Krause's avatar
Volker Krause committed
608
609
610
611
    // not a Kolab folder, no need to resync the tree, just try to update a possible structural collection
    // if that fails it's not in our tree -> we don't care
    Collection kolabCollection = createCollection( collection );
    CollectionModifyJob *job = new CollectionModifyJob( kolabCollection, this );
612
    Q_UNUSED( job );
Volker Krause's avatar
Volker Krause committed
613
614
615
616
617
  } else {
    // Kolab folder we already have in our tree, if the update fails, reload our tree
    Collection kolabCollection = createCollection( collection );
    CollectionModifyJob *job = new CollectionModifyJob( kolabCollection, this );
    connect( job, SIGNAL(result(KJob*)), SLOT(kolabFolderChangeResult(KJob*)) );
618
619
620
  }
}

621
622
623
624
625
626
627
void KolabProxyResource::imapCollectionMoved(const Akonadi::Collection& collection, const Akonadi::Collection& source, const Akonadi::Collection& destination)
{
  kDebug();
  Q_UNUSED( source );
  new CollectionMoveJob( imapToKolab( collection ), imapToKolab( destination ), this );
}

Volker Krause's avatar
Volker Krause committed
628
void KolabProxyResource::kolabFolderChangeResult(KJob* job)
629
{
630
631
632
633
634
  if ( job->error() ) {
    // something went wrong or the change was too complex to handle in the above slots,
    // so re-sync the entire tree.
    kDebug() << "Re-syncing collection tree as incremental changes did not succeed." << job->errorText();
    synchronizeCollectionTree();
635
636
637
  }
}

Volker Krause's avatar
Volker Krause committed
638
void KolabProxyResource::imapCollectionRemoved(const Collection &imapCollection)
639
{
Volker Krause's avatar
Volker Krause committed
640
  if ( imapCollection.resource() == identifier() ) // just to be sure...
641
642
    return;

643
  kDebug() << "IMAPCOLLECTIONREMOVED";
Volker Krause's avatar
Volker Krause committed
644
645
646
  Collection kolabCollection;
  kolabCollection.setRemoteId( QString::number( imapCollection.id() ) );
  new CollectionDeleteJob( kolabCollection );
647

Volker Krause's avatar
Volker Krause committed
648
  KolabHandler *handler = m_monitoredCollections.value(imapCollection.id());
649
  delete handler;
Volker Krause's avatar
Volker Krause committed
650
  m_monitoredCollections.remove(imapCollection.id());
651
652
}

653
Collection KolabProxyResource::createCollection(const Collection& imapCollection)
654
655
{
  Collection c;
Volker Krause's avatar
Volker Krause committed
656
657
658
659
660
661
662
663
  if ( imapCollection.parentCollection() == Collection::root() ) {
    c.setParentCollection( Collection::root() );
    CachePolicy policy;
    policy.setInheritFromParent( false );
    policy.setCacheTimeout( -1 );
    policy.setLocalParts( QStringList() << QLatin1String( "ALL" ) );
    c.setCachePolicy( policy );
  } else {
664
    c.parentCollection().setRemoteId( QString::number( imapCollection.parentCollection().id() ) );
Volker Krause's avatar
Volker Krause committed
665
666
  }
  c.setName( imapCollection.name() );
Volker Krause's avatar
Volker Krause committed
667
  EntityDisplayAttribute *imapAttr = imapCollection.attribute<EntityDisplayAttribute>();
668
  EntityDisplayAttribute *kolabAttr = c.attribute<EntityDisplayAttribute>( Collection::AddIfMissing );
Volker Krause's avatar
Volker Krause committed
669
670
671
672
  if ( imapAttr ) {
    if ( imapAttr->iconName() == QLatin1String( "mail-folder-inbox" ) ) {
      kolabAttr->setDisplayName( i18n( "My Data" ) );
      kolabAttr->setIconName( QLatin1String( "view-pim-summary" ) );
Volker Krause's avatar
Volker Krause committed
673
674
675
    } else if ( imapCollection.parentCollection() == Collection::root() ) {
      c.setName( i18n( "Kolab (%1)", imapAttr->displayName() ) );
      kolabAttr->setIconName( QLatin1String( "kolab" ) );
Volker Krause's avatar
Volker Krause committed
676
677
678
679
    } else {
      kolabAttr->setDisplayName( imapAttr->displayName() );
      kolabAttr->setIconName( imapAttr->iconName() );
    }
Volker Krause's avatar
Volker Krause committed
680
681
682
683
684
  } else {
    if ( imapCollection.parentCollection() == Collection::root() ) {
      c.setName( i18n( "Kolab (%1)", imapCollection.name() ) );
      kolabAttr->setIconName( QLatin1String( "kolab" ) );
    }
685
  }
Volker Krause's avatar
Volker Krause committed
686
  applyAttributesFromImap( c, imapCollection );
687
  KolabHandler *handler = m_monitoredCollections.value(imapCollection.id());
688
689
  QStringList contentTypes;
  contentTypes.append( Collection::mimeType() );
690
  if ( handler ) {
691
    contentTypes.append( handler->contentMimeTypes() );
692
    kolabAttr->setIconName( handler->iconName() );
693
694

    // hide Kolab folders on the IMAP server
Tobias Koenig's avatar
Tobias Koenig committed
695
    if ( !imapCollection.hasAttribute<EntityHiddenAttribute>() ) {
696
      Collection hiddenImapCol( imapCollection );
Tobias Koenig's avatar
Tobias Koenig committed
697
      hiddenImapCol.attribute<EntityHiddenAttribute>( Collection::AddIfMissing );
698
699
      new CollectionModifyJob( hiddenImapCol, this );
    }
700
  }
701
  c.setContentMimeTypes( contentTypes );
702
  c.setRights( imapCollection.rights() );
703
704
705
706
  c.setRemoteId(QString::number(imapCollection.id()));
  return c;
}

707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
bool KolabProxyResource::registerHandlerForCollection(const Akonadi::Collection& imapCollection)
{
  CollectionAnnotationsAttribute *annotationsAttribute =
      imapCollection.attribute<CollectionAnnotationsAttribute>();
  if ( annotationsAttribute ) {
    QMap<QByteArray, QByteArray> annotations = annotationsAttribute->annotations();

    KolabHandler *handler = KolabHandler::createHandler(annotations["/vendor/kolab/folder-type"]);
    if ( handler ) {
      connect(handler, SIGNAL(deleteItemFromImap(const Akonadi::Item&)), this, SLOT(deleteImapItem(const Akonadi::Item&)));
      connect(handler, SIGNAL(addItemToImap(const Akonadi::Item&, Akonadi::Entity::Id)), this, SLOT(addImapItem(const Akonadi::Item&, Akonadi::Entity::Id)));
      m_monitor->setCollectionMonitored(imapCollection);
      m_monitoredCollections.insert(imapCollection.id(), handler);
      return true;
    }
  }

  return false;
}
726

727
728
729
AKONADI_RESOURCE_MAIN( KolabProxyResource )

#include "kolabproxyresource.moc"