Fixes: NB#287273- [TASK] Implement nickname preference setting for qtcontacts-tracker
[qtcontacts-tracker:qtcontacts-tracker.git] / src / engine / engine.cpp
1 /*********************************************************************************
2  ** This file is part of QtContacts tracker storage plugin
3  **
4  ** Copyright (c) 2009-2011 Nokia Corporation and/or its subsidiary(-ies).
5  **
6  ** Contact:  Nokia Corporation (info@qt.nokia.com)
7  **
8  ** GNU Lesser General Public License Usage
9  ** This file may be used under the terms of the GNU Lesser General Public License
10  ** version 2.1 as published by the Free Software Foundation and appearing in the
11  ** file LICENSE.LGPL included in the packaging of this file.  Please review the
12  ** following information to ensure the GNU Lesser General Public License version
13  ** 2.1 requirements will be met:
14  ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
15  **
16  ** In addition, as a special exception, Nokia gives you certain additional rights.
17  ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included
18  ** in the file LGPL_EXCEPTION.txt in this package.
19  **
20  ** Other Usage
21  ** Alternatively, this file may be used in accordance with the terms and
22  ** conditions contained in a signed written agreement between you and Nokia.
23  *********************************************************************************/
24
25 #include "engine.h"
26 #include "engine_p.h"
27
28 #include "contactfetchrequest.h"
29 #include "contactfetchbyidrequest.h"
30 #include "contactidfetchrequest.h"
31 #include "contactcopyandremoverequest.h"
32 #include "contactunmergerequest.h"
33 #include "displaylabelgenerator.h"
34 #include "relationshipfetchrequest.h"
35 #include "relationshipremoverequest.h"
36 #include "relationshipsaverequest.h"
37 #include "detaildefinitionfetchrequest.h"
38 #include "tasks.h"
39
40 #include <dao/contactdetail.h>
41 #include <dao/contactdetailschema.h>
42 #include <dao/scalarquerybuilder.h>
43 #include <dao/scalarquerybuilder.h>
44 #include <dao/support.h>
45
46 #include <lib/constants.h>
47 #include <lib/contactmergerequest.h>
48 #include <lib/customdetails.h>
49 #include <lib/garbagecollector.h>
50 #include <lib/presenceutils.h>
51 #include <lib/queue.h>
52 #include <lib/settings.h>
53 #include <lib/threadutils.h>
54 #include <lib/sparqlresolver.h>
55 #include <lib/threadlocaldata.h>
56 #include <lib/trackerchangelistener.h>
57 #include <lib/unmergeimcontactsrequest.h>
58
59 #include <qcontactdetails.h>
60
61 #include <ontologies/nco.h>
62 #include <QElapsedTimer>
63
64 #ifdef ENABLE_CREDENTIALS
65 #include <CredentialsManager>
66 #endif // ENABLE_CREDENTIALS
67
68 ////////////////////////////////////////////////////////////////////////////////////////////////////
69
70 CUBI_USE_NAMESPACE_RESOURCES
71 QTM_USE_NAMESPACE
72
73 ////////////////////////////////////////////////////////////////////////////////////////////////////
74
75 #define REPORT_UNSUPPORTED_FUNCTION(error) do {\
76     qctWarn(QString::fromLatin1("Method not implemented yet: %1"). \
77             arg(QLatin1String(Q_FUNC_INFO))); \
78     qctPropagate(QContactManager::NotSupportedError, error); \
79 } while(0)
80
81 ////////////////////////////////////////////////////////////////////////////////////////////////////
82
83 static QContactManager::Error *const noErrorResult = 0;
84 const QString QContactTrackerEngine::DefaultSyncTarget = QContactSyncTarget__SyncTargetAddressBook;
85 const QStringList QContactTrackerEngine::DefaultWeakSyncTargets = QStringList() << QContactSyncTarget__SyncTargetTelepathy;
86
87 ////////////////////////////////////////////////////////////////////////////////////////////////////
88
89 // added here as list was not working because of
90 // [warn ] QObject::connect: Cannot queue arguments of type 'QContactAbstractRequest::State'
91 // (Make sure 'QContactAbstractRequest::State' is registered using qRegisterMetaType().)
92 Q_DECLARE_METATYPE(QContactAbstractRequest::State)
93
94 ////////////////////////////////////////////////////////////////////////////////////////////////////
95
96 static QMap<QString, QString>
97 readEnvironment()
98 {
99     const QStringList tokenList(QProcessEnvironment::systemEnvironment().
100                                 value(QLatin1String("QT_CONTACTS_TRACKER")).
101                                 remove(QLatin1Char(' ')).
102                                 split(QLatin1String(";")));
103
104     QMap<QString, QString> parameters;
105
106     foreach(const QString &token, tokenList) {
107         const int eq = token.indexOf(QLatin1Char('='));
108         const QString key((eq >= 0 ? token.left(eq) : token).trimmed());
109         const QString value((eq >= 0 ? token.mid(eq + 1).trimmed() : QString()));
110
111         if (not key.isEmpty()) {
112             parameters.insert(key, value);
113         }
114     }
115
116     return parameters;
117 }
118
119 static void
120 parseParameter(int &result, const QString &key, const QString &text)
121 {
122     bool ok = false;
123     const int value = text.toInt(&ok);
124
125     if (ok) {
126         result = value;
127     } else {
128         qctWarn(QString::fromLatin1("Invalid value for %1 argument: %2").arg(key, text));
129     }
130 }
131
132 /*!
133  * \class QContactTrackerEngineParameters
134  * \brief This class describes the various parameters you can pass at engine creation time
135  * <table>
136  * <tr>
137  *  <td>timeout</td>
138  *  <td>Timeout (in ms) after which sync requests should be canceled. 0 for no timeout.<br/>
139  *      Default value 0.</td>
140  * </tr>
141  * <tr>
142  *  <td>tracker-timeout</td>
143  *  <td>Unused</td>
144  * </tr>
145  * <tr>
146  *  <td>coalescing-delay</td>
147  *  <td>Delay to wait (in ms) before emitting one of the contactsAdded/Changed/Removed (or
148  *      relationshipsAdded/Removed) signals, to get a chance to coalesce them together.<br/>
149  *      Default value: 0.</td>
150  * </tr>
151  * <tr>
152  *  <td>writeback</td>
153  *  <td>Controls which details are written to Tracker on saving.<br/>
154  *      Valid values: "", a comma separated list of detail names, or "all"<br/>
155  *      Default value: all details except QContactPresence</td>
156  * </tr>
157  * <tr>
158  *  <td>debug</td>
159  *  <td>Controls what debug messages are printed on the console<br/>
160  *      Valid values (separated by ","):
161  *      <ul>
162  *       <li>queries: print all queries (SELECT and UPDATE)</li>
163  *       <li>selects: print all SELECT queries</li>
164  *       <li>updates: print all UPDATE queries</li>
165  *       <li>models: print the computed result set after a SELECT</li>
166  *       <li>timing: print timing information about requests</li>
167  *       <li>signals: print debug information about change signals</li>
168  *       <li>no-sec: disable security credentials checks</li>
169  *       <li>no-nagging: don't print warning messages when using waitForFinished()</li>
170  *      </ul>
171  *      Default value: ""
172  *  </td>
173  * </tr>
174  * <tr>
175  *  <td>contact-types</td>
176  *  <td>Filters the type of contacts fetched by the engine<br/>
177  *      Valid values (separated by ","): any value of QContactType::type()<br/>
178  *      Default values: "" (fetch all types)</td>
179  * </tr>
180  * <tr>
181  *  <td>concurrency</td>
182  *  <td>Unused</td>
183  * </tr>
184  * <tr>
185  *  <td>batch-size</td>
186  *  <td>Unused</td>
187  * </tr>
188  * <tr>
189  *  <td>gc-limit</td>
190  *  <td>Numbers of contacts saved/removed after which garbage collection should be triggered<br/>
191  *      Default value: 100</td>
192  * </tr>
193  * <tr>
194  *  <td>guid-algorithm</td>
195  *  <td>Name of the GUID algorithm to use<br/>
196  *      Valid values: "default", "cellular" (depends on CelullarQt)<br/>
197  *      Default value: "default"</td>
198  * </tr>
199  * <tr>
200  *  <td>default-sync-target</td>
201  *  <td>Default value for the QContactSyncTarget set on saved contacts<br/>
202  *      Default value: "addressbook"</td>
203  * </tr>
204  * <tr>
205  *  <td>weak-sync-targets</td>
206  *  <td>List of sync targets to replace with default-sync-target when saving a contact<br/>
207  *      Valid values: a comma separated list of sync targets, or "all"<br/>
208  *      Default value: "telepathy"</td>
209  * </tr>
210  * <tr>
211  *  <td>native-numbers</td>
212  *  <td>Whether to keep phone numbers in their native script, or convert them to Latin script<br/>
213  *      Valid values: true to conserve the original script, false to convert to Latin<br/>
214  *      Default value: false</td>
215  * </tr>
216  * <tr>
217  *  <td>omit-presence-changes</td>
218  *  <td>Whether the contactsChanged signals should be omitted if only the QContactPresence
219  *      detail was changed.
220  *      Valid values: true to omit the signals, false to emit all signals<br/>
221  *      Default value: false</td>
222  * </tr>
223  * <tr>
224  *  <td>compliance</td>
225  *  <td>Selects API compliance level of the engine. Setting "restrictive" lets the engine follow
226 *       QtContacts API spec by the word. By default the engine is more forgiving.<br/>
227  *      Default value: not set</td>
228  * </tr>
229  * </table>
230  */
231 QContactTrackerEngineParameters::QContactTrackerEngineParameters(const QMap<QString, QString> &parameters,
232                                                                  const QString& engineName, int engineVersion)
233     : m_engineName(engineName)
234     , m_engineVersion(engineVersion)
235     , m_requestTimeout(QContactTrackerEngine::DefaultRequestTimeout)
236     , m_trackerTimeout(QContactTrackerEngine::DefaultTrackerTimeout)
237     , m_coalescingDelay(QContactTrackerEngine::DefaultCoalescingDelay)
238     , m_concurrencyLevel(0) // Set in ctor
239     , m_batchSize(QContactTrackerEngine::DefaultBatchSize)
240     , m_gcLimit(QContactTrackerEngine::DefaultGCLimit)
241     , m_syncTarget(QContactTrackerEngine::DefaultSyncTarget)
242     , m_weakSyncTargets(QContactTrackerEngine::DefaultWeakSyncTargets)
243     , m_guidAlgorithm(0)
244     , m_parameters(parameters)
245     , m_debugFlags(0)
246     , m_omitPresenceChanges(false)
247     , m_mangleAllSyncTargets(false)
248     , m_isRestrictive(false)
249 {
250     const QctSettings *const settings = QctThreadLocalData::instance()->settings();
251
252     m_concurrencyLevel = settings->concurrencyLevel();
253
254     // Names of parameters exposed in the manager URI. See managerParameters() for details.
255     static const QStringList managerParameterNames = QStringList();
256
257     // Permit overriding manager params from environment.
258     static const QMap<QString, QString> environmentParameters = readEnvironment();
259     m_parameters.unite(environmentParameters);
260
261     // Parse engine parameters.
262     QString guidAlgorithmName = settings->guidAlgorithmName();
263     QStringList contactTypes;
264
265     for(QMap<QString, QString>::ConstIterator i = m_parameters.constBegin(); i != m_parameters.constEnd(); ++i) {
266         if (managerParameterNames.contains(i.key())) {
267             m_managerParameters.insert(i.key(), i.value());
268         }
269
270         if (QLatin1String("timeout") == i.key()) {
271             parseParameter(m_requestTimeout, i.key(), i.value());
272             continue;
273         }
274
275         if (QLatin1String("tracker-timeout") == i.key()) {
276             parseParameter(m_trackerTimeout, i.key(), i.value());
277             continue;
278         }
279
280         if (QLatin1String("coalescing-delay") == i.key()) {
281             parseParameter(m_coalescingDelay, i.key(), i.value());
282             continue;
283         }
284
285         if (QLatin1String("writeback") == i.key()) {
286             const QSet<QString> values = i.value().split(QLatin1Char(',')).toSet();
287             const bool all(values.contains(QLatin1String("all")));
288
289             if (all || values.contains(QLatin1String("presence"))) {
290                 foreach(QTrackerContactDetailSchema schema, m_detailSchemas) {
291                     schema.setWriteBackPresence(true);
292                 }
293             }
294
295             continue;
296         }
297
298         if (QLatin1String("debug") == i.key()) {
299             const QSet<QString> values = i.value().split(QLatin1Char(',')).toSet();
300             const bool all(values.contains(QLatin1String("all")));
301
302             if (all || values.contains(QLatin1String("queries"))) {
303                 m_debugFlags |= QContactTrackerEngine::ShowSelects;
304                 m_debugFlags |= QContactTrackerEngine::ShowUpdates;
305             }
306             if (all || values.contains(QLatin1String("selects"))) {
307                 m_debugFlags |= QContactTrackerEngine::ShowSelects;
308             }
309             if (all || values.contains(QLatin1String("updates"))) {
310                 m_debugFlags |= QContactTrackerEngine::ShowUpdates;
311             }
312             if (all || values.contains(QLatin1String("models"))) {
313                 m_debugFlags |= QContactTrackerEngine::ShowModels;
314             }
315             if (all || values.contains(QLatin1String("notes"))) {
316                 m_debugFlags |= QContactTrackerEngine::ShowNotes;
317             }
318             if (all || values.contains(QLatin1String("timing"))) {
319                 m_debugFlags |= QContactTrackerEngine::ShowTiming;
320             }
321             if (all || values.contains(QLatin1String("signals"))) {
322                 m_debugFlags |= QContactTrackerEngine::ShowSignals;
323             }
324             if (all || values.contains(QLatin1String("no-sec"))) {
325                 m_debugFlags |= QContactTrackerEngine::SkipSecurityChecks;
326             }
327             if (all || values.contains(QLatin1String("no-nagging"))) {
328                 m_debugFlags |= QContactTrackerEngine::SkipNagging;
329             }
330
331             continue;
332         }
333
334         if (QLatin1String("contact-types") == i.key()) {
335             contactTypes = i.value().split(QLatin1Char(','));
336             continue;
337         }
338
339         if (QLatin1String("concurrency") == i.key()) {
340             if ((m_concurrencyLevel = i.value().toInt()) < 1) {
341                 m_concurrencyLevel = settings->concurrencyLevel();
342             }
343
344             continue;
345         }
346
347         if (QLatin1String("batch-size") == i.key()) {
348             if ((m_batchSize = i.value().toInt()) < 1) {
349                 m_batchSize = QContactTrackerEngine::DefaultBatchSize;
350             }
351
352             continue;
353         }
354
355         if (QLatin1String("gc-limit") == i.key()) {
356             if ((m_gcLimit = i.value().toInt()) < 1) {
357                 m_gcLimit = QContactTrackerEngine::DefaultGCLimit;
358             }
359
360             continue;
361         }
362
363         if (QLatin1String("guid-algorithm") == i.key()) {
364             guidAlgorithmName = i.value();
365             continue;
366         }
367
368         if (QLatin1String("default-sync-target") == i.key()) {
369             m_syncTarget = i.value();
370             continue;
371         }
372
373         if (QLatin1String("weak-sync-targets") == i.key()) {
374             m_weakSyncTargets = i.value().split(QLatin1String(","), QString::SkipEmptyParts);
375
376             if (m_weakSyncTargets.contains(QLatin1String("all"))) {
377                 m_mangleAllSyncTargets = true;
378                 m_weakSyncTargets.clear();
379             }
380
381             continue;
382         }
383
384         if (QLatin1String("compliance") == i.key()) {
385             m_isRestrictive = (i.value() == QLatin1String("restrictive"));
386             continue;
387         }
388
389         if (QLatin1String("native-numbers") == i.key()) {
390             const bool value = QVariant(i.value()).toBool();
391             foreach(QTrackerContactDetailSchema schema, m_detailSchemas) {
392                 schema.setConvertNumbersToLatin(not value);
393             }
394             continue;
395         }
396
397         if (QLatin1String("omit-presence-changes") == i.key()) {
398             m_omitPresenceChanges = true;
399             continue;
400         }
401
402         if (QLatin1String("com.nokia.qt.mobility.contacts.implementation.version") == i.key()) {
403             continue;
404         }
405
406         qctWarn(QString::fromLatin1("Unknown parameter: %1").arg(i.key()));
407     }
408
409     // Setup detail schemas.
410     if (contactTypes.isEmpty() || contactTypes.contains(QContactType::TypeContact, Qt::CaseInsensitive)) {
411         QTrackerPersonContactDetailSchema personContactSchema;
412         m_detailSchemas.insert(QContactType::TypeContact, personContactSchema);
413     }
414
415     if (contactTypes.isEmpty() || contactTypes.contains(QContactType::TypeGroup, Qt::CaseInsensitive)) {
416         QTrackerContactGroupDetailSchema contactGroupSchema;
417         m_detailSchemas.insert(QContactType::TypeGroup, contactGroupSchema);
418     }
419
420     // Setup GUID algorithm.
421     m_guidAlgorithm = QctGuidAlgorithmFactory::algorithm(guidAlgorithmName);
422
423     if (0 == m_guidAlgorithm) {
424         if (not guidAlgorithmName.isEmpty()) {
425             qctWarn(QString::fromLatin1("Unknown GUID algorithm: %1. Using default algorithm.").
426                     arg(guidAlgorithmName));
427         }
428
429         m_guidAlgorithm = QctGuidAlgorithmFactory::algorithm(QctGuidAlgorithm::Default);
430     }
431 }
432
433 ////////////////////////////////////////////////////////////////////////////////////////////////////
434
435 QContactTrackerEngineData::QContactTrackerEngineData(const QMap<QString, QString> &parameters,
436                                                      const QString &engineName, int engineVersion)
437     : QSharedData()
438     , m_parameters(parameters, engineName, engineVersion)
439     , m_selfContactId(0)
440     , m_changeListener(0) // create on demand
441     , m_requestLifeGuard(QMutex::Recursive)
442     , m_queue(new QctQueue)
443     , m_satisfiedDependencies(QTrackerAbstractRequest::NoDependencies)
444     , m_mandatoryTokensFound(false)
445 {
446     // disable security token checks if requested
447     if (m_parameters.m_debugFlags & QContactTrackerEngine::SkipSecurityChecks) {
448         m_mandatoryTokensFound = true;
449     }
450 }
451
452 QContactTrackerEngineData::QContactTrackerEngineData(const QContactTrackerEngineData& other)
453     : QSharedData(other)
454     , m_parameters(other.m_parameters)
455     , m_supportedDataTypes(other.m_supportedDataTypes)
456     , m_selfContactId(other.m_selfContactId)
457     , m_changeListener(0) // must create our own when needed
458     , m_requestLifeGuard(QMutex::Recursive)
459     , m_queue(new QctQueue) // the queue is per engine
460     , m_satisfiedDependencies(other.m_satisfiedDependencies)
461     , m_gcQueryId(other.m_gcQueryId)
462     , m_mandatoryTokensFound(other.m_mandatoryTokensFound)
463 {
464 }
465
466 QContactTrackerEngineData::~QContactTrackerEngineData()
467 {
468     // Delete the queue early to avoid that its tasks see a half destructed objects
469     // like this engine or SPARQL resolvers.
470     delete m_queue;
471 }
472
473 ///////////////////////////////////////////////////////////////////////////////
474 // Synchronous API helpers
475 ///////////////////////////////////////////////////////////////////////////////
476
477 RequestEventLoop::RequestEventLoop(QContactAbstractRequest *request, int timeout)
478     : m_finished(request->isFinished() || request->isCanceled())
479 {
480     // direct connection needed to ensure m_finished is toggled immediately
481     connect(request, SIGNAL(stateChanged(QContactAbstractRequest::State)),
482             this, SLOT(stateChanged(QContactAbstractRequest::State)),
483             Qt::DirectConnection);
484     connect(request, SIGNAL(destroyed()),
485             this, SLOT(requestDone()),
486             Qt::DirectConnection);
487
488     if (timeout > 0) {
489         QTimer::singleShot(timeout, this, SLOT(quit()));
490     }
491 }
492
493 void
494 RequestEventLoop::stateChanged(QContactAbstractRequest::State newState)
495 {
496     switch (newState) {
497     case QContactAbstractRequest::FinishedState:
498     case QContactAbstractRequest::CanceledState:
499         requestDone();
500         break;
501     default:
502         break;
503     }
504 }
505
506 void
507 RequestEventLoop::requestDone()
508 {
509     m_finished = true;
510     // Slot was attached with direct connection, therefore quit() must be invoked via auto
511     // connection to ensure resultsAvailable() and stateChanged() reach the request thread's
512     // event loop before this request loop is terminated and waitForFinished() leaves.
513     metaObject()->invokeMethod(this, "quit", Qt::AutoConnection);
514 }
515
516 ////////////////////////////////////////////////////////////////////////////////////////////////////
517
518 QctTaskWaiter::QctTaskWaiter(QctTask *task, QObject *parent)
519     : QObject(parent)
520     , m_task(task)
521     , m_finished(false)
522 {
523     // Use direct connection to keep the task thread from
524     // deleting the task while we interact with it.
525     connect(task, SIGNAL(finished(QctTask*)),
526             this, SLOT(onTaskFinished()), Qt::DirectConnection);
527     connect(task, SIGNAL(destroyed(QObject*)),
528             this, SLOT(onTaskDestroyed()), Qt::DirectConnection);
529 }
530
531 bool
532 QctTaskWaiter::wait(int timeout)
533 {
534     QCT_SYNCHRONIZED(&m_mutex);
535
536     if (0 == m_task || m_finished) {
537         return m_finished; // return proper result in case of onTaskDestroyed()
538     }
539
540     // while waiting the mutex will get released
541     return m_waitCondition.wait(&m_mutex, (timeout != 0 ? timeout : ULONG_MAX));
542 }
543
544 void
545 QctTaskWaiter::onTaskFinished()
546 {
547     {
548         QCT_SYNCHRONIZED(&m_mutex);
549
550         if (0 != m_task) {
551             // We disconnect make sure that we don't get the signal twice with destroyed
552             m_task->disconnect(this);
553         }
554
555         m_finished = true;
556     }
557
558     m_waitCondition.wakeAll();
559 }
560
561 void
562 QctTaskWaiter::onTaskDestroyed()
563 {
564     {
565         QCT_SYNCHRONIZED(&m_mutex);
566         m_task = 0;
567     }
568
569     m_waitCondition.wakeAll();
570 }
571
572 ////////////////////////////////////////////////////////////////////////////////////////////////////
573 // Instances which manage contact engine instances
574 ////////////////////////////////////////////////////////////////////////////////////////////////////
575
576 QContactTrackerEngine::QContactTrackerEngine(const QMap<QString, QString> &parameters,
577                                              const QString &managerName, int interfaceVersion,
578                                              QObject *parent)
579     : d(new QContactTrackerEngineData(parameters, managerName, interfaceVersion))
580 {
581     // workaround for Qt type system madness
582     qRegisterMetaType<QContactAbstractRequest::State>();
583
584     // workaround for QTMOBILITY-1526
585     if (0 != parent) {
586         setParent(parent);
587     }
588
589     connectSignals();
590     registerGcQuery();
591 }
592
593 QContactTrackerEngine::QContactTrackerEngine(const QContactTrackerEngine& other)
594     : d(other.d)
595 {
596     d.detach();
597     connectSignals();
598 }
599
600 QContactTrackerEngine::~QContactTrackerEngine()
601 {
602     disconnectSignals();
603 }
604
605 QContactTrackerEngine&
606 QContactTrackerEngine::operator=(const QContactTrackerEngine& other)
607 {
608     disconnectSignals();
609
610     d = other.d;
611     d.detach();
612
613     connectSignals();
614
615     return *this;
616 }
617
618 ////////////////////////////////////////////////////////////////////////////////////////////////////
619 // Methods which describe the contact engine's capabilities
620 ////////////////////////////////////////////////////////////////////////////////////////////////////
621
622 QMap<QString, QString>
623 QContactTrackerEngine::managerParameters() const
624 {
625     // Manager parameters appear within the contact manager's URI. The set of parameters exposed
626     // in the manager URI must be minimal since the manager URI becomes part of each contact id.
627     // Exposing too many parameters would cause the same contact having a different contact id
628     // when read from different contact manager instances with slightly different settings like
629     // different timeouts or change listener modes.
630     return d->m_parameters.m_managerParameters;
631 }
632
633 QString
634 QContactTrackerEngine::managerName() const
635 {
636     return d->m_parameters.m_engineName;
637 }
638
639 int
640 QContactTrackerEngine::managerVersion() const
641 {
642     return d->m_parameters.m_engineVersion;
643 }
644
645 bool
646 QContactTrackerEngine::hasFeature(QContactManager::ManagerFeature feature,
647                                   const QString& contactType) const
648 {
649     if (not supportedContactTypes().contains(contactType)) {
650         return false;
651     }
652
653     switch (feature) {
654     case QContactManager::Anonymous:
655     case QContactManager::ChangeLogs:
656         return true;
657
658     case QContactManager::Groups:
659     case QContactManager::Relationships:
660         return supportedContactTypes().contains(QContactType::TypeGroup);
661
662     default:
663         return false;
664     }
665 }
666
667 bool
668 QContactTrackerEngine::isFilterSupported(const QContactFilter& filter) const
669 {
670     return QTrackerScalarContactQueryBuilder::isFilterSupported(filter);
671 }
672
673 QList<QVariant::Type>
674 QContactTrackerEngine::supportedDataTypes() const
675 {
676     if (d->m_supportedDataTypes.isEmpty()) {
677         QSet<QVariant::Type> typeSet;
678
679         foreach(const QTrackerContactDetailSchema& schema, schemas()) {
680             typeSet += schema.supportedDataTypes();
681         }
682
683         d->m_supportedDataTypes = typeSet.toList();
684     }
685
686     return d->m_supportedDataTypes;
687 }
688
689 QStringList
690 QContactTrackerEngine::supportedContactTypes() const
691 {
692     return schemas().keys();
693 }
694
695 QContactDetailDefinitionMap
696 QContactTrackerEngine::detailDefinitions(const QString& contactType,
697                                          QContactManager::Error* error) const
698 {
699     const QTrackerContactDetailSchemaMap::ConstIterator schema = schemas().find(contactType);
700
701     if (schema == schemas().constEnd()) {
702         qctPropagate(QContactManager::InvalidContactTypeError, error);
703         return QContactDetailDefinitionMap();
704     }
705
706     qctPropagate(QContactManager::NoError, error);
707     return schema->detailDefinitions();
708 }
709
710 QContactDetailDefinition
711 QContactTrackerEngine::detailDefinition(const QString& definitionName,
712                                         const QString& contactType,
713                                         QContactManager::Error* error) const
714 {
715     const QTrackerContactDetailSchemaMap::ConstIterator schema = schemas().find(contactType);
716
717     if (schema == schemas().constEnd()) {
718         qctPropagate(QContactManager::InvalidContactTypeError, error);
719         return QContactDetailDefinition();
720     }
721
722     const QContactDetailDefinitionMap &definitions(schema->detailDefinitions());
723     const QContactDetailDefinitionMap::ConstIterator detail(definitions.find(definitionName));
724
725     if (definitions.constEnd() == detail) {
726         qctPropagate(QContactManager::DoesNotExistError, error);
727         return QContactDetailDefinition();
728     }
729
730     qctPropagate(QContactManager::NoError, error);
731     return detail.value();
732 }
733
734 const QTrackerContactDetailSchemaMap &
735 QContactTrackerEngine::schemas() const
736 {
737     return d->m_parameters.m_detailSchemas;
738 }
739
740 const QTrackerContactDetailSchema &
741 QContactTrackerEngine::schema(const QString& contactType) const
742 {
743     QTrackerContactDetailSchemaMap::ConstIterator schema = schemas().find(contactType);
744
745     if (schema == schemas().constEnd()) {
746         qctFail(QString::fromLatin1("Unexpected contact type %1. Aborting.").arg(contactType));
747         schema = schemas().constBegin();
748     }
749
750     return schema.value();
751 }
752
753 bool
754 QContactTrackerEngine::setSelfContactId(const QContactLocalId&,
755                                         QContactManager::Error* error)
756 {
757     REPORT_UNSUPPORTED_FUNCTION(error);
758     return false;
759 }
760
761 QContactLocalId
762 QContactTrackerEngine::selfContactId(QContactManager::Error* error) const
763 {
764     if (0 == d->m_selfContactId) {
765         QctTrackerIdResolver resolver(QStringList() << nco::default_contact_me::iri());
766
767         if (resolver.lookupAndWait()) {
768             d->m_selfContactId = resolver.trackerIds().first();
769         }
770     }
771
772     qctPropagate(d->m_selfContactId ? QContactManager::NoError
773                                     : QContactManager::DoesNotExistError, error);
774
775     return d->m_selfContactId;
776 }
777
778 const QContactTrackerEngine::DebugFlags &
779 QContactTrackerEngine::debugFlags() const
780 {
781     return d->m_parameters.m_debugFlags;
782 }
783
784 bool
785 QContactTrackerEngine::hasDebugFlag(DebugFlag flag) const
786 {
787     return debugFlags().testFlag(flag);
788 }
789
790 int
791 QContactTrackerEngine::concurrencyLevel() const
792 {
793     return d->m_parameters.m_concurrencyLevel;
794 }
795
796 int
797 QContactTrackerEngine::batchSize() const
798 {
799     return d->m_parameters.m_batchSize;
800 }
801
802 int
803 QContactTrackerEngine::gcLimit() const
804 {
805     return d->m_parameters.m_gcLimit;
806 }
807
808 int
809 QContactTrackerEngine::requestTimeout() const
810 {
811     return d->m_parameters.m_requestTimeout;
812 }
813
814 int
815 QContactTrackerEngine::trackerTimeout() const
816 {
817     return d->m_parameters.m_trackerTimeout;
818 }
819
820 int
821 QContactTrackerEngine::coalescingDelay() const
822 {
823     return d->m_parameters.m_coalescingDelay;
824 }
825
826 QctGuidAlgorithm &
827 QContactTrackerEngine::guidAlgorithm() const
828 {
829     return *d->m_parameters.m_guidAlgorithm;
830 }
831
832 const QString &
833 QContactTrackerEngine::syncTarget() const
834 {
835     return d->m_parameters.m_syncTarget;
836 }
837
838 const QStringList &
839 QContactTrackerEngine::weakSyncTargets() const
840 {
841     return d->m_parameters.m_weakSyncTargets;
842 }
843
844 bool
845 QContactTrackerEngine::mangleAllSyncTargets() const
846 {
847     return d->m_parameters.m_mangleAllSyncTargets;
848 }
849
850 bool
851 QContactTrackerEngine::isRestrictive() const
852 {
853     return d->m_parameters.m_isRestrictive;
854 }
855
856 ////////////////////////////////////////////////////////////////////////////////////////////////////
857 // Synchronous data access methods
858 ////////////////////////////////////////////////////////////////////////////////////////////////////
859
860 QList<QContactLocalId>
861 QContactTrackerEngine::contactIds(const QList<QContactSortOrder>& sortOrders,
862                                   QContactManager::Error* error) const
863 {
864     return contactIds(QContactFilter(), sortOrders, error);
865 }
866
867 QList<QContactLocalId>
868 QContactTrackerEngine::contactIds(const QContactFilter& filter,
869                                   const QList<QContactSortOrder>& sortOrders,
870                                   QContactManager::Error* error) const
871 {
872     QContactLocalIdFetchRequest request;
873
874     request.setFilter(filter);
875     request.setSorting(sortOrders);
876
877     runSyncRequest(&request, error);
878
879     return request.ids();
880 }
881
882 QList<QContact>
883 QContactTrackerEngine::contacts(const QContactFilter& filter,
884                                 const QList<QContactSortOrder>& sortOrders,
885                                 const QContactFetchHint& fetchHint,
886                                 QContactManager::Error* error) const
887 {
888     QContactFetchRequest request;
889
890     request.setFetchHint(fetchHint);
891     request.setFilter(filter);
892     request.setSorting(sortOrders);
893
894     runSyncRequest(&request, error);
895
896     return request.contacts();
897 }
898
899 QList<QContact>
900 QContactTrackerEngine::contacts(const QList<QContactLocalId> &localIds,
901                                 const QContactFetchHint &fetchHint,
902                                 ErrorMap *errorMap,
903                                 QContactManager::Error *error) const
904 {
905     QContactFetchByIdRequest request;
906
907     request.setLocalIds(localIds);
908     request.setFetchHint(fetchHint);
909
910     runSyncRequest(&request, error);
911
912     qctPropagate(request.errorMap(), errorMap);
913
914     return request.contacts();
915 }
916
917 QContact
918 QContactTrackerEngine::contact(const QContactLocalId& contactId,
919                                const QContactFetchHint& fetchHint,
920                                QContactManager::Error* error) const
921 {
922     static bool warningNotShownYet = true;
923
924     if (warningNotShownYet) {
925         if (not hasDebugFlag(SkipNagging)) {
926             qctWarn(QString::fromLatin1("\n"
927                     "=============================================================================\n"
928                     "WARNING /!\\ - AVOID CALLING THIS FUNCTION FROM PRODUCTION CODE!!!\n"
929                     "=============================================================================\n"
930                     "QContactManager::contact() is blocking on D-Bus roundtrip while accessing\n"
931                     "tracker. Please consider using batch API (QContactManager::contacts()),\n"
932                     "or even better use the asynchronous QContactFetchRequest API, instead of\n"
933                     "fetching contacts one by one.\n"
934                     "\n"
935                     "Please note that reading 100 ids and 100 contact by ids is ~100 times\n"
936                     "slower than reading 100 contacts at once with QContactFetchRequest.\n"
937                     "\n"
938                     "Offending application is %1 [%2].\n"
939                     "=============================================================================").
940                     arg(QCoreApplication::applicationFilePath(),
941                         QString::number(QCoreApplication::applicationPid())));
942         }
943
944         warningNotShownYet = false;
945     }
946
947     return contactImpl(contactId, fetchHint, error);
948 }
949
950 // Used in tests, removed warning while decided if to provide sync api.
951 // Until then customers are advised to use async.
952 QContact
953 QContactTrackerEngine::contactImpl(const QContactLocalId& contactId,
954                                    const QContactFetchHint& fetchHint,
955                                    QContactManager::Error* error) const
956 {
957     QContactLocalIdFilter idFilter;
958     idFilter.setIds(QList<QContactLocalId>() << contactId);
959
960     QContactFetchRequest request;
961     request.setFetchHint(fetchHint);
962     request.setFilter(idFilter);
963
964     if (not runSyncRequest(&request, error)) {
965         return QContact();
966     }
967
968     QList<QContact> contacts(request.contacts());
969
970     if (contacts.isEmpty()) {
971         qctPropagate(QContactManager::DoesNotExistError, error);
972         return QContact();
973     }
974
975     if (contacts.count() > 1) {
976         qctWarn(QString::fromLatin1("Expected only one contact, but got %1").arg(contacts.count()));
977     }
978
979     return contacts.first();
980 }
981
982 bool
983 QContactTrackerEngine::saveContact(QContact* contact, QContactManager::Error* error)
984 {
985     if (0 == contact) {
986         qctPropagate(QContactManager::BadArgumentError, error);
987         return false;
988     }
989
990     QList<QContact> contactList = QList<QContact>() << *contact;
991     const bool success = saveContacts(&contactList, 0, error);
992
993     if (success) {
994         setContactDisplayLabel(contact, contactList.first().displayLabel());
995         contact->setId(contactList.first().id());
996     }
997
998     return success;
999 }
1000
1001 bool
1002 QContactTrackerEngine::saveContacts(QList<QContact> *contacts,
1003                                     ErrorMap *errorMap, QContactManager::Error *error)
1004 {
1005     return saveContacts(contacts, QStringList(), errorMap, error);
1006 }
1007
1008 bool
1009 QContactTrackerEngine::saveContacts(QList<QContact> *contacts,
1010                                     const QStringList &definitionMask,
1011                                     ErrorMap *errorMap, QContactManager::Error *error)
1012 {
1013     if (0 == contacts) {
1014         qctPropagate(QContactManager::BadArgumentError, error);
1015         return false;
1016     }
1017
1018     QContactSaveRequest request;
1019     request.setContacts(*contacts);
1020     request.setDefinitionMask(definitionMask);
1021
1022     const bool hasFinished = runSyncRequest(&request, error);
1023     qctPropagate(request.errorMap(), errorMap);
1024
1025     if (not hasFinished) {
1026         return false;
1027     }
1028
1029     QList<QContact>::Iterator outputIter = contacts->begin();
1030
1031     // only update the contact id and the display label, all other updates must be fetched
1032     foreach(const QContact &savedContact, request.contacts()) {
1033         setContactDisplayLabel(&(*outputIter), savedContact.displayLabel());
1034         outputIter->setId(savedContact.id());
1035         outputIter++;
1036     }
1037
1038     return QContactManager::NoError == request.error();
1039 }
1040
1041
1042 bool
1043 QContactTrackerEngine::removeContact(const QContactLocalId &contactId,
1044                                      QContactManager::Error *error)
1045 {
1046     return removeContacts(QList<QContactLocalId>() << contactId, 0, error);
1047 }
1048
1049 bool
1050 QContactTrackerEngine::removeContacts(const QList<QContactLocalId>& contactIds,
1051                                       ErrorMap *errorMap, QContactManager::Error *error)
1052 {
1053     QContactRemoveRequest request;
1054
1055     request.setContactIds(contactIds);
1056
1057     runSyncRequest(&request, error);
1058
1059     qctPropagate(request.errorMap(), errorMap);
1060
1061     return QContactManager::NoError == request.error();
1062 }
1063
1064 QList<QContactRelationship>
1065 QContactTrackerEngine::relationships(const QString &relationshipType,
1066                                      const QContactId &participantId,
1067                                      QContactRelationship::Role role,
1068                                      QContactManager::Error *error) const
1069 {
1070     // Mapping of this call to QContactRelationshipFetchRequest is not directly possible
1071     // as QContactRelationshipFetchRequest gets set first and second contact but not
1072     // just one participantId and a role.
1073     //
1074     // So in the case that role is QContactRelationship::Either two separate requests need to be done.
1075     // As a contact is not allowed to be on the both side of any relationship this should not result in
1076     // duplicates.
1077     //
1078     // If participantId is the default-constructed QContactId, all relationships of the given type shall
1079     // be returned and the role ignored. The first request is used for that.
1080
1081
1082     if (role == QContactRelationship::Either && participantId != QContactId()) {
1083         QContactManager::Error internalError;
1084         QList<QContactRelationship> result;
1085
1086         internalError = QContactManager::UnspecifiedError;
1087         result = relationships(relationshipType, participantId,
1088                                QContactRelationship::First,
1089                                &internalError);
1090
1091         if (internalError != QContactManager::NoError) {
1092             qctPropagate(internalError, error);
1093             return QList<QContactRelationship>();
1094         }
1095
1096         internalError = QContactManager::UnspecifiedError;
1097         result += relationships(relationshipType, participantId,
1098                                 QContactRelationship::Second,
1099                                 &internalError);
1100
1101         if (internalError != QContactManager::NoError) {
1102             qctPropagate(internalError, error);
1103             return QList<QContactRelationship>();
1104         }
1105
1106         return result;
1107     }
1108
1109     QContactRelationshipFetchRequest request;
1110
1111     request.setRelationshipType(relationshipType);
1112
1113     switch(role) {
1114     case QContactRelationship::First:
1115         request.setFirst(participantId);
1116         break;
1117     case QContactRelationship::Second:
1118         request.setSecond(participantId);
1119         break;
1120     case QContactRelationship::Either:
1121         break;
1122     }
1123
1124     runSyncRequest(&request, error);
1125     return request.relationships();
1126 }
1127
1128 bool
1129 QContactTrackerEngine::saveRelationship(QContactRelationship *relationship, QContactManager::Error *error)
1130 {
1131     if (0 == relationship) {
1132         qctPropagate(QContactManager::BadArgumentError, error);
1133         return false;
1134     }
1135
1136     QList<QContactRelationship> relationships =
1137             QList<QContactRelationship>() << *relationship;
1138
1139     const bool success = saveRelationships(&relationships, 0, error);
1140
1141     *relationship = relationships.first();
1142
1143     return success;
1144 }
1145
1146 bool
1147 QContactTrackerEngine::saveRelationships(QList<QContactRelationship> *relationships,
1148                                          ErrorMap *errorMap, QContactManager::Error *error)
1149 {
1150     if (0 == relationships) {
1151         qctPropagate(QContactManager::BadArgumentError, error);
1152         return false;
1153     }
1154
1155     QContactRelationshipSaveRequest request;
1156     request.setRelationships(*relationships);
1157
1158     runSyncRequest(&request, error);
1159
1160     *relationships = request.relationships();
1161     qctPropagate(request.errorMap(), errorMap);
1162
1163     return QContactManager::NoError == request.error();
1164 }
1165
1166 bool
1167 QContactTrackerEngine::removeRelationship(const QContactRelationship &relationship, QContactManager::Error *error)
1168 {
1169     return removeRelationships(QList<QContactRelationship>() << relationship, 0, error);
1170 }
1171
1172 bool
1173 QContactTrackerEngine::removeRelationships(const QList<QContactRelationship> &relationships,
1174                                            ErrorMap *errorMap, QContactManager::Error *error)
1175 {
1176     QContactRelationshipRemoveRequest request;
1177     request.setRelationships(relationships);
1178
1179     runSyncRequest(&request, error);
1180     qctPropagate(request.errorMap(), errorMap);
1181
1182     return QContactManager::NoError == request.error();
1183 }
1184
1185 /// returns the DisplayNameDetailList for the name order given, if found.
1186 /// Default is the one for QContactDisplayLabel__FirstNameLastNameOrder.
1187 static const QList<QctDisplayLabelGenerator> &
1188 findDisplayNameGenerators(const QString &nameOrder)
1189 {
1190     const QctSettings *const settings = QctThreadLocalData::instance()->settings();
1191
1192     QctDisplayLabelGenerator::ListOptions options = 0;
1193
1194     if (nameOrder == QContactDisplayLabel__FieldOrderFirstName) {
1195         options |= QctDisplayLabelGenerator::PreferFirstName;
1196     } else if (nameOrder == QContactDisplayLabel__FieldOrderLastName) {
1197         options |= QctDisplayLabelGenerator::PreferLastName;
1198     } else {
1199         if (nameOrder != settings->nameOrder()) {
1200             // use configured name-order setting when an unknown value was passed
1201             return findDisplayNameGenerators(settings->nameOrder());
1202         }
1203
1204         // use a default value when the name-order setting itself is wrong
1205         return findDisplayNameGenerators(QctSettings::DefaultNameOrder);
1206     }
1207
1208     if (settings->preferNickname()) {
1209         options |= QctDisplayLabelGenerator::PreferNickname;
1210     }
1211
1212     return QctDisplayLabelGenerator::getList(options);
1213 }
1214
1215 QContactFetchHint
1216 QContactTrackerEngine::normalizedFetchHint(QContactFetchHint fetchHint, const QString &nameOrder)
1217 {
1218     QStringList detailDefinitionHint = fetchHint.detailDefinitionsHint();
1219
1220     // Make sure the display name can be synthesized when needed
1221     if (detailDefinitionHint.contains(QContactDisplayLabel::DefinitionName)) {
1222         foreach(const QctDisplayLabelGenerator &generator, findDisplayNameGenerators(nameOrder)) {
1223             if (not detailDefinitionHint.contains(generator.requiredDetailName())) {
1224                 detailDefinitionHint += generator.requiredDetailName();
1225             }
1226         }
1227
1228         fetchHint.setDetailDefinitionsHint(detailDefinitionHint);
1229     }
1230
1231     return fetchHint;
1232 }
1233
1234 void
1235 QContactTrackerEngine::updateDisplayLabel(QContact &contact, const QString &nameOrder) const
1236 {
1237     setContactDisplayLabel(&contact, createDisplayLabel(contact, nameOrder));
1238 }
1239
1240 QString
1241 QContactTrackerEngine::createDisplayLabel(const QContact &contact,
1242                                           const QString &nameOrder) const
1243 {
1244     QString label;
1245
1246     foreach(const QctDisplayLabelGenerator &generator, findDisplayNameGenerators(nameOrder)) {
1247         label = generator.createDisplayLabel(contact);
1248
1249         if (not label.isEmpty()) {
1250             break;
1251         }
1252     }
1253
1254     return label;
1255 }
1256
1257 template <class T> static void
1258 transfer(const T &key, const QContactDetail &source, QContactDetail &target)
1259 {
1260     QVariant value(source.variantValue(key));
1261
1262     if (not value.isNull()) {
1263         target.setValue(key, value);
1264     }
1265 }
1266
1267 typedef QPair<QContactAvatar, int> AvatarWithAvailability;
1268
1269 inline bool
1270 operator<(const AvatarWithAvailability &a, const AvatarWithAvailability &b)
1271 {
1272     // the lower the availability rank, the more significant the detail
1273     return a.second > b.second;
1274 }
1275
1276 void
1277 QContactTrackerEngine::updateAvatar(QContact &contact)
1278 {
1279     QContactPersonalAvatar personalAvatar = contact.detail<QContactPersonalAvatar>();
1280     const QList<QContactOnlineAvatar> currentOnlineAvatars = contact.details<QContactOnlineAvatar>();
1281
1282     // Function was triggered by an update of nco:photo
1283     if (not personalAvatar.isEmpty()) {
1284         QContactAvatar photoAvatar = personalAvatar.toAvatar();
1285
1286         // If there was already a nco:photo (no linked uri), remove it
1287         contact.removeDetail(&personalAvatar);
1288
1289         // nco:photo always goes first, so that QContact::detail<QContactAvatar> returns it
1290         contact.saveDetail(&photoAvatar);
1291     }
1292
1293     // Function was triggered by an update of a IMAddress' imAvatar
1294     if (not currentOnlineAvatars.empty()) {
1295         // find all available presence details
1296         QMap<QString, QContactPresence> presenceDetails;
1297
1298         foreach(const QContactPresence &detail, contact.details<QContactPresence>()) {
1299             const QString accountPath = parsePresenceIri(detail.detailUri());
1300
1301             if (not accountPath.isEmpty()) {
1302                 presenceDetails.insert(accountPath, detail);
1303             }
1304         }
1305
1306         // store online avatars with their availablity
1307         QList<AvatarWithAvailability> avatarsWithAvailability;
1308
1309         foreach(QContactOnlineAvatar avatar, currentOnlineAvatars) {
1310             contact.removeDetail(&avatar);
1311
1312             // FIXME: properly define schema for detailUri and linkedDetailUri
1313             QStringList linkedDetailUris = avatar.linkedDetailUris();
1314
1315             if (linkedDetailUris.isEmpty()) {
1316                 linkedDetailUris += QString();
1317             }
1318
1319             foreach(const QString &uri, linkedDetailUris) {
1320                 const QString accountPath = not uri.isEmpty()
1321                         ? parseTelepathyIri(uri)
1322                         : QString();
1323
1324                 int availability = qctAvailabilityRank(QContactPresence::PresenceUnknown);
1325
1326                 if (not accountPath.isEmpty()) {
1327                     const QContactPresence presence = presenceDetails.value(accountPath);
1328                     availability = qctAvailabilityRank(presence.presenceState());
1329                 } else {
1330                     qctWarn("QTrackerContactOnlineAvatar should always be linked with an online account");
1331                 }
1332
1333                 avatarsWithAvailability += qMakePair(avatar.toAvatar(), availability);
1334             }
1335         }
1336
1337         // sort avatars by their availablity
1338         qSort(avatarsWithAvailability);
1339
1340         // add regular avatar details to contact
1341         for(int i = 0; i < avatarsWithAvailability.count(); ++i) {
1342             contact.saveDetail(&avatarsWithAvailability[i].first);
1343         }
1344     }
1345 }
1346
1347 bool
1348 QContactTrackerEngine::isWeakSyncTarget(const QString &syncTarget) const
1349 {
1350     return mangleAllSyncTargets() || weakSyncTargets().contains(syncTarget);
1351 }
1352
1353 QString
1354 QContactTrackerEngine::gcQueryId() const
1355 {
1356     return d->m_gcQueryId;
1357 }
1358
1359 // TODO: we could cache the return value in this function
1360 QString
1361 QContactTrackerEngine::cleanupQueryString() const
1362 {
1363     // Notice FILTER in WHERE clause - garbage collector needs to verify parenting outside this graph too.
1364     // reason - IMAccount hasIMAddress in contactsd private graph so no need to delete it. The same could apply
1365     // to any other property - it could have been added to parent in some other graph.
1366     // Camera geotagging creates addresses outside qct graph and have no "parent" contact pointing
1367     // to them through nco:hasAddress
1368     static const QString obsoleteResourcesPrefix = QLatin1String
1369             ("\n"
1370              "DELETE\n"
1371              "{\n"
1372              "  GRAPH <%1>\n"
1373              "  {\n"
1374              "    ?resource a <%2> .\n"
1375              "  }\n"
1376              "}\n"
1377              "WHERE\n"
1378              "{\n"
1379              "  GRAPH <%1>\n"
1380              "  {\n"
1381              "    ?resource a <%3> .\n"
1382              "  }\n");
1383     static const QString imAccountStopPattern = QLatin1String
1384             ("    FILTER(NOT EXISTS { ?resource a nco:IMAccount }) .\n");
1385     static const QString obsoleteResourcesPattern = QLatin1String
1386             ("    FILTER(NOT EXISTS { ?parent <%1> ?resource }) .\n");
1387     static const QString obsoleteResourcesSuffix = QLatin1String
1388             ("}\n");
1389
1390     QString queryString;
1391
1392 #ifndef QT_NO_DEBUG
1393     queryString += QLatin1String
1394             ("\n"
1395              "#--------------------------------------.\n"
1396              "# Drop drop obsolete custom properties |\n"
1397              "#--------------------------------------'\n");
1398 #endif // QT_NO_DEBUG
1399
1400     // collect obsolete resource types and their predicates
1401     typedef QMap<QString, bool> PredicateMap;
1402     typedef QMap<QString, PredicateMap> ResourceTypePredicateMap;
1403     ResourceTypePredicateMap obsoleteResources;
1404
1405     foreach(const QTrackerContactDetailSchema &schema, schemas()) {
1406         foreach(const QTrackerContactDetail &detail, schema.details()) {
1407             foreach(const PropertyInfoList &chain, detail.possessedChains()) {
1408                 const PropertyInfoBase &pi = chain.last();
1409
1410                 if (pi.rangeIri() == rdfs::Resource::iri()) {
1411                     qctWarn(QString::fromLatin1("Not cleaning up obsolete resources for %1 property"
1412                                                 "since the property's range is too generic (%2).").
1413                             arg(qctIriAlias(pi.iri()), qctIriAlias(pi.rangeIri())));
1414                     continue;
1415                 }
1416
1417                 obsoleteResources[pi.rangeIri()].insert(pi.iri(), true);
1418             }
1419         }
1420     }
1421
1422     // glue everything together
1423     queryString += obsoleteResourcesPrefix.arg(QtContactsTrackerDefaultGraphIri,
1424                                                nao::Property::iri(),
1425                                                nao::Property::iri());
1426     queryString += obsoleteResourcesPattern.arg(nao::hasProperty::iri());
1427     queryString += obsoleteResourcesSuffix;
1428
1429     for(ResourceTypePredicateMap::ConstIterator
1430         t = obsoleteResources.constBegin(); t != obsoleteResources.constEnd(); ++t) {
1431         queryString += obsoleteResourcesPrefix.arg(QtContactsTrackerDefaultGraphIri,
1432                                                    rdfs::Resource::iri(),
1433                                                    t.key());
1434
1435         if (nco::IMAddress::iri() == t.key()) {
1436             // FIXME: Remove this workaround for NB#206404 - Saving a contact using
1437             // qtcontacts-tracker causes nco:IMAccount to be removed.
1438             queryString += imAccountStopPattern;
1439         }
1440
1441         foreach(const QString &predicate, t.value().keys()) {
1442             queryString += obsoleteResourcesPattern.arg(predicate);
1443         }
1444
1445         queryString += obsoleteResourcesSuffix;
1446     }
1447
1448     return queryString;
1449 }
1450
1451 void
1452 QContactTrackerEngine::enqueueTask(QctTask *task)
1453 {
1454     d->m_queue->enqueue(task);
1455 }
1456
1457 ////////////////////////////////////////////////////////////////////////////////////////////////////
1458 // Asynchronous data access methods
1459 ////////////////////////////////////////////////////////////////////////////////////////////////////
1460
1461 bool
1462 QContactTrackerEngine::checkSecurityTokens(QContactAbstractRequest *request)
1463 {
1464     // Plugin users often fail to provide all required security tokens.
1465     // Therefore we print warnings to educate them. If the security checks
1466     // should cause trouble they can be temporarly disabled by exporting
1467     // QT_CONTACTS_TRACKER="debug=no-sec".
1468
1469     if (d->m_mandatoryTokensFound) {
1470         return true;
1471     }
1472
1473     QStringList theGateKeepersBlameList;
1474     QStringList missingOptionalTokens;
1475
1476 #ifdef ENABLE_CREDENTIALS
1477
1478     static const QStringList requiredSecurityTokens =
1479             QStringList() << QLatin1String("TrackerReadAccess")
1480                           << QLatin1String("TrackerWriteAccess");
1481
1482     static const QStringList optionalSecurityTokens =
1483             QStringList() << QLatin1String("GRP::metadata-users");
1484
1485     foreach(const QString &credential, requiredSecurityTokens) {
1486         QString errorMessage;
1487
1488         if (not MssfQt::CredentialsManager::hasProcessCredential(0, credential, &errorMessage)) {
1489             theGateKeepersBlameList += credential;
1490             qctWarn(errorMessage);
1491         }
1492     }
1493
1494     foreach(const QString &credential, optionalSecurityTokens) {
1495         QString errorMessage;
1496
1497         if (not MssfQt::CredentialsManager::hasProcessCredential(0, credential, &errorMessage)) {
1498             missingOptionalTokens += credential;
1499             qctWarn(errorMessage);
1500         }
1501     }
1502
1503 #endif // ENABLE_CREDENTIALS
1504
1505     if (not theGateKeepersBlameList.isEmpty()) {
1506         qctWarn(QString::fromLatin1("\n"
1507                  "=============================================================================\n"
1508                  "WARNING /!\\ - MANDATORY SECURITY TOKENS ARE MISSING\n"
1509                  "=============================================================================\n"
1510                  "Rejecting %2.\n"
1511                  "Please add an AEGIS manifest to your package requesting the following\n"
1512                  "security tokens: %1 for %3 [%4].\n"
1513                  "=============================================================================").
1514                 arg(theGateKeepersBlameList.join(QLatin1String(", ")),
1515                     QLatin1String(request->metaObject()->className()),
1516                     QCoreApplication::applicationFilePath(),
1517                     QString::number(QCoreApplication::applicationPid())));
1518
1519         return false;
1520     }
1521
1522     if (not missingOptionalTokens.isEmpty()) {
1523         qctWarn(QString::fromLatin1("\n"
1524                  "=============================================================================\n"
1525                  "WARNING /!\\ - OPTIONAL SECURITY TOKENS ARE MISSING\n"
1526                  "=============================================================================\n"
1527                  "Full functionality like tracker direct access is not available.\n"
1528                  "Please add an AEGIS manifest to your package requesting the following\n"
1529                  "security tokens: %1 for %2 [%3].\n"
1530                  "=============================================================================").
1531                 arg(missingOptionalTokens.join(QLatin1String(", ")),
1532                     QCoreApplication::applicationFilePath(),
1533                     QString::number(QCoreApplication::applicationPid())));
1534     }
1535
1536     d->m_mandatoryTokensFound = true;
1537
1538     return true;
1539 }
1540
1541 QctTask *
1542 QContactTrackerEngine::startRequestImpl(QContactAbstractRequest *request)
1543 {
1544     QCT_SYNCHRONIZED(&d->m_requestLifeGuard);
1545
1546     QWeakPointer<QContactAbstractRequest> guardedRequest = request;
1547
1548     // The worker puts the request under protection of the engine's request life-guard.
1549     QScopedPointer<QTrackerAbstractRequest> worker(createRequestWorker(guardedRequest.data()));
1550
1551     if (worker.isNull()) {
1552         return 0;
1553     }
1554
1555     QScopedPointer<QctRequestTask> task(new QctRequestTask(guardedRequest.data(), worker.take()));
1556
1557     // Check if some evil client managed to delete the request from some other thread.
1558     if (guardedRequest.isNull()) {
1559         return 0;
1560     }
1561
1562     // Make sure mandatory tracker:ids have been resolved.
1563     if (task->dependencies().testFlag(QTrackerAbstractRequest::ResourceCache) &&
1564         not d->m_satisfiedDependencies.testFlag(QTrackerAbstractRequest::ResourceCache)) {
1565         // Queue will take ownership of the task
1566         // We don't explicitly parent the taks when creating them, because this method
1567         // might be called from a thread which is not the one where the engine was created.
1568         // In that case, we would get the "QObject: Cannot create children for a parent that
1569         // is in a different thread" warning, and the parent would be NULL anyway...
1570         enqueueTask(new QctResourceCacheTask(schemas()));
1571         d->m_satisfiedDependencies |= QTrackerAbstractRequest::ResourceCache;
1572     }
1573
1574     // Make sure GUID algorithms are available.
1575     if (task->dependencies().testFlag(QTrackerAbstractRequest::GuidAlgorithm) &&
1576         not d->m_satisfiedDependencies.testFlag(QTrackerAbstractRequest::GuidAlgorithm)) {
1577         // Queue will take ownership of the task
1578         // See above why we don't set the parent here
1579         enqueueTask(new QctGuidAlgorithmTask(this));
1580         d->m_satisfiedDependencies |= QTrackerAbstractRequest::GuidAlgorithm;
1581     }
1582
1583     // The contacts engine must bring requests into activate state as soon as it is dealing
1584     // with them. We cannot queue the task first, since the queue might be empty right now,
1585     // causing the task to start work from inactive state. This would be ridicilous.
1586     updateRequestState(guardedRequest.data(), QContactAbstractRequest::ActiveState);
1587
1588     // Check if some evil client managed to delete the request from some slot (or thread).
1589     if (guardedRequest.isNull()) {
1590         return 0;
1591     }
1592
1593     // Transfer task ownership to the queue.
1594     QctRequestTask *const result = task.data();
1595     d->m_queue->enqueue(task.take());
1596     return result;
1597 }
1598
1599 bool
1600 QContactTrackerEngine::startRequest(QContactAbstractRequest *request)
1601 {
1602     startRequestImpl(request);
1603     return true; // this always works
1604 }
1605
1606 QTrackerAbstractRequest *
1607 QContactTrackerEngine::createRequestWorker(QContactAbstractRequest *request)
1608 {
1609     // about if mandatory security tokens are missing
1610     if (not checkSecurityTokens(request)) {
1611         return 0;
1612     }
1613
1614     // ensure old worker got destroyed when request gets reused
1615     requestDestroyed(request);
1616
1617     QTrackerAbstractRequest *worker = 0;
1618     QElapsedTimer t; t.start();
1619
1620     // choose proper request implementation
1621     switch(request->type())
1622     {
1623     case QContactAbstractRequest::ContactFetchRequest:
1624         worker = new QTrackerContactFetchRequest(request, this);
1625         break;
1626
1627     case QContactAbstractRequest::ContactFetchByIdRequest:
1628         worker = new QTrackerContactFetchByIdRequest(request, this);
1629         break;
1630
1631     case QContactAbstractRequest::ContactLocalIdFetchRequest:
1632         worker = new QTrackerContactIdFetchRequest(request, this);
1633         break;
1634
1635     case QContactAbstractRequest::ContactRemoveRequest:
1636         if (0 == qobject_cast<QctContactMergeRequest *>(request)) {
1637             worker = new QTrackerContactRemoveRequest(request, this);
1638         } else {
1639             worker = new QTrackerContactCopyAndRemoveRequest(request, this);
1640         }
1641         break;
1642
1643     case QContactAbstractRequest::ContactSaveRequest:
1644         if (0 == qobject_cast< QctUnmergeIMContactsRequest *> (request)) {
1645             worker = new QTrackerContactSaveRequest(request, this);
1646         } else {
1647             worker = new QTrackerContactSaveOrUnmergeRequest(request, this);
1648         }
1649         break;
1650
1651     case QContactAbstractRequest::RelationshipFetchRequest:
1652         worker = new QTrackerRelationshipFetchRequest(request, this);
1653         break;
1654
1655     case QContactAbstractRequest::RelationshipRemoveRequest:
1656         worker = new QTrackerRelationshipRemoveRequest(request, this);
1657         break;
1658
1659     case QContactAbstractRequest::RelationshipSaveRequest:
1660         worker = new QTrackerRelationshipSaveRequest(request, this);
1661         break;
1662
1663     case QContactAbstractRequest::DetailDefinitionFetchRequest:
1664         worker = new QTrackerDetailDefinitionFetchRequest(request, this);
1665         break;
1666
1667     case QContactAbstractRequest::InvalidRequest:
1668     case QContactAbstractRequest::DetailDefinitionRemoveRequest:
1669     case QContactAbstractRequest::DetailDefinitionSaveRequest:
1670         break;
1671     }
1672
1673     if (0 == worker) {
1674         qctWarn(QString::fromLatin1("Unsupported request type: %1").
1675                 arg(QLatin1String(request->metaObject()->className())));
1676         return 0;
1677     }
1678
1679     if (hasDebugFlag(ShowNotes)) {
1680         qDebug() << Q_FUNC_INFO << "time elapsed while constructing request workers:"<< request << t.elapsed();
1681     }
1682
1683     if (hasDebugFlag(ShowNotes)) {
1684         qDebug() << Q_FUNC_INFO << "running" << worker->metaObject()->className();
1685     }
1686
1687     // XXX The unit tests directly access the engine. Therefore requests created by our unit
1688     // tests don't have a manager attached. This prevents ~QContactAbstractRequest() to call our
1689     // engine's requestDestroyed(QContactAbstractRequest*) method, which results in memory leaks
1690     // within our our unit tests. To prevent those leaks we watch the request's destroyed()
1691     // signal and forward those signals to requestDestroyed(QContactAbstractRequest*) when
1692     // a request without contact manager is found.
1693     //
1694     // XXX This all of course is an ugly workaround. We probably should change the unit tests
1695     // to create use QContactManager accessing the engine via a static plugin.
1696     if (0 == request->manager()) {
1697         connect(request, SIGNAL(destroyed(QObject*)),
1698                 this, SLOT(onRequestDestroyed(QObject*)), Qt::DirectConnection);
1699     }
1700
1701     // track original request for this worker
1702     QCT_SYNCHRONIZED_WRITE(&d->m_tableLock);
1703
1704     d->m_workersByRequest.insert(request, worker);
1705     d->m_requestsByWorker.insert(worker, request);
1706
1707     return worker;
1708 }
1709
1710 QctRequestLocker
1711 QContactTrackerEngine::request(const QTrackerAbstractRequest *worker) const
1712 {
1713     QScopedPointer<QMutexLocker> locker(new QMutexLocker(&d->m_requestLifeGuard));
1714     QCT_SYNCHRONIZED_READ(&d->m_tableLock);
1715     return QctRequestLocker(locker.take(), d->m_requestsByWorker.value(worker));
1716 }
1717
1718 void
1719 QContactTrackerEngine::connectSignals()
1720 {
1721     if (d->m_changeListener) {
1722         connect(d->m_changeListener,
1723                 SIGNAL(contactsAdded(QList<QContactLocalId>)),
1724                 SIGNAL(contactsAdded(QList<QContactLocalId>)));
1725         connect(d->m_changeListener,
1726                 SIGNAL(contactsChanged(QList<QContactLocalId>)),
1727                 SIGNAL(contactsChanged(QList<QContactLocalId>)));
1728         connect(d->m_changeListener,
1729                 SIGNAL(contactsRemoved(QList<QContactLocalId>)),
1730                 SIGNAL(contactsRemoved(QList<QContactLocalId>)));
1731         connect(d->m_changeListener,
1732                 SIGNAL(relationshipsAdded(QList<QContactLocalId>)),
1733                 SIGNAL(relationshipsAdded(QList<QContactLocalId>)));
1734         connect(d->m_changeListener,
1735                 SIGNAL(relationshipsRemoved(QList<QContactLocalId>)),
1736                 SIGNAL(relationshipsRemoved(QList<QContactLocalId>)));
1737     }
1738 }
1739
1740 void
1741 QContactTrackerEngine::disconnectSignals()
1742 {
1743     if (d->m_changeListener) {
1744         d->m_changeListener->disconnect(this);
1745         d->m_changeListener = 0;
1746     }
1747 }
1748
1749 void
1750 QContactTrackerEngine::registerGcQuery()
1751 {
1752     d->m_gcQueryId = QString::fromLatin1("com.nokia.qtcontacts-tracker");
1753     QctGarbageCollector::registerQuery(d->m_gcQueryId, cleanupQueryString());
1754 }
1755
1756 void
1757 QContactTrackerEngine::connectNotify(const char *signal)
1758 {
1759     if (0 == d->m_changeListener) {
1760         // Create the change listener on demand since:
1761         //
1762         // 1) Creating the listener is expensive as we must subscribe to some DBus signals.
1763         // 2) Watching DBus without any specific need wastes energy by wakeing up processes.
1764         //
1765         // Share the change listener for the reasons listed above.
1766         //
1767         // Still only share per thread (instead of process) to avoid nasty situations like the
1768         // random, listener owning thread terminating before other threads using the listener.
1769
1770         // Must monitor individual contact classes instead of nco:Contact
1771         // to avoid bogus notifications for:
1772         //
1773         //  - QContactOrganization details, implemented via nco:OrganizationContact
1774         //  - incoming calls which cause call-ui to create temporary(?) nco:Contact instances
1775         //
1776         // Additionally, nco:Contact does not have the tracker:notify property set to true, whereas
1777         // nco:PersonContact and nco:ContactGroup do have the property, so only those classes will
1778         // receive notifications of changes.
1779         QSet<QString> contactClassIris;
1780
1781         foreach (const QTrackerContactDetailSchema &schema, d->m_parameters.m_detailSchemas) {
1782             foreach(const QString &iri, schema.contactClassIris()) {
1783                 if (iri != nco::Contact::iri()) {
1784                     contactClassIris += iri;
1785                 }
1786             }
1787         }
1788
1789         // Compute change filtering mode.
1790         QctTrackerChangeListener::ChangeFilterMode changeFilterMode = QctTrackerChangeListener::AllChanges;
1791
1792         if (d->m_parameters.m_omitPresenceChanges) {
1793             changeFilterMode = QctTrackerChangeListener::IgnorePresenceChanges;
1794         }
1795
1796         // Compute debug flags for the listener.
1797         QctTrackerChangeListener::DebugFlags debugFlags = QctTrackerChangeListener::NoDebug;
1798
1799         if (d->m_parameters.m_debugFlags.testFlag(QContactTrackerEngine::ShowSignals)) {
1800             debugFlags |= QctTrackerChangeListener::PrintSignals;
1801         }
1802
1803         // Compute cache id for the listener.
1804         // Cannot use the manager URI because it doesn't expose all relevant parameters.
1805         const QString listenerId = (QStringList(contactClassIris.toList()) <<
1806                                     QString::number(d->m_parameters.m_coalescingDelay) <<
1807                                     QString::number(changeFilterMode) <<
1808                                     QString::number(debugFlags)).join(QLatin1String(";"));
1809
1810         // Check if there already exists a listener for that computed id.
1811         QctThreadLocalData *const threadLocalData = QctThreadLocalData::instance();
1812         d->m_changeListener = threadLocalData->trackerChangeListener(listenerId);
1813
1814         if (0 == d->m_changeListener) {
1815             // Create new listener when needed.
1816             d->m_changeListener = new QctTrackerChangeListener(contactClassIris.toList(), QThread::currentThread());
1817             d->m_changeListener->setCoalescingDelay(d->m_parameters.m_coalescingDelay);
1818             d->m_changeListener->setChangeFilterMode(changeFilterMode);
1819             d->m_changeListener->setDebugFlags(debugFlags);
1820
1821             threadLocalData->setTrackerChangeListener(listenerId, d->m_changeListener);
1822         }
1823
1824         // Monitor the choosen listener's signals.
1825         connectSignals();
1826     }
1827
1828     QContactManagerEngine::connectNotify(signal);
1829 }
1830
1831 void
1832 QContactTrackerEngine::requestDestroyed(QContactAbstractRequest *req)
1833 {
1834     dropRequest(QctRequestLocker(new QMutexLocker(&d->m_requestLifeGuard), req));
1835 }
1836
1837 void
1838 QContactTrackerEngine::dropRequest(const QctRequestLocker &req)
1839 {
1840     QCT_SYNCHRONIZED_WRITE(&d->m_tableLock);
1841
1842     if (req.isNull()) {
1843         return;
1844     }
1845
1846     QTrackerAbstractRequest *const worker = d->m_workersByRequest.take(req.data());
1847
1848     if (0 != worker) {
1849         d->m_requestsByWorker.remove(worker);
1850     }
1851 }
1852
1853 void
1854 QContactTrackerEngine::onRequestDestroyed(QObject *req)
1855 {
1856     // dynamic_cast<> does not work at this point (has a 0 result, or even crashes) because the
1857     // derived parts of the class have already been destroyed by the time the base QObject's
1858     // destructor has emitted this signal. So we do a regular static case, because
1859     // requestDestroyed(QContactAbstractRequest*) just compares the pointer value anyway.
1860     requestDestroyed(static_cast<QContactAbstractRequest *>(req));
1861 }
1862
1863 bool
1864 QContactTrackerEngine::waitForRequestFinished(QContactAbstractRequest *request, int msecs)
1865 {
1866     if (not hasDebugFlag(SkipNagging)) {
1867         qctWarn(QString::fromLatin1("\n"
1868             "=============================================================================\n"
1869             "WARNING /!\\ - NEVER EVER CALL THIS FUNCTION FROM PRODUCTION CODE!!!\n"
1870             "=============================================================================\n"
1871             "QContactAbstractRequest::waitForFinished(), respectively\n"
1872             "QContactManagerEngine::waitForRequestFinished() must spin your\n"
1873             "application's event loop. Doing so will cause HAVOC AND PAIN for\n"
1874             "any non-trivial program.\n"
1875             "\n"
1876             "So please refactor your asynchronious code, or use the synchronious\n"
1877             "API if blocking your application is acceptable.\n"
1878             "\n"
1879             "WE MEAN IT!!!\n"
1880             "\n"
1881             "Offending application is %1 [%2].\n"
1882             "=============================================================================").
1883             arg(QCoreApplication::applicationFilePath(),
1884                 QString::number(QCoreApplication::applicationPid())));
1885     }
1886
1887     return waitForRequestFinishedImpl(request, msecs);
1888 }
1889
1890 bool
1891 QContactTrackerEngine::waitForRequestFinishedImpl(QContactAbstractRequest *request, int msecs)
1892 {
1893     if (0 == request) {
1894         return false;
1895     }
1896
1897     RequestEventLoop eventLoop(request, msecs);
1898
1899     if (not eventLoop.isFinished()) {
1900         eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
1901     }
1902
1903     return eventLoop.isFinished();
1904 }
1905
1906 bool
1907 QContactTrackerEngine::runSyncRequest(QContactAbstractRequest *request,
1908                                       QContactManager::Error *error) const
1909 {
1910     // Copy the engine to prevent forwarding side-effects to the caller's engine.
1911     // It costs a bit, but guess that's a justified penalty to all sync API users.
1912     QContactTrackerEngine taskEngine(*this);
1913
1914     QctTask *const task = taskEngine.startRequestImpl(request);
1915
1916     if (0 != task && not QctTaskWaiter(task).wait(requestTimeout())) {
1917         qctPropagate(QContactManager::UnspecifiedError, error);
1918         return false;
1919     }
1920
1921     qctPropagate(request->error(), error);
1922
1923     return true;
1924 }
1925
1926 ////////////////////////////////////////////////////////////////////////////////////////////////////
1927 // Synthesized contact details
1928 ////////////////////////////////////////////////////////////////////////////////////////////////////
1929
1930 QString
1931 QContactTrackerEngine::synthesizedDisplayLabel(const QContact& contact,
1932                                                QContactManager::Error* error) const
1933 {
1934     qctPropagate(QContactManager::NoError, error);
1935
1936     return createDisplayLabel(contact);
1937 }
1938
1939 QContact
1940 QContactTrackerEngine::compatibleContact(const QContact &original, QContactManager::Error* error) const
1941 {
1942     QContact contact = original;
1943
1944     foreach(const QTrackerContactDetail &detail, schema(contact.type()).details()) {
1945         QList<QContactDetail> contactDetails = contact.details(detail.name());
1946
1947         if (contactDetails.empty()) {
1948             continue;
1949         }
1950
1951         // Check that detail respects our schema's cardinality
1952         if (not detail.isUnique()) {
1953             continue;
1954         }
1955
1956         if (contactDetails.count() < 2) {
1957             continue;
1958         }
1959
1960         qctWarn(QString::fromLatin1("Dropping odd details: %2 detail must be unique").
1961                 arg(detail.name()));
1962
1963         for(int i = 1; i < contactDetails.count(); ++i) {
1964             contact.removeDetail(&contactDetails[i]);
1965         }
1966     }
1967
1968     // Check fields contents
1969     foreach(const QTrackerContactDetail &detail, schema(contact.type()).details()) {
1970         QList<QContactDetail> contactDetails = contact.details(detail.name());
1971
1972         foreach (QContactDetail contactDetail, contactDetails) {
1973             const QVariantMap detailValues = contactDetail.variantValues();
1974
1975             for (QVariantMap::ConstIterator it = detailValues.constBegin();
1976                  it != detailValues.constEnd(); ++it) {
1977                 const QTrackerContactDetailField *field = detail.field(it.key());
1978
1979                 if (field == 0) {
1980                     // We can't validate custom fields, skip
1981                     continue;
1982                 }
1983
1984                 QVariant computedValue;
1985
1986                 if (not field->makeValue(it.value(), computedValue)) {
1987                     // Apparently this is not an error, we just prune the field...
1988                     // makeValue already prints a debug message if it returns false,
1989                     // so we can stay silent here. No idea how UI can provide any
1990                     // useful feedback out of that.
1991                     contactDetail.removeValue(it.key());
1992                     break;
1993                 }
1994
1995                 // The computed value is only useful to generate SPARQL queries,
1996                 // we should not save it back to the field.
1997             }
1998
1999             contact.saveDetail(&contactDetail);
2000         }
2001     }
2002
2003     qctPropagate(QContactManager::NoError, error);
2004
2005     return contact;
2006 }
2007
2008 ////////////////////////////////////////////////////////////////////////////////////////////////////
2009 // Unsupported functions
2010 ////////////////////////////////////////////////////////////////////////////////////////////////////
2011
2012 bool
2013 QContactTrackerEngine::validateContact(const QContact&, QContactManager::Error* error) const
2014 {
2015     REPORT_UNSUPPORTED_FUNCTION(error);
2016     return false;
2017 }
2018
2019 bool
2020 QContactTrackerEngine::validateDefinition(const QContactDetailDefinition&,
2021                                           QContactManager::Error* error) const
2022 {
2023     REPORT_UNSUPPORTED_FUNCTION(error);
2024     return false;
2025 }
2026
2027 bool
2028 QContactTrackerEngine::saveDetailDefinition(const QContactDetailDefinition&, const QString&,
2029                                             QContactManager::Error* error)
2030 {
2031     REPORT_UNSUPPORTED_FUNCTION(error);
2032     return false;
2033 }
2034
2035 bool
2036 QContactTrackerEngine::removeDetailDefinition(const QString&, const QString&,
2037                                               QContactManager::Error* error)
2038 {
2039     REPORT_UNSUPPORTED_FUNCTION(error);
2040     return false;
2041 }
2042
2043 bool
2044 QContactTrackerEngine::cancelRequest(QContactAbstractRequest *request)
2045 {
2046     if (0 == request) {
2047         return false;
2048     }
2049
2050     QCT_SYNCHRONIZED(&d->m_requestLifeGuard);
2051     QCT_SYNCHRONIZED_READ(&d->m_tableLock);
2052
2053     QTrackerAbstractRequest *const worker = d->m_workersByRequest.value(request);
2054
2055     if (0 != worker) {
2056         return worker->cancel();
2057     }
2058     return false;
2059 }
2060
2061 bool
2062 QContactTrackerEngine::isRelationshipTypeSupported(const QString& relationshipType,
2063                                                    const QString& contactType) const
2064 {
2065     if (relationshipType == QContactRelationship::HasMember
2066             && supportedContactTypes().contains(contactType)
2067             && (contactType == QContactType::TypeContact
2068                 || contactType == QContactType::TypeGroup)) {
2069         return true;
2070     }
2071
2072     return false;
2073 }