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