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