• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepimlibs API Reference
  • KDE Home
  • Contact Us
 

akonadi

  • sources
  • kde-4.14
  • kdepimlibs
  • akonadi
collectionfetchjob.cpp
1 /*
2  Copyright (c) 2006 - 2007 Volker Krause <vkrause@kde.org>
3 
4  This library is free software; you can redistribute it and/or modify it
5  under the terms of the GNU Library General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or (at your
7  option) any later version.
8 
9  This library is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12  License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to the
16  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  02110-1301, USA.
18 */
19 
20 #include "collectionfetchjob.h"
21 
22 #include "imapparser_p.h"
23 #include "job_p.h"
24 #include "protocol_p.h"
25 #include "protocolhelper_p.h"
26 #include "entity_p.h"
27 #include "collectionfetchscope.h"
28 #include "collectionutils_p.h"
29 
30 #include <kdebug.h>
31 #include <KLocalizedString>
32 
33 #include <QtCore/QHash>
34 #include <QtCore/QStringList>
35 #include <QtCore/QTimer>
36 
37 using namespace Akonadi;
38 
39 class Akonadi::CollectionFetchJobPrivate : public JobPrivate
40 {
41 public:
42  CollectionFetchJobPrivate(CollectionFetchJob *parent)
43  : JobPrivate(parent)
44  , mEmitTimer(0)
45  , mBasePrefetch(false)
46  {
47 
48  }
49 
50  void init()
51  {
52  mEmitTimer = new QTimer(q_ptr);
53  mEmitTimer->setSingleShot(true);
54  mEmitTimer->setInterval(100);
55  q_ptr->connect(mEmitTimer, SIGNAL(timeout()), q_ptr, SLOT(timeout()));
56  }
57 
58  Q_DECLARE_PUBLIC(CollectionFetchJob)
59 
60  CollectionFetchJob::Type mType;
61  Collection mBase;
62  Collection::List mBaseList;
63  Collection::List mCollections;
64  CollectionFetchScope mScope;
65  Collection::List mPendingCollections;
66  QTimer *mEmitTimer;
67  bool mBasePrefetch;
68  Collection::List mPrefetchList;
69 
70  void aboutToFinish()
71  {
72  timeout();
73  }
74 
75  void timeout()
76  {
77  Q_Q(CollectionFetchJob);
78 
79  mEmitTimer->stop(); // in case we are called by result()
80  if (!mPendingCollections.isEmpty()) {
81  if (!q->error()) {
82  emit q->collectionsReceived(mPendingCollections);
83  }
84  mPendingCollections.clear();
85  }
86  }
87 
88  void subJobCollectionReceived(const Akonadi::Collection::List &collections)
89  {
90  mPendingCollections += collections;
91  if (!mEmitTimer->isActive()) {
92  mEmitTimer->start();
93  }
94  }
95 
96  QString jobDebuggingString() const
97  {
98  if (mBase.isValid()) {
99  return QString::fromLatin1("Collection Id %1").arg(mBase.id());
100  } else if (CollectionUtils::hasValidHierarchicalRID(mBase)) {
101  return QString::fromUtf8(QByteArray(QByteArray("(") + ProtocolHelper::hierarchicalRidToByteArray(mBase) + QByteArray(")")));
102  } else {
103  return QString::fromLatin1("Collection RemoteId %1").arg(mBase.remoteId());
104  }
105  }
106 };
107 
108 CollectionFetchJob::CollectionFetchJob(const Collection &collection, Type type, QObject *parent)
109  : Job(new CollectionFetchJobPrivate(this), parent)
110 {
111  Q_D(CollectionFetchJob);
112  d->init();
113 
114  d->mBase = collection;
115  d->mType = type;
116 }
117 
118 CollectionFetchJob::CollectionFetchJob(const Collection::List &cols, QObject *parent)
119  : Job(new CollectionFetchJobPrivate(this), parent)
120 {
121  Q_D(CollectionFetchJob);
122  d->init();
123 
124  Q_ASSERT(!cols.isEmpty());
125  if (cols.size() == 1) {
126  d->mBase = cols.first();
127  } else {
128  d->mBaseList = cols;
129  }
130  d->mType = CollectionFetchJob::Base;
131 }
132 
133 CollectionFetchJob::CollectionFetchJob(const Collection::List &cols, Type type, QObject *parent)
134  : Job(new CollectionFetchJobPrivate(this), parent)
135 {
136  Q_D(CollectionFetchJob);
137  d->init();
138 
139  Q_ASSERT(!cols.isEmpty());
140  if (cols.size() == 1) {
141  d->mBase = cols.first();
142  } else {
143  d->mBaseList = cols;
144  }
145  d->mType = type;
146 }
147 
148 CollectionFetchJob::CollectionFetchJob(const QList<Collection::Id> &cols, Type type, QObject *parent)
149  : Job(new CollectionFetchJobPrivate(this), parent)
150 {
151  Q_D(CollectionFetchJob);
152  d->init();
153 
154  Q_ASSERT(!cols.isEmpty());
155  if (cols.size() == 1) {
156  d->mBase = Collection(cols.first());
157  } else {
158  foreach (Collection::Id id, cols) {
159  d->mBaseList.append(Collection(id));
160  }
161  }
162  d->mType = type;
163 }
164 
165 CollectionFetchJob::~CollectionFetchJob()
166 {
167 }
168 
169 Akonadi::Collection::List CollectionFetchJob::collections() const
170 {
171  Q_D(const CollectionFetchJob);
172 
173  return d->mCollections;
174 }
175 
176 void CollectionFetchJob::doStart()
177 {
178  Q_D(CollectionFetchJob);
179 
180  if (!d->mBaseList.isEmpty()) {
181  if (d->mType == Recursive) {
182  // Because doStart starts several subjobs and @p cols could contain descendants of
183  // other elements in the list, if type is Recusrive, we could end up with duplicates in the result.
184  // To fix this we require an initial fetch of @p cols with Base and RetrieveAncestors,
185  // Iterate over that result removing intersections and then perform the Recursive fetch on
186  // the remainder.
187  d->mBasePrefetch = true;
188  // No need to connect to the collectionsReceived signal here. This job is internal. The
189  // result needs to be filtered through filterDescendants before it is useful.
190  new CollectionFetchJob(d->mBaseList, NonOverlappingRoots, this);
191  } else if (d->mType == NonOverlappingRoots) {
192  foreach (const Collection &col, d->mBaseList) {
193  // No need to connect to the collectionsReceived signal here. This job is internal. The (aggregated)
194  // result needs to be filtered through filterDescendants before it is useful.
195  CollectionFetchJob *subJob = new CollectionFetchJob(col, Base, this);
196  subJob->fetchScope().setAncestorRetrieval(Akonadi::CollectionFetchScope::All);
197  }
198  } else {
199  foreach (const Collection &col, d->mBaseList) {
200  CollectionFetchJob *subJob = new CollectionFetchJob(col, d->mType, this);
201  connect(subJob, SIGNAL(collectionsReceived(Akonadi::Collection::List)), SLOT(subJobCollectionReceived(Akonadi::Collection::List)));
202  subJob->setFetchScope(fetchScope());
203  }
204  }
205  return;
206  }
207 
208  if (!d->mBase.isValid() && d->mBase.remoteId().isEmpty()) {
209  setError(Unknown);
210  setErrorText(i18n("Invalid collection given."));
211  emitResult();
212  return;
213  }
214 
215  QByteArray command = d->newTag();
216  if (!d->mBase.isValid()) {
217  if (CollectionUtils::hasValidHierarchicalRID(d->mBase)) {
218  command += " HRID";
219  } else {
220  command += " " AKONADI_CMD_RID;
221  }
222  }
223  command += " LIST ";
224 
225  if (d->mBase.isValid()) {
226  command += QByteArray::number(d->mBase.id());
227  } else if (CollectionUtils::hasValidHierarchicalRID(d->mBase)) {
228  command += '(' + ProtocolHelper::hierarchicalRidToByteArray(d->mBase) + ')';
229  } else {
230  command += ImapParser::quote(d->mBase.remoteId().toUtf8());
231  }
232 
233  command += ' ';
234  switch (d->mType) {
235  case Base:
236  command += "0 (";
237  break;
238  case FirstLevel:
239  command += "1 (";
240  break;
241  case Recursive:
242  command += "INF (";
243  break;
244  default:
245  Q_ASSERT(false);
246  }
247 
248  QList<QByteArray> filter;
249  if (!d->mScope.resource().isEmpty()) {
250  filter.append("RESOURCE");
251  // FIXME: Does this need to be quoted??
252  filter.append(d->mScope.resource().toUtf8());
253  }
254 
255  if (!d->mScope.contentMimeTypes().isEmpty()) {
256  filter.append("MIMETYPE");
257  QList<QByteArray> mts;
258  foreach (const QString &mt, d->mScope.contentMimeTypes()) {
259  // FIXME: Does this need to be quoted??
260  mts.append(mt.toUtf8());
261  }
262  filter.append('(' + ImapParser::join(mts, " ") + ')');
263  }
264 
265  switch (d->mScope.listFilter()) {
266  case CollectionFetchScope::Display:
267  filter.append("DISPLAY TRUE");
268  break;
269  case CollectionFetchScope::Sync:
270  filter.append("SYNC TRUE");
271  break;
272  case CollectionFetchScope::Index:
273  filter.append("INDEX TRUE");
274  break;
275  case CollectionFetchScope::Enabled:
276  filter.append("ENABLED TRUE");
277  break;
278  case CollectionFetchScope::NoFilter:
279  break;
280  default:
281  Q_ASSERT(false);
282  }
283 
284  QList<QByteArray> options;
285  if (d->mScope.includeStatistics()) {
286  options.append("STATISTICS");
287  options.append("true");
288  }
289  if (d->mScope.ancestorRetrieval() != CollectionFetchScope::None) {
290  options.append("ANCESTORS");
291  switch (d->mScope.ancestorRetrieval()) {
292  case CollectionFetchScope::None:
293  options.append("0");
294  break;
295  case CollectionFetchScope::Parent:
296  options.append("1");
297  break;
298  case CollectionFetchScope::All:
299  options.append("INF");
300  break;
301  default:
302  Q_ASSERT(false);
303  }
304  }
305 
306  command += ImapParser::join(filter, " ") + ") (" + ImapParser::join(options, " ") + ")\n";
307  d->writeData(command);
308 }
309 
310 void CollectionFetchJob::doHandleResponse(const QByteArray &tag, const QByteArray &data)
311 {
312  Q_D(CollectionFetchJob);
313 
314  if (d->mBasePrefetch || d->mType == NonOverlappingRoots) {
315  return;
316  }
317 
318  if (tag == "*") {
319  Collection collection;
320  ProtocolHelper::parseCollection(data, collection);
321  if (!collection.isValid()) {
322  return;
323  }
324 
325  collection.d_ptr->resetChangeLog();
326  d->mCollections.append(collection);
327  d->mPendingCollections.append(collection);
328  if (!d->mEmitTimer->isActive()) {
329  d->mEmitTimer->start();
330  }
331  return;
332  }
333  kDebug() << "Unhandled server response" << tag << data;
334 }
335 
336 void CollectionFetchJob::setResource(const QString &resource)
337 {
338  Q_D(CollectionFetchJob);
339 
340  d->mScope.setResource(resource);
341 }
342 
343 static Collection::List filterDescendants(const Collection::List &list)
344 {
345  Collection::List result;
346 
347  QVector<QList<Collection::Id> > ids;
348  foreach (const Collection &collection, list) {
349  QList<Collection::Id> ancestors;
350  Collection parent = collection.parentCollection();
351  ancestors << parent.id();
352  if (parent != Collection::root()) {
353  while (parent.parentCollection() != Collection::root()) {
354  parent = parent.parentCollection();
355  QList<Collection::Id>::iterator i = qLowerBound(ancestors.begin(), ancestors.end(), parent.id());
356  ancestors.insert(i, parent.id());
357  }
358  }
359  ids << ancestors;
360  }
361 
362  QSet<Collection::Id> excludeList;
363  foreach (const Collection &collection, list) {
364  int i = 0;
365  foreach (const QList<Collection::Id> &ancestors, ids) {
366  if (qBinaryFind(ancestors, collection.id()) != ancestors.end()) {
367  excludeList.insert(list.at(i).id());
368  }
369  ++i;
370  }
371  }
372 
373  foreach (const Collection &collection, list) {
374  if (!excludeList.contains(collection.id())) {
375  result.append(collection);
376  }
377  }
378 
379  return result;
380 }
381 
382 void CollectionFetchJob::slotResult(KJob *job)
383 {
384  Q_D(CollectionFetchJob);
385 
386  CollectionFetchJob *list = qobject_cast<CollectionFetchJob *>(job);
387  Q_ASSERT(job);
388  if (d->mBasePrefetch) {
389  d->mBasePrefetch = false;
390  const Collection::List roots = list->collections();
391  Job::slotResult(job);
392  Q_ASSERT(!hasSubjobs());
393  if (!job->error()) {
394  foreach (const Collection &col, roots) {
395  CollectionFetchJob *subJob = new CollectionFetchJob(col, d->mType, this);
396  connect(subJob, SIGNAL(collectionsReceived(Akonadi::Collection::List)), SLOT(subJobCollectionReceived(Akonadi::Collection::List)));
397  subJob->setFetchScope(fetchScope());
398  }
399  }
400  // No result yet.
401  } else if (d->mType == NonOverlappingRoots) {
402  d->mPrefetchList += list->collections();
403  Job::slotResult(job);
404  if (!job->error() && !hasSubjobs()) {
405  const Collection::List result = filterDescendants(d->mPrefetchList);
406  d->mPendingCollections += result;
407  d->mCollections = result;
408  d->delayedEmitResult();
409  }
410  } else {
411  d->mCollections += list->collections();
412  Job::slotResult(job);
413  if (!job->error() && !hasSubjobs()) {
414  d->delayedEmitResult();
415  }
416  }
417 }
418 
419 void CollectionFetchJob::includeUnsubscribed(bool include)
420 {
421  Q_D(CollectionFetchJob);
422 
423  d->mScope.setIncludeUnsubscribed(include);
424 }
425 
426 void CollectionFetchJob::includeStatistics(bool include)
427 {
428  Q_D(CollectionFetchJob);
429 
430  d->mScope.setIncludeStatistics(include);
431 }
432 
433 void CollectionFetchJob::setFetchScope(const CollectionFetchScope &scope)
434 {
435  Q_D(CollectionFetchJob);
436  d->mScope = scope;
437 }
438 
439 CollectionFetchScope &CollectionFetchJob::fetchScope()
440 {
441  Q_D(CollectionFetchJob);
442  return d->mScope;
443 }
444 
445 #include "moc_collectionfetchjob.cpp"
Akonadi::CollectionFetchScope::setAncestorRetrieval
void setAncestorRetrieval(AncestorRetrieval ancestorDepth)
Sets how many levels of ancestor collections should be included in the retrieval. ...
Definition: collectionfetchscope.cpp:138
Akonadi::CollectionFetchJob::collections
Collection::List collections() const
Returns the list of fetched collection.
Definition: collectionfetchjob.cpp:169
Akonadi::CollectionFetchJob::includeUnsubscribed
AKONADI_DEPRECATED void includeUnsubscribed(bool include=true)
Include also unsubscribed collections.
Definition: collectionfetchjob.cpp:419
Akonadi::CollectionFetchJob::CollectionFetchJob
CollectionFetchJob(const Collection &collection, Type type=FirstLevel, QObject *parent=0)
Creates a new collection fetch job.
Definition: collectionfetchjob.cpp:108
QByteArray
Akonadi::CollectionFetchScope
Specifies which parts of a collection should be fetched from the Akonadi storage. ...
Definition: collectionfetchscope.h:68
Akonadi::CollectionFetchJob::Type
Type
Describes the type of fetch depth.
Definition: collectionfetchjob.h:61
Akonadi::Job::Unknown
Unknown error.
Definition: job.h:108
Akonadi::CollectionFetchJob::FirstLevel
Only list direct sub-collections of the base collection.
Definition: collectionfetchjob.h:63
Akonadi::CollectionFetchJob::fetchScope
CollectionFetchScope & fetchScope()
Returns the collection fetch scope.
Definition: collectionfetchjob.cpp:439
Akonadi::Collection
Represents a collection of PIM items.
Definition: collection.h:75
Akonadi::CollectionFetchJob::NonOverlappingRoots
List the roots of a list of fetched collections.
Definition: collectionfetchjob.h:65
QList::at
const T & at(int i) const
Akonadi::CollectionFetchJob
Job that fetches collections from the Akonadi storage.
Definition: collectionfetchjob.h:53
Akonadi::Entity::Id
qint64 Id
Describes the unique id type.
Definition: entity.h:65
Akonadi::Job
Base class for all actions in the Akonadi storage.
Definition: job.h:86
Akonadi::CollectionFetchJob::collectionsReceived
void collectionsReceived(const Akonadi::Collection::List &collections)
This signal is emitted whenever the job has received collections.
QSet::insert
const_iterator insert(const T &value)
Akonadi::CollectionFetchJob::doHandleResponse
virtual void doHandleResponse(const QByteArray &tag, const QByteArray &data)
This method should be reimplemented in the concrete jobs in case you want to handle incoming data...
Definition: collectionfetchjob.cpp:310
Akonadi::CollectionFetchScope::Display
Only retrieve collections for display, taking the local preference and enabled into account...
Definition: collectionfetchscope.h:134
Akonadi::CollectionFetchScope::Parent
Only retrieve the immediate parent collection.
Definition: collectionfetchscope.h:76
Akonadi::ProtocolHelper::parseCollection
static int parseCollection(const QByteArray &data, Collection &collection, int start=0)
Parse a collection description.
Definition: protocolhelper.cpp:145
QList::size
int size() const
Akonadi::CollectionFetchJob::Base
Only fetch the base collection.
Definition: collectionfetchjob.h:62
Akonadi::CollectionFetchScope::Index
Only retrieve collections for indxing, taking the local preference and enabled into account...
Definition: collectionfetchscope.h:136
QList::append
void append(const T &value)
QString::fromUtf8
QString fromUtf8(const char *str, int size)
QTimer
Akonadi::Entity::parentCollection
Collection parentCollection() const
Returns the parent collection of this object.
Definition: entity.cpp:185
QObject
QList::isEmpty
bool isEmpty() const
QByteArray::number
QByteArray number(int n, int base)
Akonadi::CollectionFetchScope::All
Retrieve all ancestors, up to Collection::root()
Definition: collectionfetchscope.h:77
Akonadi::ProtocolHelper::hierarchicalRidToByteArray
static QByteArray hierarchicalRidToByteArray(const Collection &col)
Converts the given collection's hierarchical RID into a protocol representation.
Definition: protocolhelper.cpp:395
Akonadi::Collection::root
static Collection root()
Returns the root collection.
Definition: collection.cpp:192
QSet
Definition: itemfetchscope.h:29
QList::first
T & first()
QString
QList
Akonadi::Entity::id
Id id() const
Returns the unique identifier of the entity.
Definition: entity.cpp:72
QList::iterator
Akonadi::CollectionFetchJob::setFetchScope
void setFetchScope(const CollectionFetchScope &fetchScope)
Sets the collection fetch scope.
Definition: collectionfetchjob.cpp:433
Akonadi::CollectionFetchJob::doStart
virtual void doStart()
This method must be reimplemented in the concrete jobs.
Definition: collectionfetchjob.cpp:176
QList::end
iterator end()
Akonadi::CollectionFetchScope::NoFilter
No filtering, retrieve all collections.
Definition: collectionfetchscope.h:133
QSet::contains
bool contains(const T &value) const
QVector
Definition: kcolumnfilterproxymodel_p.h:27
QList::insert
void insert(int i, const T &value)
Akonadi::CollectionFetchJob::includeStatistics
AKONADI_DEPRECATED void includeStatistics(bool include=true)
Include also statistics about the collections.
Definition: collectionfetchjob.cpp:426
Akonadi::JobPrivate
Definition: job_p.h:31
Akonadi::CollectionFetchJob::~CollectionFetchJob
virtual ~CollectionFetchJob()
Destroys the collection fetch job.
Definition: collectionfetchjob.cpp:165
QString::fromLatin1
QString fromLatin1(const char *str, int size)
Akonadi::CollectionFetchScope::Enabled
Only retrieve enabled collections, ignoring the local preference. This is the same as setIncludeUnsub...
Definition: collectionfetchscope.h:137
Akonadi::Entity::isValid
bool isValid() const
Returns whether the entity is valid.
Definition: entity.cpp:97
Akonadi::CollectionFetchScope::None
No ancestor retrieval at all (the default)
Definition: collectionfetchscope.h:75
QString::arg
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
Akonadi::CollectionFetchJob::Recursive
List all sub-collections.
Definition: collectionfetchjob.h:64
QList::begin
iterator begin()
Akonadi::CollectionFetchScope::Sync
Only retrieve collections for synchronization, taking the local preference and enabled into account...
Definition: collectionfetchscope.h:135
Akonadi::CollectionFetchJob::setResource
AKONADI_DEPRECATED void setResource(const QString &resource)
Sets a resource identifier to limit collection listing to one resource.
Definition: collectionfetchjob.cpp:336
QString::toUtf8
QByteArray toUtf8() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:38:02 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

akonadi

Skip menu "akonadi"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Modules
  • Related Pages

kdepimlibs API Reference

Skip menu "kdepimlibs API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal