Fixes: Restore saveContact() method
[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     const bool success = saveContacts(&contactList, 0, error);
1049
1050     if (success) {
1051         setContactDisplayLabel(contact, contactList.first().displayLabel());
1052         contact->setId(contactList.first().id());
1053     }
1054
1055     return success;
1056 }
1057
1058 bool
1059 QContactTrackerEngine::saveContacts(QList<QContact> *contacts,
1060                                     ErrorMap *errorMap, QContactManager::Error *error)
1061 {
1062     return saveContacts(contacts, QStringList(), errorMap, error);
1063 }
1064
1065 bool
1066 QContactTrackerEngine::saveContacts(QList<QContact> *contacts,
1067                                     const QStringList &definitionMask,
1068                                     ErrorMap *errorMap, QContactManager::Error *error)
1069 {
1070     if (0 == contacts) {
1071         qctPropagate(QContactManager::BadArgumentError, error);
1072         return false;
1073     }
1074
1075     QContactSaveRequest request;
1076     request.setContacts(*contacts);
1077     request.setDefinitionMask(definitionMask);
1078
1079     const bool hasFinished = runSyncRequest(&request, error);
1080     qctPropagate(request.errorMap(), errorMap);
1081
1082     if (not hasFinished) {
1083         return false;
1084     }
1085
1086     const QList<QContact> savedContacts = request.contacts();
1087
1088     // only update the contact id and the display label, all other updates must be fetched
1089     for(int i = 0; i < contacts->size(); ++i) {
1090         QContact &requestContact = (*contacts)[i];
1091         const QContact &savedContact = savedContacts[i];
1092         setContactDisplayLabel(&requestContact, savedContact.displayLabel());
1093         requestContact.setId(savedContact.id());
1094     }
1095
1096     return QContactManager::NoError == request.error();
1097 }
1098
1099
1100 bool
1101 QContactTrackerEngine::removeContact(const QContactLocalId &contactId,
1102                                      QContactManager::Error *error)
1103 {
1104     return removeContacts(QList<QContactLocalId>() << contactId, 0, error);
1105 }
1106
1107 bool
1108 QContactTrackerEngine::removeContacts(const QList<QContactLocalId>& contactIds,
1109                                       ErrorMap *errorMap, QContactManager::Error *error)
1110 {
1111     QContactRemoveRequest request;
1112
1113     request.setContactIds(contactIds);
1114
1115     runSyncRequest(&request, error);
1116
1117     qctPropagate(request.errorMap(), errorMap);
1118
1119     return QContactManager::NoError == request.error();
1120 }
1121
1122 QList<QContactRelationship>
1123 QContactTrackerEngine::relationships(const QString &relationshipType,
1124                                      const QContactId &participantId,
1125                                      QContactRelationship::Role role,
1126                                      QContactManager::Error *error) const
1127 {
1128     // Mapping of this call to QContactRelationshipFetchRequest is not directly possible
1129     // as QContactRelationshipFetchRequest gets set first and second contact but not
1130     // just one participantId and a role.
1131     //
1132     // So in the case that role is QContactRelationship::Either two separate requests need to be done.
1133     // As a contact is not allowed to be on the both side of any relationship this should not result in
1134     // duplicates.
1135     //
1136     // If participantId is the default-constructed QContactId, all relationships of the given type shall
1137     // be returned and the role ignored. The first request is used for that.
1138
1139
1140     if (role == QContactRelationship::Either && participantId != QContactId()) {
1141         QContactManager::Error internalError;
1142         QList<QContactRelationship> result;
1143
1144         internalError = QContactManager::UnspecifiedError;
1145         result = relationships(relationshipType, participantId,
1146                                QContactRelationship::First,
1147                                &internalError);
1148
1149         if (internalError != QContactManager::NoError) {
1150             qctPropagate(internalError, error);
1151             return QList<QContactRelationship>();
1152         }
1153
1154         internalError = QContactManager::UnspecifiedError;
1155         result += relationships(relationshipType, participantId,
1156                                 QContactRelationship::Second,
1157                                 &internalError);
1158
1159         if (internalError != QContactManager::NoError) {
1160             qctPropagate(internalError, error);
1161             return QList<QContactRelationship>();
1162         }
1163
1164         return result;
1165     }
1166
1167     QContactRelationshipFetchRequest request;
1168
1169     request.setRelationshipType(relationshipType);
1170
1171     switch(role) {
1172     case QContactRelationship::First:
1173         request.setFirst(participantId);
1174         break;
1175     case QContactRelationship::Second:
1176         request.setSecond(participantId);
1177         break;
1178     case QContactRelationship::Either:
1179         break;
1180     }
1181
1182     runSyncRequest(&request, error);
1183     return request.relationships();
1184 }
1185
1186 bool
1187 QContactTrackerEngine::saveRelationship(QContactRelationship *relationship, QContactManager::Error *error)
1188 {
1189     if (0 == relationship) {
1190         qctPropagate(QContactManager::BadArgumentError, error);
1191         return false;
1192     }
1193
1194     QList<QContactRelationship> relationships =
1195             QList<QContactRelationship>() << *relationship;
1196
1197     const bool success = saveRelationships(&relationships, 0, error);
1198
1199     *relationship = relationships.first();
1200
1201     return success;
1202 }
1203
1204 bool
1205 QContactTrackerEngine::saveRelationships(QList<QContactRelationship> *relationships,
1206                                          ErrorMap *errorMap, QContactManager::Error *error)
1207 {
1208     if (0 == relationships) {
1209         qctPropagate(QContactManager::BadArgumentError, error);
1210         return false;
1211     }
1212
1213     QContactRelationshipSaveRequest request;
1214     request.setRelationships(*relationships);
1215
1216     runSyncRequest(&request, error);
1217
1218     *relationships = request.relationships();
1219     qctPropagate(request.errorMap(), errorMap);
1220
1221     return QContactManager::NoError == request.error();
1222 }
1223
1224 bool
1225 QContactTrackerEngine::removeRelationship(const QContactRelationship &relationship, QContactManager::Error *error)
1226 {
1227     return removeRelationships(QList<QContactRelationship>() << relationship, 0, error);
1228 }
1229
1230 bool
1231 QContactTrackerEngine::removeRelationships(const QList<QContactRelationship> &relationships,
1232                                            ErrorMap *errorMap, QContactManager::Error *error)
1233 {
1234     QContactRelationshipRemoveRequest request;
1235     request.setRelationships(relationships);
1236
1237     runSyncRequest(&request, error);
1238     qctPropagate(request.errorMap(), errorMap);
1239
1240     return QContactManager::NoError == request.error();
1241 }
1242
1243
1244 /// returns the DisplayNameDetailList for the name order given, if found.
1245 /// Default is the one for QContactDisplayLabel__FirstNameLastNameOrder.
1246 static const DisplayLabelGeneratorList &
1247 findDisplayLabelDetails(const DisplayLabelGeneratorListMap &displayNameGeneratorListMap,
1248                         const QString &nameOrder)
1249 {
1250     DisplayLabelGeneratorListMap::ConstIterator it = displayNameGeneratorListMap.find(nameOrder);
1251
1252     // not found?
1253     if (it == displayNameGeneratorListMap.constEnd()) {
1254         // use default setting
1255         it = displayNameGeneratorListMap.find(QctSettings().nameOrder());
1256     }
1257     // should not happen, but better safe than sorry:
1258     if (it == displayNameGeneratorListMap.constEnd()) {
1259         it = displayNameGeneratorListMap.constBegin();
1260     }
1261
1262     return it.value();
1263 }
1264
1265 QContactFetchHint
1266 QContactTrackerEngine::normalizedFetchHint(QContactFetchHint fetchHint, const QString &nameOrder)
1267 {
1268     QStringList detailDefinitionHint = fetchHint.detailDefinitionsHint();
1269
1270     // Make sure the display name can be synthesized when needed
1271     if (detailDefinitionHint.contains(QContactDisplayLabel::DefinitionName)) {
1272         const DisplayLabelGeneratorList &displayLabelGenerators =
1273             findDisplayLabelDetails(d->m_displayNameGeneratorListMap, nameOrder);
1274
1275         foreach(const DisplayLabelGenerator &generator, displayLabelGenerators) {
1276             if (not detailDefinitionHint.contains(generator.requiredDetailName())) {
1277                 detailDefinitionHint += generator.requiredDetailName();
1278             }
1279         }
1280
1281         fetchHint.setDetailDefinitionsHint(detailDefinitionHint);
1282     }
1283
1284     return fetchHint;
1285 }
1286
1287 void
1288 QContactTrackerEngine::updateDisplayLabel(QContact &contact, const QString &nameOrder) const
1289 {
1290     setContactDisplayLabel(&contact, createDisplayLabel(contact, nameOrder));
1291 }
1292
1293 QString
1294 QContactTrackerEngine::createDisplayLabel(const QContact &contact,
1295                                           const QString &nameOrder) const
1296 {
1297     const DisplayLabelGeneratorList &displayNameGeneratorList =
1298         findDisplayLabelDetails(d->m_displayNameGeneratorListMap, nameOrder);
1299
1300     QString label;
1301
1302     foreach(const DisplayLabelGenerator &generator, displayNameGeneratorList) {
1303         label = generator.createDisplayLabel(contact);
1304
1305         if (not label.isEmpty()) {
1306             break;
1307         }
1308     }
1309
1310     return label;
1311 }
1312
1313 template <class T> static void
1314 transfer(const T &key, const QContactDetail &source, QContactDetail &target)
1315 {
1316     QVariant value(source.variantValue(key));
1317
1318     if (not value.isNull()) {
1319         target.setValue(key, value);
1320     }
1321 }
1322
1323 typedef QPair<QContactAvatar, int> AvatarWithAvailability;
1324
1325 inline bool
1326 operator<(const AvatarWithAvailability &a, const AvatarWithAvailability &b)
1327 {
1328     // the lower the availability rank, the more significant the detail
1329     return a.second > b.second;
1330 }
1331
1332 void
1333 QContactTrackerEngine::updateAvatar(QContact &contact)
1334 {
1335     QContactPersonalAvatar personalAvatar = contact.detail<QContactPersonalAvatar>();
1336     const QList<QContactOnlineAvatar> currentOnlineAvatars = contact.details<QContactOnlineAvatar>();
1337
1338     // Function was triggered by an update of nco:photo
1339     if (not personalAvatar.isEmpty()) {
1340         QContactAvatar photoAvatar = personalAvatar.toAvatar();
1341
1342         // If there was already a nco:photo (no linked uri), remove it
1343         contact.removeDetail(&personalAvatar);
1344
1345         // nco:photo always goes first, so that QContact::detail<QContactAvatar> returns it
1346         contact.saveDetail(&photoAvatar);
1347     }
1348
1349     // Function was triggered by an update of a IMAddress' imAvatar
1350     if (not currentOnlineAvatars.empty()) {
1351         // find all available presence details
1352         QMap<QString, QContactPresence> presenceDetails;
1353
1354         foreach(const QContactPresence &detail, contact.details<QContactPresence>()) {
1355             const QString accountPath = parsePresenceIri(detail.detailUri());
1356
1357             if (not accountPath.isEmpty()) {
1358                 presenceDetails.insert(accountPath, detail);
1359             }
1360         }
1361
1362         // store online avatars with their availablity
1363         QList<AvatarWithAvailability> avatarsWithAvailability;
1364
1365         foreach(QContactOnlineAvatar avatar, currentOnlineAvatars) {
1366             contact.removeDetail(&avatar);
1367
1368             // FIXME: properly define schema for detailUri and linkedDetailUri
1369             QStringList linkedDetailUris = avatar.linkedDetailUris();
1370
1371             if (linkedDetailUris.isEmpty()) {
1372                 linkedDetailUris += QString();
1373             }
1374
1375             foreach(const QString &uri, linkedDetailUris) {
1376                 const QString accountPath = not uri.isEmpty()
1377                         ? parseTelepathyIri(uri)
1378                         : QString();
1379
1380                 int availability = qctAvailabilityRank(QContactPresence::PresenceUnknown);
1381
1382                 if (not accountPath.isEmpty()) {
1383                     const QContactPresence presence = presenceDetails.value(accountPath);
1384                     availability = qctAvailabilityRank(presence.presenceState());
1385                 } else {
1386                     qctWarn("QTrackerContactOnlineAvatar should always be linked with an online account");
1387                 }
1388
1389                 avatarsWithAvailability += qMakePair(avatar.toAvatar(), availability);
1390             }
1391         }
1392
1393         // sort avatars by their availablity
1394         qSort(avatarsWithAvailability);
1395
1396         // add regular avatar details to contact
1397         for(int i = 0; i < avatarsWithAvailability.count(); ++i) {
1398             contact.saveDetail(&avatarsWithAvailability[i].first);
1399         }
1400     }
1401 }
1402
1403 bool
1404 QContactTrackerEngine::isWeakSyncTarget(const QString &syncTarget) const
1405 {
1406     if (d->m_parameters.m_weakSyncTargets.contains(syncTarget)) {
1407         return true;
1408     }
1409
1410     return (d->m_parameters.m_weakSyncTargets.count() == 1 &&
1411             d->m_parameters.m_weakSyncTargets.first() == MangleAllSyncTargets);
1412 }
1413
1414 QString
1415 QContactTrackerEngine::gcQueryId() const
1416 {
1417     return d->m_gcQueryId;
1418 }
1419
1420 // TODO: we could cache the return value in this function
1421 QString
1422 QContactTrackerEngine::cleanupQueryString() const
1423 {
1424     // Notice FILTER in WHERE clause - garbage collector needs to verify parenting outside this graph too.
1425     // reason - IMAccount hasIMAddress in contactsd private graph so no need to delete it. The same could apply
1426     // to any other property - it could have been added to parent in some other graph.
1427     // Camera geotagging creates addresses outside qct graph and have no "parent" contact pointing
1428     // to them through nco:hasAddress
1429     static const QString obsoleteResourcesPrefix = QLatin1String
1430             ("\n"
1431              "DELETE\n"
1432              "{\n"
1433              "  GRAPH <%1>\n"
1434              "  {\n"
1435              "    ?resource a <%2> .\n"
1436              "  }\n"
1437              "}\n"
1438              "WHERE\n"
1439              "{\n"
1440              "  GRAPH <%1>\n"
1441              "  {\n"
1442              "    ?resource a <%3> .\n"
1443              "  }\n");
1444     static const QString imAccountStopPattern = QLatin1String
1445             ("    FILTER(NOT EXISTS { ?resource a nco:IMAccount }) .\n");
1446     static const QString obsoleteResourcesPattern = QLatin1String
1447             ("    FILTER(NOT EXISTS { ?parent <%1> ?resource }) .\n");
1448     static const QString obsoleteResourcesSuffix = QLatin1String
1449             ("}\n");
1450
1451     QString queryString;
1452
1453 #ifndef QT_NO_DEBUG
1454     queryString += QLatin1String
1455             ("\n"
1456              "#--------------------------------------.\n"
1457              "# Drop drop obsolete custom properties |\n"
1458              "#--------------------------------------'\n");
1459 #endif // QT_NO_DEBUG
1460
1461     // collect obsolete resource types and their predicates
1462     typedef QMap<QString, bool> PredicateMap;
1463     typedef QMap<QString, PredicateMap> ResourceTypePredicateMap;
1464     ResourceTypePredicateMap obsoleteResources;
1465
1466     foreach(const QTrackerContactDetailSchema &schema, schemas()) {
1467         foreach(const QTrackerContactDetail &detail, schema.details()) {
1468             foreach(const PropertyInfoList &chain, detail.possessedChains()) {
1469                 const PropertyInfoBase &pi = chain.last();
1470
1471                 if (pi.rangeIri() == rdfs::Resource::iri()) {
1472                     qctWarn(QString::fromLatin1("Not cleaning up obsolete resources for %1 property"
1473                                                 "since the property's range is too generic (%2).").
1474                             arg(qctIriAlias(pi.iri()), qctIriAlias(pi.rangeIri())));
1475                     continue;
1476                 }
1477
1478                 obsoleteResources[pi.rangeIri()].insert(pi.iri(), true);
1479             }
1480         }
1481     }
1482
1483     // glue everything together
1484     queryString += obsoleteResourcesPrefix.arg(QtContactsTrackerDefaultGraphIri,
1485                                                nao::Property::iri(),
1486                                                nao::Property::iri());
1487     queryString += obsoleteResourcesPattern.arg(nao::hasProperty::iri());
1488     queryString += obsoleteResourcesSuffix;
1489
1490     for(ResourceTypePredicateMap::ConstIterator
1491         t = obsoleteResources.constBegin(); t != obsoleteResources.constEnd(); ++t) {
1492         queryString += obsoleteResourcesPrefix.arg(QtContactsTrackerDefaultGraphIri,
1493                                                    rdfs::Resource::iri(),
1494                                                    t.key());
1495
1496         if (nco::IMAddress::iri() == t.key()) {
1497             // FIXME: Remove this workaround for NB#206404 - Saving a contact using
1498             // qtcontacts-tracker causes nco:IMAccount to be removed.
1499             queryString += imAccountStopPattern;
1500         }
1501
1502         foreach(const QString &predicate, t.value().keys()) {
1503             queryString += obsoleteResourcesPattern.arg(predicate);
1504         }
1505
1506         queryString += obsoleteResourcesSuffix;
1507     }
1508
1509     return queryString;
1510 }
1511
1512 void
1513 QContactTrackerEngine::enqueueTask(QctTask *task)
1514 {
1515     d->m_queue->enqueue(task);
1516 }
1517
1518 ////////////////////////////////////////////////////////////////////////////////////////////////////
1519 // Asynchronous data access methods
1520 ////////////////////////////////////////////////////////////////////////////////////////////////////
1521
1522 bool
1523 QContactTrackerEngine::checkSecurityTokens(QContactAbstractRequest *request)
1524 {
1525     // Plugin users often fail to provide all required security tokens.
1526     // Therefore we print warnings to educate them. If the security checks
1527     // should cause trouble they can be temporarly disabled by exporting
1528     // QT_CONTACTS_TRACKER="debug=no-sec".
1529
1530     if (d->m_mandatoryTokensFound) {
1531         return true;
1532     }
1533
1534     QStringList theGateKeepersBlameList;
1535     QStringList missingOptionalTokens;
1536
1537 #ifdef ENABLE_CREDENTIALS
1538
1539     static const QStringList requiredSecurityTokens =
1540             QStringList() << QLatin1String("TrackerReadAccess")
1541                           << QLatin1String("TrackerWriteAccess");
1542
1543     static const QStringList optionalSecurityTokens =
1544             QStringList() << QLatin1String("GRP::metadata-users");
1545
1546     foreach(const QString &credential, requiredSecurityTokens) {
1547         QString errorMessage;
1548
1549         if (not MssfQt::CredentialsManager::hasProcessCredential(0, credential, &errorMessage)) {
1550             theGateKeepersBlameList += credential;
1551             qctWarn(errorMessage);
1552         }
1553     }
1554
1555     foreach(const QString &credential, optionalSecurityTokens) {
1556         QString errorMessage;
1557
1558         if (not MssfQt::CredentialsManager::hasProcessCredential(0, credential, &errorMessage)) {
1559             missingOptionalTokens += credential;
1560             qctWarn(errorMessage);
1561         }
1562     }
1563
1564 #endif // ENABLE_CREDENTIALS
1565
1566     if (not theGateKeepersBlameList.isEmpty()) {
1567         qctWarn(QString::fromLatin1("\n"
1568                  "=============================================================================\n"
1569                  "WARNING /!\\ - MANDATORY SECURITY TOKENS ARE MISSING\n"
1570                  "=============================================================================\n"
1571                  "Rejecting %2.\n"
1572                  "Please add an AEGIS manifest to your package requesting the following\n"
1573                  "security tokens: %1 for %3 [%4].\n"
1574                  "=============================================================================").
1575                 arg(theGateKeepersBlameList.join(QLatin1String(", ")),
1576                     QLatin1String(request->metaObject()->className()),
1577                     QCoreApplication::applicationFilePath(),
1578                     QString::number(QCoreApplication::applicationPid())));
1579
1580         return false;
1581     }
1582
1583     if (not missingOptionalTokens.isEmpty()) {
1584         qctWarn(QString::fromLatin1("\n"
1585                  "=============================================================================\n"
1586                  "WARNING /!\\ - OPTIONAL SECURITY TOKENS ARE MISSING\n"
1587                  "=============================================================================\n"
1588                  "Full functionality like tracker direct access is not available.\n"
1589                  "Please add an AEGIS manifest to your package requesting the following\n"
1590                  "security tokens: %1 for %2 [%3].\n"
1591                  "=============================================================================").
1592                 arg(missingOptionalTokens.join(QLatin1String(", ")),
1593                     QCoreApplication::applicationFilePath(),
1594                     QString::number(QCoreApplication::applicationPid())));
1595     }
1596
1597     d->m_mandatoryTokensFound = true;
1598
1599     return true;
1600 }
1601
1602 QctTask *
1603 QContactTrackerEngine::startRequestImpl(QContactAbstractRequest *request)
1604 {
1605     QCT_SYNCHRONIZED(&d->m_requestLifeGuard);
1606
1607     QWeakPointer<QContactAbstractRequest> guardedRequest = request;
1608
1609     // The worker puts the request under protection of the engine's request life-guard.
1610     QScopedPointer<QTrackerAbstractRequest> worker(createRequestWorker(guardedRequest.data()));
1611
1612     if (worker.isNull()) {
1613         return 0;
1614     }
1615
1616     QScopedPointer<QctRequestTask> task(new QctRequestTask(guardedRequest.data(), worker.take()));
1617
1618     // Check if some evil client managed to delete the request from some other thread.
1619     if (guardedRequest.isNull()) {
1620         return 0;
1621     }
1622
1623     // Make sure mandatory tracker:ids have been resolved.
1624     if (task->dependencies().testFlag(QTrackerAbstractRequest::ResourceCache) &&
1625         not d->m_satisfiedDependencies.testFlag(QTrackerAbstractRequest::ResourceCache)) {
1626         // Queue will take ownership of the task
1627         // We don't explicitly parent the taks when creating them, because this method
1628         // might be called from a thread which is not the one where the engine was created.
1629         // In that case, we would get the "QObject: Cannot create children for a parent that
1630         // is in a different thread" warning, and the parent would be NULL anyway...
1631         enqueueTask(new QctResourceCacheTask(schemas()));
1632         d->m_satisfiedDependencies |= QTrackerAbstractRequest::ResourceCache;
1633     }
1634
1635     // Make sure GUID algorithms are available.
1636     if (task->dependencies().testFlag(QTrackerAbstractRequest::GuidAlgorithm) &&
1637         not d->m_satisfiedDependencies.testFlag(QTrackerAbstractRequest::GuidAlgorithm)) {
1638         // Queue will take ownership of the task
1639         // See above why we don't set the parent here
1640         enqueueTask(new QctGuidAlgorithmTask(this));
1641         d->m_satisfiedDependencies |= QTrackerAbstractRequest::GuidAlgorithm;
1642     }
1643
1644     // The contacts engine must bring requests into activate state as soon as it is dealing
1645     // with them. We cannot queue the task first, since the queue might be empty right now,
1646     // causing the task to start work from inactive state. This would be ridicilous.
1647     updateRequestState(guardedRequest.data(), QContactAbstractRequest::ActiveState);
1648
1649     // Check if some evil client managed to delete the request from some slot (or thread).
1650     if (guardedRequest.isNull()) {
1651         return 0;
1652     }
1653
1654     // Transfer task ownership to the queue.
1655     QctRequestTask *const result = task.data();
1656     d->m_queue->enqueue(task.take());
1657     return result;
1658 }
1659
1660 bool
1661 QContactTrackerEngine::startRequest(QContactAbstractRequest *request)
1662 {
1663     startRequestImpl(request);
1664     return true; // this always works
1665 }
1666
1667 QTrackerAbstractRequest *
1668 QContactTrackerEngine::createRequestWorker(QContactAbstractRequest *request)
1669 {
1670     // about if mandatory security tokens are missing
1671     if (not checkSecurityTokens(request)) {
1672         return 0;
1673     }
1674
1675     // ensure old worker got destroyed when request gets reused
1676     requestDestroyed(request);
1677
1678     QTrackerAbstractRequest *worker = 0;
1679     QElapsedTimer t; t.start();
1680
1681     // choose proper request implementation
1682     switch(request->type())
1683     {
1684     case QContactAbstractRequest::ContactFetchRequest:
1685         worker = new QTrackerContactFetchRequest(request, this);
1686         break;
1687
1688     case QContactAbstractRequest::ContactFetchByIdRequest:
1689         worker = new QTrackerContactFetchByIdRequest(request, this);
1690         break;
1691
1692     case QContactAbstractRequest::ContactLocalIdFetchRequest:
1693         worker = new QTrackerContactIdFetchRequest(request, this);
1694         break;
1695
1696     case QContactAbstractRequest::ContactRemoveRequest:
1697         if (0 == qobject_cast<QctContactMergeRequest *>(request)) {
1698             worker = new QTrackerContactRemoveRequest(request, this);
1699         } else {
1700             worker = new QTrackerContactCopyAndRemoveRequest(request, this);
1701         }
1702         break;
1703
1704     case QContactAbstractRequest::ContactSaveRequest:
1705         if (0 == qobject_cast< QctUnmergeIMContactsRequest *> (request)) {
1706             worker = new QTrackerContactSaveRequest(request, this);
1707         } else {
1708             worker = new QTrackerContactSaveOrUnmergeRequest(request, this);
1709         }
1710         break;
1711
1712     case QContactAbstractRequest::RelationshipFetchRequest:
1713         worker = new QTrackerRelationshipFetchRequest(request, this);
1714         break;
1715
1716     case QContactAbstractRequest::RelationshipRemoveRequest:
1717         worker = new QTrackerRelationshipRemoveRequest(request, this);
1718         break;
1719
1720     case QContactAbstractRequest::RelationshipSaveRequest:
1721         worker = new QTrackerRelationshipSaveRequest(request, this);
1722         break;
1723
1724     case QContactAbstractRequest::DetailDefinitionFetchRequest:
1725         worker = new QTrackerDetailDefinitionFetchRequest(request, this);
1726         break;
1727
1728     case QContactAbstractRequest::InvalidRequest:
1729     case QContactAbstractRequest::DetailDefinitionRemoveRequest:
1730     case QContactAbstractRequest::DetailDefinitionSaveRequest:
1731         break;
1732     }
1733
1734     if (0 == worker) {
1735         qctWarn(QString::fromLatin1("Unsupported request type: %1").
1736                 arg(QLatin1String(request->metaObject()->className())));
1737         return 0;
1738     }
1739
1740     if (hasDebugFlag(ShowNotes)) {
1741         qDebug() << Q_FUNC_INFO << "time elapsed while constructing request workers:"<< request << t.elapsed();
1742     }
1743
1744     if (hasDebugFlag(ShowNotes)) {
1745         qDebug() << Q_FUNC_INFO << "running" << worker->metaObject()->className();
1746     }
1747
1748     // XXX The unit tests directly access the engine. Therefore requests created by our unit
1749     // tests don't have a manager attached. This prevents ~QContactAbstractRequest() to call our
1750     // engine's requestDestroyed(QContactAbstractRequest*) method, which results in memory leaks
1751     // within our our unit tests. To prevent those leaks we watch the request's destroyed()
1752     // signal and forward those signals to requestDestroyed(QContactAbstractRequest*) when
1753     // a request without contact manager is found.
1754     //
1755     // XXX This all of course is an ugly workaround. We probably should change the unit tests
1756     // to create use QContactManager accessing the engine via a static plugin.
1757     if (0 == request->manager()) {
1758         connect(request, SIGNAL(destroyed(QObject*)),
1759                 this, SLOT(onRequestDestroyed(QObject*)), Qt::DirectConnection);
1760     }
1761
1762     // track original request for this worker
1763     QCT_SYNCHRONIZED_WRITE(&d->m_tableLock);
1764
1765     d->m_workersByRequest.insert(request, worker);
1766     d->m_requestsByWorker.insert(worker, request);
1767
1768     return worker;
1769 }
1770
1771 QctRequestLocker
1772 QContactTrackerEngine::request(const QTrackerAbstractRequest *worker) const
1773 {
1774     QScopedPointer<QMutexLocker> locker(new QMutexLocker(&d->m_requestLifeGuard));
1775     QCT_SYNCHRONIZED_READ(&d->m_tableLock);
1776     return QctRequestLocker(locker.take(), d->m_requestsByWorker.value(worker));
1777 }
1778
1779 void
1780 QContactTrackerEngine::connectSignals()
1781 {
1782     if (d->m_changeListener) {
1783         connect(d->m_changeListener,
1784                 SIGNAL(contactsAdded(QList<QContactLocalId>)),
1785                 SIGNAL(contactsAdded(QList<QContactLocalId>)));
1786         connect(d->m_changeListener,
1787                 SIGNAL(contactsChanged(QList<QContactLocalId>)),
1788                 SIGNAL(contactsChanged(QList<QContactLocalId>)));
1789         connect(d->m_changeListener,
1790                 SIGNAL(contactsRemoved(QList<QContactLocalId>)),
1791                 SIGNAL(contactsRemoved(QList<QContactLocalId>)));
1792         connect(d->m_changeListener,
1793                 SIGNAL(relationshipsAdded(QList<QContactLocalId>)),
1794                 SIGNAL(relationshipsAdded(QList<QContactLocalId>)));
1795         connect(d->m_changeListener,
1796                 SIGNAL(relationshipsRemoved(QList<QContactLocalId>)),
1797                 SIGNAL(relationshipsRemoved(QList<QContactLocalId>)));
1798     }
1799 }
1800
1801 void
1802 QContactTrackerEngine::disconnectSignals()
1803 {
1804     if (d->m_changeListener) {
1805         d->m_changeListener->disconnect(this);
1806         d->m_changeListener = 0;
1807     }
1808 }
1809
1810 void
1811 QContactTrackerEngine::registerGcQuery()
1812 {
1813     d->m_gcQueryId = QString::fromLatin1("com.nokia.qtcontacts-tracker");
1814     QctGarbageCollector::registerQuery(d->m_gcQueryId, cleanupQueryString());
1815 }
1816
1817 void
1818 QContactTrackerEngine::connectNotify(const char *signal)
1819 {
1820     if (0 == d->m_changeListener) {
1821         // Create the change listener on demand since:
1822         //
1823         // 1) Creating the listener is expensive as we must subscribe to some DBus signals.
1824         // 2) Watching DBus without any specific need wastes energy by wakeing up processes.
1825         //
1826         // Share the change listener for the reasons listed above.
1827         //
1828         // Still only share per thread (instead of process) to avoid nasty situations like the
1829         // random, listener owning thread terminating before other threads using the listener.
1830
1831         // Must monitor individual contact classes instead of nco:Contact
1832         // to avoid bogus notifications for:
1833         //
1834         //  - QContactOrganization details, implemented via nco:OrganizationContact
1835         //  - incoming calls which cause call-ui to create temporary(?) nco:Contact instances
1836         //
1837         // Additionally, nco:Contact does not have the tracker:notify property set to true, whereas
1838         // nco:PersonContact and nco:ContactGroup do have the property, so only those classes will
1839         // receive notifications of changes.
1840         QSet<QString> contactClassIris;
1841
1842         foreach (const QTrackerContactDetailSchema &schema, d->m_parameters.m_detailSchemas) {
1843             foreach(const QString &iri, schema.contactClassIris()) {
1844                 if (iri != nco::Contact::iri()) {
1845                     contactClassIris += iri;
1846                 }
1847             }
1848         }
1849
1850         // Compute change filtering mode.
1851         QctTrackerChangeListener::ChangeFilterMode changeFilterMode = QctTrackerChangeListener::AllChanges;
1852
1853         if (d->m_parameters.m_omitPresenceChanges) {
1854             changeFilterMode = QctTrackerChangeListener::IgnorePresenceChanges;
1855         }
1856
1857         // Compute debug flags for the listener.
1858         QctTrackerChangeListener::DebugFlags debugFlags = QctTrackerChangeListener::NoDebug;
1859
1860         if (d->m_parameters.m_debugFlags.testFlag(QContactTrackerEngine::ShowSignals)) {
1861             debugFlags |= QctTrackerChangeListener::PrintSignals;
1862         }
1863
1864         // Compute cache id for the listener.
1865         // Cannot use the manager URI because it doesn't expose all relevant parameters.
1866         const QString listenerId = (QStringList(contactClassIris.toList()) <<
1867                                     QString::number(d->m_parameters.m_coalescingDelay) <<
1868                                     QString::number(changeFilterMode) <<
1869                                     QString::number(debugFlags)).join(QLatin1String(";"));
1870
1871         // Check if there already exists a listener for that computed id.
1872         QctThreadLocalData *const threadLocalData = QctThreadLocalData::instance();
1873         d->m_changeListener = threadLocalData->trackerChangeListener(listenerId);
1874
1875         if (0 == d->m_changeListener) {
1876             // Create new listener when needed.
1877             d->m_changeListener = new QctTrackerChangeListener(contactClassIris.toList(), QThread::currentThread());
1878             d->m_changeListener->setCoalescingDelay(d->m_parameters.m_coalescingDelay);
1879             d->m_changeListener->setChangeFilterMode(changeFilterMode);
1880             d->m_changeListener->setDebugFlags(debugFlags);
1881
1882             threadLocalData->setTrackerChangeListener(listenerId, d->m_changeListener);
1883         }
1884
1885         // Monitor the choosen listener's signals.
1886         connectSignals();
1887     }
1888
1889     QContactManagerEngine::connectNotify(signal);
1890 }
1891
1892 void
1893 QContactTrackerEngine::requestDestroyed(QContactAbstractRequest *req)
1894 {
1895     dropRequest(QctRequestLocker(new QMutexLocker(&d->m_requestLifeGuard), req));
1896 }
1897
1898 void
1899 QContactTrackerEngine::dropRequest(const QctRequestLocker &req)
1900 {
1901     QCT_SYNCHRONIZED_WRITE(&d->m_tableLock);
1902
1903     if (req.isNull()) {
1904         return;
1905     }
1906
1907     QTrackerAbstractRequest *const worker = d->m_workersByRequest.take(req.data());
1908
1909     if (0 != worker) {
1910         d->m_requestsByWorker.remove(worker);
1911     }
1912 }
1913
1914 void
1915 QContactTrackerEngine::onRequestDestroyed(QObject *req)
1916 {
1917     // dynamic_cast<> does not work at this point (has a 0 result, or even crashes) because the
1918     // derived parts of the class have already been destroyed by the time the base QObject's
1919     // destructor has emitted this signal. So we do a regular static case, because
1920     // requestDestroyed(QContactAbstractRequest*) just compares the pointer value anyway.
1921     requestDestroyed(static_cast<QContactAbstractRequest *>(req));
1922 }
1923
1924 bool
1925 QContactTrackerEngine::waitForRequestFinished(QContactAbstractRequest *request, int msecs)
1926 {
1927     if (not hasDebugFlag(SkipNagging)) {
1928         qctWarn(QString::fromLatin1("\n"
1929             "=============================================================================\n"
1930             "WARNING /!\\ - NEVER EVER CALL THIS FUNCTION FROM PRODUCTION CODE!!!\n"
1931             "=============================================================================\n"
1932             "QContactAbstractRequest::waitForFinished(), respectively\n"
1933             "QContactManagerEngine::waitForRequestFinished() must spin your\n"
1934             "application's event loop. Doing so will cause HAVOC AND PAIN for\n"
1935             "any non-trivial program.\n"
1936             "\n"
1937             "So please refactor your asynchronious code, or use the synchronious\n"
1938             "API if blocking your application is acceptable.\n"
1939             "\n"
1940             "WE MEAN IT!!!\n"
1941             "\n"
1942             "Offending application is %1 [%2].\n"
1943             "=============================================================================").
1944             arg(QCoreApplication::applicationFilePath(),
1945                 QString::number(QCoreApplication::applicationPid())));
1946     }
1947
1948     return waitForRequestFinishedImpl(request, msecs);
1949 }
1950
1951 bool
1952 QContactTrackerEngine::waitForRequestFinishedImpl(QContactAbstractRequest *request, int msecs)
1953 {
1954     if (0 == request) {
1955         return false;
1956     }
1957
1958     RequestEventLoop eventLoop(request, msecs);
1959
1960     if (not eventLoop.isFinished()) {
1961         eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
1962     }
1963
1964     return eventLoop.isFinished();
1965 }
1966
1967 bool
1968 QContactTrackerEngine::runSyncRequest(QContactAbstractRequest *request,
1969                                       QContactManager::Error *error) const
1970 {
1971     // Copy the engine to prevent forwarding side-effects to the caller's engine.
1972     // It costs a bit, but guess that's a justified penalty to all sync API users.
1973     QContactTrackerEngine taskEngine(*this);
1974
1975     QctTask *const task = taskEngine.startRequestImpl(request);
1976
1977     if (0 != task && not QctTaskWaiter(task).wait(requestTimeout())) {
1978         qctPropagate(QContactManager::UnspecifiedError, error);
1979         return false;
1980     }
1981
1982     qctPropagate(request->error(), error);
1983
1984     return true;
1985 }
1986
1987 ////////////////////////////////////////////////////////////////////////////////////////////////////
1988 // Synthesized contact details
1989 ////////////////////////////////////////////////////////////////////////////////////////////////////
1990
1991 QString
1992 QContactTrackerEngine::synthesizedDisplayLabel(const QContact& contact,
1993                                                QContactManager::Error* error) const
1994 {
1995     qctPropagate(QContactManager::NoError, error);
1996
1997     return createDisplayLabel(contact);
1998 }
1999
2000 QContact
2001 QContactTrackerEngine::compatibleContact(const QContact &original, QContactManager::Error* error) const
2002 {
2003     QContact contact = original;
2004
2005     foreach(const QTrackerContactDetail &detail, schema(contact.type()).details()) {
2006         QList<QContactDetail> contactDetails = contact.details(detail.name());
2007
2008         if (contactDetails.empty()) {
2009             continue;
2010         }
2011
2012         // Check that detail respects our schema's cardinality
2013         if (not detail.isUnique()) {
2014             continue;
2015         }
2016
2017         if (contactDetails.count() < 2) {
2018             continue;
2019         }
2020
2021         qctWarn(QString::fromLatin1("Dropping odd details: %2 detail must be unique").
2022                 arg(detail.name()));
2023
2024         for(int i = 1; i < contactDetails.count(); ++i) {
2025             contact.removeDetail(&contactDetails[i]);
2026         }
2027     }
2028
2029     // Check fields contents
2030     foreach(const QTrackerContactDetail &detail, schema(contact.type()).details()) {
2031         QList<QContactDetail> contactDetails = contact.details(detail.name());
2032
2033         foreach (QContactDetail contactDetail, contactDetails) {
2034             const QVariantMap detailValues = contactDetail.variantValues();
2035
2036             for (QVariantMap::ConstIterator it = detailValues.constBegin();
2037                  it != detailValues.constEnd(); ++it) {
2038                 const QTrackerContactDetailField *field = detail.field(it.key());
2039
2040                 if (field == 0) {
2041                     // We can't validate custom fields, skip
2042                     continue;
2043                 }
2044
2045                 QVariant computedValue;
2046
2047                 if (not field->makeValue(it.value(), computedValue)) {
2048                     // Apparently this is not an error, we just prune the field...
2049                     // makeValue already prints a debug message if it returns false,
2050                     // so we can stay silent here. No idea how UI can provide any
2051                     // useful feedback out of that.
2052                     contactDetail.removeValue(it.key());
2053                     break;
2054                 }
2055
2056                 // The computed value is only useful to generate SPARQL queries,
2057                 // we should not save it back to the field.
2058             }
2059
2060             contact.saveDetail(&contactDetail);
2061         }
2062     }
2063
2064     qctPropagate(QContactManager::NoError, error);
2065
2066     return contact;
2067 }
2068
2069 ////////////////////////////////////////////////////////////////////////////////////////////////////
2070 // Unsupported functions
2071 ////////////////////////////////////////////////////////////////////////////////////////////////////
2072
2073 bool
2074 QContactTrackerEngine::validateContact(const QContact&, QContactManager::Error* error) const
2075 {
2076     REPORT_UNSUPPORTED_FUNCTION(error);
2077     return false;
2078 }
2079
2080 bool
2081 QContactTrackerEngine::validateDefinition(const QContactDetailDefinition&,
2082                                           QContactManager::Error* error) const
2083 {
2084     REPORT_UNSUPPORTED_FUNCTION(error);
2085     return false;
2086 }
2087
2088 bool
2089 QContactTrackerEngine::saveDetailDefinition(const QContactDetailDefinition&, const QString&,
2090                                             QContactManager::Error* error)
2091 {
2092     REPORT_UNSUPPORTED_FUNCTION(error);
2093     return false;
2094 }
2095
2096 bool
2097 QContactTrackerEngine::removeDetailDefinition(const QString&, const QString&,
2098                                               QContactManager::Error* error)
2099 {
2100     REPORT_UNSUPPORTED_FUNCTION(error);
2101     return false;
2102 }
2103
2104 bool
2105 QContactTrackerEngine::cancelRequest(QContactAbstractRequest *request)
2106 {
2107     if (0 == request) {
2108         return false;
2109     }
2110
2111     QCT_SYNCHRONIZED(&d->m_requestLifeGuard);
2112     QCT_SYNCHRONIZED_READ(&d->m_tableLock);
2113
2114     QTrackerAbstractRequest *const worker = d->m_workersByRequest.value(request);
2115
2116     if (0 != worker) {
2117         return worker->cancel();
2118     }
2119     return false;
2120 }
2121
2122 bool
2123 QContactTrackerEngine::isRelationshipTypeSupported(const QString& relationshipType,
2124                                                    const QString& contactType) const
2125 {
2126     if (relationshipType == QContactRelationship::HasMember
2127             && supportedContactTypes().contains(contactType)
2128             && (contactType == QContactType::TypeContact
2129                 || contactType == QContactType::TypeGroup)) {
2130         return true;
2131     }
2132
2133     return false;
2134 }