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