Changes: Add QctTaskWaiter to wait for a task to finish
[qtcontacts-tracker:qtcontacts-tracker.git] / src / engine / engine.cpp
1 /** This file is part of QtContacts tracker storage plugin
2  **
3  ** Copyright (c) 2009-2011 Nokia Corporation and/or its subsidiary(-ies).
4  **
5  ** Contact:  Nokia Corporation (info@qt.nokia.com)
6  **
7  ** GNU Lesser General Public License Usage
8  ** This file may be used under the terms of the GNU Lesser General Public License
9  ** version 2.1 as published by the Free Software Foundation and appearing in the
10  ** file LICENSE.LGPL included in the packaging of this file.  Please review the
11  ** following information to ensure the GNU Lesser General Public License version
12  ** 2.1 requirements will be met:
13  ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
14  **
15  ** In addition, as a special exception, Nokia gives you certain additional rights.
16  ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included
17  ** in the file LGPL_EXCEPTION.txt in this package.
18  **
19  ** Other Usage
20  ** Alternatively, this file may be used in accordance with the terms and
21  ** conditions contained in a signed written agreement between you and Nokia.
22  **/
23
24 #include "engine.h"
25 #include "engine_p.h"
26
27 #include "contactfetchrequest.h"
28 #include "contactidfetchrequest.h"
29 #include "contactcopyandremoverequest.h"
30 #include "contactunmergerequest.h"
31 #include "queue.h"
32 #include "relationshipfetchrequest.h"
33 #include "relationshipremoverequest.h"
34 #include "relationshipsaverequest.h"
35 #include "detaildefinitionfetchrequest.h"
36 #include "trackerchangelistener.h"
37 #include "threadmanager.h"
38 #include "requesttask.h"
39 #include "tasks.h"
40
41 #include <dao/contactdetail.h>
42 #include <dao/contactdetailschema.h>
43 #include <dao/scalarquerybuilder.h>
44 #include <dao/scalarquerybuilder.h>
45 #include <dao/sparqlresolver.h>
46 #include <dao/support.h>
47
48 #include <lib/contactmergerequest.h>
49 #include <lib/customdetails.h>
50 #include <lib/settings.h>
51 #include <lib/threadutils.h>
52 #include <lib/unmergeimcontactsrequest.h>
53
54 #include <qcontactdetails.h>
55
56 #include <ontologies/nco.h>
57 #include <QElapsedTimer>
58
59 #ifdef ENABLE_CREDENTIALS
60 #include <CredentialsManager>
61 #endif // ENABLE_CREDENTIALS
62
63 ////////////////////////////////////////////////////////////////////////////////////////////////////
64
65 CUBI_USE_NAMESPACE_RESOURCES
66 QTM_USE_NAMESPACE
67
68 ////////////////////////////////////////////////////////////////////////////////////////////////////
69
70 #define REPORT_UNSUPPORTED_FUNCTION(error) do {\
71     qctWarn(QString::fromLatin1("Method not implemented yet: %1"). \
72             arg(QLatin1String(Q_FUNC_INFO))); \
73     qctPropagate(QContactManager::NotSupportedError, error); \
74 } while(0)
75
76 ////////////////////////////////////////////////////////////////////////////////////////////////////
77
78 static QContactManager::Error *const noErrorResult = 0;
79 const QString QContactTrackerEngine::DefaultGraphIri = QLatin1String("urn:uuid:08070f5c-a334-4d19-a8b0-12a3071bfab9");
80 const QString QContactTrackerEngine::DefaultSyncTarget = QContactSyncTarget__SyncTargetAddressBook;
81 const QStringList QContactTrackerEngine::DefaultWeakSyncTargets = QStringList() << QContactSyncTarget__SyncTargetTelepathy;
82 const QString QContactTrackerEngine::MangleAllSyncTargets = QLatin1String("all");
83
84 ////////////////////////////////////////////////////////////////////////////////////////////////////
85
86 // added here as list was not working because of
87 // [warn ] QObject::connect: Cannot queue arguments of type 'QContactAbstractRequest::State'
88 // (Make sure 'QContactAbstractRequest::State' is registered using qRegisterMetaType().)
89 Q_DECLARE_METATYPE(QContactAbstractRequest::State)
90
91 ////////////////////////////////////////////////////////////////////////////////////////////////////
92
93 static QMap<QString, QString>
94 readEnvironment()
95 {
96     const QStringList tokenList(QProcessEnvironment::systemEnvironment().
97                                 value(QLatin1String("QT_CONTACTS_TRACKER")).
98                                 replace(QLatin1Char(' '), QString()).
99                                 split(QLatin1String(";")));
100
101     QMap<QString, QString> parameters;
102
103     foreach(const QString &token, tokenList) {
104         const int eq = token.indexOf(QLatin1Char('='));
105         const QString key((eq >= 0 ? token.left(eq) : token).trimmed());
106         const QString value((eq >= 0 ? token.mid(eq + 1).trimmed() : QString()));
107
108         if (not key.isEmpty()) {
109             parameters.insert(key, value);
110         }
111     }
112
113     return parameters;
114 }
115
116 static void
117 parseParameter(int &result, const QString &key, const QString &text)
118 {
119     bool ok = false;
120     const int value = text.toInt(&ok);
121
122     if (ok) {
123         result = value;
124     } else {
125         qctWarn(QString::fromLatin1("Invalid value for %1 argument: %2").arg(key, text));
126     }
127 }
128
129 QContactTrackerEngineParameters::QContactTrackerEngineParameters(const QMap<QString, QString> &parameters,
130                                                                  const QString& engineName, int engineVersion)
131     : m_engineName(engineName)
132     , m_engineVersion(engineVersion)
133     , m_requestTimeout(QContactTrackerEngine::DefaultRequestTimeout)
134     , m_trackerTimeout(QContactTrackerEngine::DefaultTrackerTimeout)
135     , m_coalescingDelay(QContactTrackerEngine::DefaultCoalescingDelay)
136     , m_concurrencyLevel(QctSettings().concurrencyLevel())
137     , m_batchSize(QContactTrackerEngine::DefaultBatchSize)
138     , m_graphIri(QContactTrackerEngine::DefaultGraphIri)
139     , m_syncTarget(QContactTrackerEngine::DefaultSyncTarget)
140     , m_weakSyncTargets(QContactTrackerEngine::DefaultWeakSyncTargets)
141     , m_guidAlgorithm(0)
142     , m_parameters(parameters)
143     , m_debugFlags(0)
144 {
145     // parse parameters
146     static const QStringList managerParameterNames = QStringList() << QLatin1String("graph-iri");
147     static const QMap<QString, QString> environmentParameters = readEnvironment();
148     m_parameters.unite(environmentParameters);
149
150     QString guidAlgorithmName = QctSettings().guidAlgorithmName();
151     QStringList contactTypes;
152
153     for(QMap<QString, QString>::ConstIterator i = m_parameters.constBegin(); i != m_parameters.constEnd(); ++i) {
154         if (managerParameterNames.contains(i.key())) {
155             m_managerParameters.insert(i.key(), i.value());
156         }
157
158         if (QLatin1String("timeout") == i.key()) {
159             parseParameter(m_requestTimeout, i.key(), i.value());
160             continue;
161         }
162
163         if (QLatin1String("tracker-timeout") == i.key()) {
164             parseParameter(m_trackerTimeout, i.key(), i.value());
165             continue;
166         }
167
168         if (QLatin1String("coalescing-delay") == i.key()) {
169             parseParameter(m_coalescingDelay, i.key(), i.value());
170             continue;
171         }
172
173         if (QLatin1String("writeback") == i.key()) {
174             const QSet<QString> values = i.value().split(QLatin1Char(',')).toSet();
175             const bool all(values.contains(QLatin1String("all")));
176
177             if (all || values.contains(QLatin1String("presence"))) {
178                 foreach(QTrackerContactDetailSchema schema, m_detailSchemas) {
179                     schema.setWriteBackPresence(true);
180                 }
181             }
182
183             continue;
184         }
185
186         if (QLatin1String("debug") == i.key()) {
187             const QSet<QString> values = i.value().split(QLatin1Char(',')).toSet();
188             const bool all(values.contains(QLatin1String("all")));
189
190             if (all || values.contains(QLatin1String("queries"))) {
191                 m_debugFlags |= QContactTrackerEngine::ShowSelects;
192                 m_debugFlags |= QContactTrackerEngine::ShowUpdates;
193             }
194             if (all || values.contains(QLatin1String("selects"))) {
195                 m_debugFlags |= QContactTrackerEngine::ShowSelects;
196             }
197             if (all || values.contains(QLatin1String("updates"))) {
198                 m_debugFlags |= QContactTrackerEngine::ShowUpdates;
199             }
200             if (all || values.contains(QLatin1String("models"))) {
201                 m_debugFlags |= QContactTrackerEngine::ShowModels;
202             }
203             if (all || values.contains(QLatin1String("notes"))) {
204                 m_debugFlags |= QContactTrackerEngine::ShowNotes;
205             }
206             if (all || values.contains(QLatin1String("timing"))) {
207                 m_debugFlags |= QContactTrackerEngine::ShowTiming;
208             }
209             if (all || values.contains(QLatin1String("signals"))) {
210                 m_debugFlags |= QContactTrackerEngine::ShowSignals;
211             }
212             if (all || values.contains(QLatin1String("no-sec"))) {
213                 m_debugFlags |= QContactTrackerEngine::SkipSecurityChecks;
214             }
215
216             continue;
217         }
218
219         if (QLatin1String("contact-types") == i.key()) {
220             contactTypes = i.value().split(QLatin1Char(','));
221             continue;
222         }
223
224         if (QLatin1String("concurrency") == i.key()) {
225             if ((m_concurrencyLevel = i.value().toInt()) < 1) {
226                 m_concurrencyLevel = QctSettings().concurrencyLevel();
227             }
228
229             continue;
230         }
231
232         if (QLatin1String("batch-size") == i.key()) {
233             if ((m_batchSize = i.value().toInt()) < 1) {
234                 m_batchSize = QContactTrackerEngine::DefaultBatchSize;
235             }
236
237             continue;
238         }
239
240         if (QLatin1String("graph-iri") == i.key()) {
241             m_graphIri = i.value();
242             continue;
243         }
244
245         if (QLatin1String("guid-algorithm") == i.key()) {
246             guidAlgorithmName = i.value();
247             continue;
248         }
249
250         if (QLatin1String("default-sync-target") == i.key()) {
251             m_syncTarget = i.value();
252             continue;
253         }
254
255         if (QLatin1String("weak-sync-targets") == i.key()) {
256             m_weakSyncTargets = i.value().split(QLatin1String(","), QString::SkipEmptyParts);
257
258             if (m_weakSyncTargets.contains(QContactTrackerEngine::MangleAllSyncTargets)) {
259                 m_weakSyncTargets = QStringList(QContactTrackerEngine::MangleAllSyncTargets);
260             }
261
262             continue;
263         }
264
265         if (QLatin1String("com.nokia.qt.mobility.contacts.implementation.version") == i.key()) {
266             continue;
267         }
268
269         qctWarn(QString::fromLatin1("Unknown parameter: %1").arg(i.key()));
270     }
271
272     // setup detail schemas
273     if (contactTypes.isEmpty() || contactTypes.contains(QContactType::TypeContact, Qt::CaseInsensitive)) {
274         QTrackerPersonContactDetailSchema personContactSchema;
275         m_detailSchemas.insert(QContactType::TypeContact, personContactSchema);
276     }
277
278     if (contactTypes.isEmpty() || contactTypes.contains(QContactType::TypeGroup, Qt::CaseInsensitive)) {
279         QTrackerContactGroupDetailSchema contactGroupSchema;
280         m_detailSchemas.insert(QContactType::TypeGroup, contactGroupSchema);
281     }
282
283     // setup GUID algorithm
284     m_guidAlgorithm = QctGuidAlgorithmFactory::algorithm(guidAlgorithmName);
285
286     if (0 == m_guidAlgorithm) {
287         if (not guidAlgorithmName.isEmpty()) {
288             qctWarn(QString::fromLatin1("Unknown GUID algorithm: %1. Using default algorithm.").
289                     arg(guidAlgorithmName));
290         }
291
292         m_guidAlgorithm = QctGuidAlgorithmFactory::algorithm(QctGuidAlgorithm::Default);
293     }
294 }
295
296 ////////////////////////////////////////////////////////////////////////////////////////////////////
297
298 QContactTrackerEngineData::QContactTrackerEngineData(const QMap<QString, QString> &parameters,
299                                                      const QString &engineName, int engineVersion)
300     : QSharedData()
301     , m_parameters(parameters, engineName, engineVersion)
302     , m_selfContactId(0)
303     , m_changeListener(0) // create on demand
304     , m_requestLifeGuard(QMutex::Recursive)
305     , m_queue(new QctQueue)
306     , m_satisfiedDependencies(QTrackerAbstractRequest::NoDependencies)
307     , m_mandatoryTokensFound(false)
308 {
309     // disable security token checks if requested
310     if (m_parameters.m_debugFlags & QContactTrackerEngine::SkipSecurityChecks) {
311         m_mandatoryTokensFound = true;
312     }
313
314     setupDisplayNameFields();
315 }
316
317 QContactTrackerEngineData::QContactTrackerEngineData(const QContactTrackerEngineData& other)
318     : QSharedData(other)
319     , m_parameters(other.m_parameters)
320     , m_displayNameGeneratorListMap(other.m_displayNameGeneratorListMap)
321     , m_supportedDataTypes(other.m_supportedDataTypes)
322     , m_selfContactId(other.m_selfContactId)
323     , m_changeListener(0) // must create our own when needed
324     , m_requestLifeGuard(QMutex::Recursive)
325     , m_queue(new QctQueue) // the queue is per engine
326     , m_satisfiedDependencies(other.m_satisfiedDependencies)
327     , m_mandatoryTokensFound(other.m_mandatoryTokensFound)
328 {
329 }
330
331 QContactTrackerEngineData::~QContactTrackerEngineData()
332 {
333     delete m_queue;
334 }
335
336 static DisplayLabelGeneratorList
337 makeHighPriorityGeneratorList()
338 {
339     DisplayLabelGeneratorList list;
340     return list;
341 }
342
343 static DisplayLabelGeneratorList
344 makeLowPriorityGeneratorList()
345 {
346     DisplayLabelGeneratorList list;
347
348     list
349         // 1) If CustomLabel is set it dominates all other rules, but detailed name fields (NB#200059)
350         << DisplayLabelGenerator::simpleDetailFieldGenerator(QContactName::DefinitionName,
351                                                              QContactName::FieldCustomLabel)
352
353         // 2a) "any available name information" - nickname
354         << DisplayLabelGenerator::simpleDetailFieldGenerator(QContactNickname::DefinitionName,
355                                                              QContactNickname::FieldNickname)
356
357         // 2b) "any available name information" - middle name
358         << DisplayLabelGenerator::simpleDetailFieldGenerator(QContactName::DefinitionName,
359                                                              QContactName::FieldMiddleName)
360
361         // 3) The company name, e.g. "Nokia"
362         << DisplayLabelGenerator::simpleDetailFieldGenerator(QContactOrganization::DefinitionName,
363                                                              QContactOrganization::FieldName)
364
365         // 4a) "IM username", first the most available IM nickname (e.g "The Dude")
366         << DisplayLabelGenerator::simpleDetailFieldGenerator(QContactGlobalPresence::DefinitionName,
367                                                              QContactGlobalPresence::FieldNickname)
368
369         // 4b) IM address as desparate fallback, this could be a long incomprehensible URI.
370         //    Usually it should be something like "thedude@ovi.com".
371         << DisplayLabelGenerator::simpleDetailFieldGenerator(QContactOnlineAccount::DefinitionName,
372                                                              QContactOnlineAccount::FieldAccountUri)
373
374         // 5) The email address, e.g. "hans.wurst@nokia.com"
375         << DisplayLabelGenerator::simpleDetailFieldGenerator(QContactEmailAddress::DefinitionName,
376                                                              QContactEmailAddress::FieldEmailAddress)
377
378         // 6) The phone number, e.g. "+3581122334455
379         << DisplayLabelGenerator::simpleDetailFieldGenerator(QContactPhoneNumber::DefinitionName,
380                                                                 QContactPhoneNumber::FieldNumber)
381
382         // 7) The contact's homepage URL, e.g. "http://www.nokia.com/"
383         << DisplayLabelGenerator::simpleDetailFieldGenerator(QContactUrl::DefinitionName,
384                                                              QContactUrl::FieldUrl);
385
386     return list;
387 }
388
389 void
390 QContactTrackerEngineData::setupDisplayNameFields()
391 {
392     // TODO Get name display rules from contacts settings, described in UI Spec
393     // v1.1 (section 4.9).
394
395     if (m_displayNameGeneratorListMap.isEmpty()) {
396         const DisplayLabelGeneratorList highPriorityGenerators = makeHighPriorityGeneratorList();
397         const DisplayLabelGeneratorList lowPriorityGenerators = makeLowPriorityGeneratorList();
398
399         // variant with "FirstName LastName"
400         m_displayNameGeneratorListMap[QContactDisplayLabel__FieldOrderFirstName] =
401                 DisplayLabelGeneratorList() << highPriorityGenerators
402                 // First and last name, e.g. "Hans Wurst"
403                 << DisplayLabelGenerator::firstNameLastNameGenerator()
404                 << lowPriorityGenerators;
405
406         // variant with "LastName, FirstName"
407         m_displayNameGeneratorListMap[QContactDisplayLabel__FieldOrderLastName] =
408                 DisplayLabelGeneratorList() << highPriorityGenerators
409                 // Last and first name, e.g. "Wurst, Hans"
410                 << DisplayLabelGenerator::lastNameFirstNameGenerator()
411                 << lowPriorityGenerators;
412
413         // variant for no generators, thus no needed details.
414         // useful for unit tests and for optimizing fetch request performance.
415         m_displayNameGeneratorListMap[QContactDisplayLabel__FieldOrderNone] =
416                 DisplayLabelGeneratorList();
417     }
418 }
419
420 ///////////////////////////////////////////////////////////////////////////////
421 // Synchronous API helpers
422 ///////////////////////////////////////////////////////////////////////////////
423
424 RequestEventLoop::RequestEventLoop(QContactAbstractRequest *request, int timeout)
425     : m_finished(request->isFinished() || request->isCanceled())
426 {
427     connect(request, SIGNAL(stateChanged(QContactAbstractRequest::State)),
428             this, SLOT(stateChanged(QContactAbstractRequest::State)),
429             Qt::DirectConnection);
430
431     if (timeout > 0) {
432         QTimer::singleShot(timeout, this, SLOT(quit()));
433     }
434 }
435
436 void
437 RequestEventLoop::stateChanged(QContactAbstractRequest::State newState)
438 {
439     switch (newState) {
440     case QContactAbstractRequest::FinishedState:
441     case QContactAbstractRequest::CanceledState:
442         m_finished = true;
443         quit();
444         break;
445     default:
446         break;
447     }
448 }
449
450 ////////////////////////////////////////////////////////////////////////////////////////////////////
451
452 QctTaskWaiter::QctTaskWaiter(QctTask *task, QObject *parent)
453     : QObject(parent)
454     , m_task(task)
455     , m_finished(false)
456 {
457     connect(task, SIGNAL(finished(QctTask*)), this, SLOT(onTaskFinished()), Qt::DirectConnection);
458     connect(task, SIGNAL(destroyed(QObject*)), this, SLOT(onTaskFinished()), Qt::DirectConnection);
459 }
460
461 bool
462 QctTaskWaiter::wait(int timeout)
463 {
464     // To make this really thread safe, we'd need a function to lock the queue
465
466     if (m_task.isNull()) {
467         return false;
468     }
469
470     if (m_finished) {
471         return true;
472     }
473
474     m_mutex.lock();
475     const bool finished = m_condition.wait(&m_mutex, (timeout != 0 ? timeout : ULONG_MAX));
476     m_mutex.unlock();
477
478     return finished;
479 }
480
481 void
482 QctTaskWaiter::onTaskFinished()
483 {
484     m_finished = true;
485
486     // We disconnect make sure that we don't get the signal twice with destroyed
487     m_task->disconnect(this, SLOT(onTaskFinished()));
488
489     m_condition.wakeAll();
490 }
491
492 ////////////////////////////////////////////////////////////////////////////////////////////////////
493 // Instances which manage contact engine instances
494 ////////////////////////////////////////////////////////////////////////////////////////////////////
495
496 QContactTrackerEngine::QContactTrackerEngine(const QMap<QString, QString> &parameters,
497                                              const QString &managerName, int interfaceVersion,
498                                              QObject *parent)
499     : d(new QContactTrackerEngineData(parameters, managerName, interfaceVersion))
500 {
501     // workaround for Qt type system madness
502     qRegisterMetaType<QContactAbstractRequest::State>();
503
504     // workaround for QTMOBILITY-1526
505     if (0 != parent) {
506         setParent(parent);
507     }
508
509     connectSignals();
510 }
511
512 QContactTrackerEngine::QContactTrackerEngine(const QContactTrackerEngine& other)
513     : d(other.d)
514 {
515     d.detach();
516     connectSignals();
517 }
518
519 QContactTrackerEngine::~QContactTrackerEngine()
520 {
521     disconnectSignals();
522 }
523
524 QContactTrackerEngine&
525 QContactTrackerEngine::operator=(const QContactTrackerEngine& other)
526 {
527     disconnectSignals();
528
529     d = other.d;
530     d.detach();
531
532     connectSignals();
533
534     return *this;
535 }
536
537 ////////////////////////////////////////////////////////////////////////////////////////////////////
538 // Methods which describe the contact engine's capabilities
539 ////////////////////////////////////////////////////////////////////////////////////////////////////
540
541 QMap<QString, QString>
542 QContactTrackerEngine::managerParameters() const
543 {
544     return d->m_parameters.m_managerParameters;
545 }
546
547 QString
548 QContactTrackerEngine::managerName() const
549 {
550     return d->m_parameters.m_engineName;
551 }
552
553 int
554 QContactTrackerEngine::managerVersion() const
555 {
556     return d->m_parameters.m_engineVersion;
557 }
558
559 bool
560 QContactTrackerEngine::hasFeature(QContactManager::ManagerFeature feature,
561                                   const QString& contactType) const
562 {
563     if (not supportedContactTypes().contains(contactType)) {
564         return false;
565     }
566
567     switch (feature) {
568     case QContactManager::ArbitraryRelationshipTypes:
569     case QContactManager::ActionPreferences:
570     case QContactManager::ChangeLogs:
571     case QContactManager::SelfContact:
572         return true;
573
574     case QContactManager::Groups:
575     case QContactManager::Relationships:
576         return supportedContactTypes().contains(QContactType::TypeGroup);
577
578     default:
579         return false;
580     }
581 }
582
583 bool
584 QContactTrackerEngine::isFilterSupported(const QContactFilter& filter) const
585 {
586     return QTrackerScalarContactQueryBuilder::isFilterSupported(filter);
587 }
588
589 QList<QVariant::Type>
590 QContactTrackerEngine::supportedDataTypes() const
591 {
592     if (d->m_supportedDataTypes.isEmpty()) {
593         QSet<QVariant::Type> typeSet;
594
595         foreach(const QTrackerContactDetailSchema& schema, schemas()) {
596             typeSet += schema.supportedDataTypes();
597         }
598
599         d->m_supportedDataTypes = typeSet.toList();
600     }
601
602     return d->m_supportedDataTypes;
603 }
604
605 QStringList
606 QContactTrackerEngine::supportedContactTypes() const
607 {
608     return schemas().keys();
609 }
610
611 QContactDetailDefinitionMap
612 QContactTrackerEngine::detailDefinitions(const QString& contactType,
613                                          QContactManager::Error* error) const
614 {
615     const QTrackerContactDetailSchemaMap::ConstIterator schema = schemas().find(contactType);
616
617     if (schema == schemas().constEnd()) {
618         qctPropagate(QContactManager::InvalidContactTypeError, error);
619         return QContactDetailDefinitionMap();
620     }
621
622     qctPropagate(QContactManager::NoError, error);
623     return schema->detailDefinitions();
624 }
625
626 QContactDetailDefinition
627 QContactTrackerEngine::detailDefinition(const QString& definitionName,
628                                         const QString& contactType,
629                                         QContactManager::Error* error) const
630 {
631     const QTrackerContactDetailSchemaMap::ConstIterator schema = schemas().find(contactType);
632
633     if (schema == schemas().constEnd()) {
634         qctPropagate(QContactManager::InvalidContactTypeError, error);
635         return QContactDetailDefinition();
636     }
637
638     const QContactDetailDefinitionMap &definitions(schema->detailDefinitions());
639     const QContactDetailDefinitionMap::ConstIterator detail(definitions.find(definitionName));
640
641     if (definitions.constEnd() == detail) {
642         qctPropagate(QContactManager::DoesNotExistError, error);
643         return QContactDetailDefinition();
644     }
645
646     qctPropagate(QContactManager::NoError, error);
647     return detail.value();
648 }
649
650 const QTrackerContactDetailSchemaMap &
651 QContactTrackerEngine::schemas() const
652 {
653     return d->m_parameters.m_detailSchemas;
654 }
655
656 const QTrackerContactDetailSchema &
657 QContactTrackerEngine::schema(const QString& contactType) const
658 {
659     QTrackerContactDetailSchemaMap::ConstIterator schema = schemas().find(contactType);
660
661     if (schema == schemas().constEnd()) {
662         qctFail(QString::fromLatin1("Unexpected contact type %1. Aborting.").arg(contactType));
663         schema = schemas().constBegin();
664     }
665
666     return schema.value();
667 }
668
669 bool
670 QContactTrackerEngine::setSelfContactId(const QContactLocalId&,
671                                         QContactManager::Error* error)
672 {
673     REPORT_UNSUPPORTED_FUNCTION(error);
674     return false;
675 }
676
677 QContactLocalId
678 QContactTrackerEngine::selfContactId(QContactManager::Error* error) const
679 {
680     if (0 == d->m_selfContactId) {
681         QctTrackerIdResolver resolver(QStringList() << nco::default_contact_me::iri());
682
683         if (resolver.lookupAndWait()) {
684             d->m_selfContactId = resolver.trackerIds().first();
685         }
686     }
687
688     qctPropagate(d->m_selfContactId ? QContactManager::NoError
689                                     : QContactManager::DoesNotExistError, error);
690
691     return d->m_selfContactId;
692 }
693
694 const QContactTrackerEngine::DebugFlags &
695 QContactTrackerEngine::debugFlags() const
696 {
697     return d->m_parameters.m_debugFlags;
698 }
699
700 bool
701 QContactTrackerEngine::hasDebugFlag(DebugFlag flag) const
702 {
703     return debugFlags().testFlag(flag);
704 }
705
706 int
707 QContactTrackerEngine::concurrencyLevel() const
708 {
709     return d->m_parameters.m_concurrencyLevel;
710 }
711
712 int
713 QContactTrackerEngine::batchSize() const
714 {
715     return d->m_parameters.m_batchSize;
716 }
717
718 int
719 QContactTrackerEngine::requestTimeout() const
720 {
721     return d->m_parameters.m_requestTimeout;
722 }
723
724 int
725 QContactTrackerEngine::trackerTimeout() const
726 {
727     return d->m_parameters.m_trackerTimeout;
728 }
729
730 int
731 QContactTrackerEngine::coalescingDelay() const
732 {
733     return d->m_parameters.m_coalescingDelay;
734 }
735
736 const QString &
737 QContactTrackerEngine::graphIri() const
738 {
739     return d->m_parameters.m_graphIri;
740 }
741
742 QctGuidAlgorithm &
743 QContactTrackerEngine::guidAlgorithm() const
744 {
745     return *d->m_parameters.m_guidAlgorithm;
746 }
747
748 const QString &
749 QContactTrackerEngine::syncTarget() const
750 {
751     return d->m_parameters.m_syncTarget;
752 }
753
754 QStringList
755 QContactTrackerEngine::weakSyncTargets() const
756 {
757     return d->m_parameters.m_weakSyncTargets;
758 }
759
760 ////////////////////////////////////////////////////////////////////////////////////////////////////
761 // Synchronous data access methods
762 ////////////////////////////////////////////////////////////////////////////////////////////////////
763
764 QList<QContactLocalId>
765 QContactTrackerEngine::contactIds(const QList<QContactSortOrder>& sortOrders,
766                                   QContactManager::Error* error) const
767 {
768     return contactIds(QContactFilter(), sortOrders, error);
769 }
770
771 QList<QContactLocalId>
772 QContactTrackerEngine::contactIds(const QContactFilter& filter,
773                                   const QList<QContactSortOrder>& sortOrders,
774                                   QContactManager::Error* error) const
775 {
776     QContactLocalIdFetchRequest request;
777
778     request.setFilter(filter);
779     request.setSorting(sortOrders);
780
781     runSyncRequest(&request, error);
782
783     return request.ids();
784 }
785
786 QList<QContact>
787 QContactTrackerEngine::contacts(const QContactFilter& filter,
788                                 const QList<QContactSortOrder>& sortOrders,
789                                 const QContactFetchHint& fetchHint,
790                                 QContactManager::Error* error) const
791 {
792     QContactFetchRequest request;
793
794     request.setFetchHint(fetchHint);
795     request.setFilter(filter);
796     request.setSorting(sortOrders);
797
798     runSyncRequest(&request, error);
799
800     return request.contacts();
801 }
802
803 QContact
804 QContactTrackerEngine::contact(const QContactLocalId& contactId,
805                                const QContactFetchHint& fetchHint,
806                                QContactManager::Error* error) const
807 {
808     static bool warningNotShownYet = true;
809
810     if (warningNotShownYet) {
811         qctWarn(QString::fromLatin1("\n"
812                 "=============================================================================\n"
813                 "WARNING /!\\ - AVOID CALLING THIS FUNCTION FROM PRODUCTION CODE!!!\n"
814                 "=============================================================================\n"
815                 "QContactManager::contact() is blocking on D-Bus roundtrip while accessing\n"
816                 "tracker. Please consider using batch API (QContactManager::contacts()),\n"
817                 "or even better use the asynchronous QContactFetchRequest API, instead of\n"
818                 "fetching contacts one by one.\n"
819                 "\n"
820                 "Please note that reading 100 ids and 100 contact by ids is ~100 times\n"
821                 "slower than reading 100 contacts at once with QContactFetchRequest.\n"
822                 "\n"
823                 "Offending application is %1 [%2].\n"
824                 "=============================================================================").
825                 arg(QCoreApplication::applicationFilePath(),
826                     QString::number(QCoreApplication::applicationPid())));
827
828
829         warningNotShownYet = false;
830     }
831
832     return contactImpl(contactId, fetchHint, error);
833 }
834
835 // Used in tests, removed warning while decided if to provide sync api.
836 // Until then customers are advised to use async.
837 QContact
838 QContactTrackerEngine::contactImpl(const QContactLocalId& contactId,
839                                    const QContactFetchHint& fetchHint,
840                                    QContactManager::Error* error) const
841 {
842     QContactLocalIdFilter idFilter;
843     idFilter.setIds(QList<QContactLocalId>() << contactId);
844
845     QContactFetchRequest request;
846     request.setFetchHint(fetchHint);
847     request.setFilter(idFilter);
848
849     if (not runSyncRequest(&request, error)) {
850         return QContact();
851     }
852
853     QList<QContact> contacts(request.contacts());
854
855     if (contacts.isEmpty()) {
856         qctPropagate(QContactManager::DoesNotExistError, error);
857         return QContact();
858     }
859
860     if (contacts.count() > 1) {
861         qctWarn(QString::fromLatin1("Expected only one contact, but got %1").arg(contacts.count()));
862     }
863
864     return contacts.first();
865 }
866
867 bool
868 QContactTrackerEngine::saveContact(QContact* contact, QContactManager::Error* error)
869 {
870     if (0 == contact) {
871         qctPropagate(QContactManager::BadArgumentError, error);
872         return false;
873     }
874
875     QList<QContact> contactList(QList<QContact>() << *contact);
876     bool success = saveContacts(&contactList, 0, error);
877
878     if (not contactList.isEmpty()) {
879         const QContact &savedContact = contactList.first();
880         setContactDisplayLabel(contact, savedContact.displayLabel());
881         contact->setId(savedContact.id());
882     }
883
884     return success;
885 }
886
887 bool
888 QContactTrackerEngine::saveContacts(QList<QContact>* contacts, ErrorMap* errorMap,
889                                     QContactManager::Error* error)
890 {
891     if (0 == contacts) {
892         qctPropagate(QContactManager::BadArgumentError, error);
893         return false;
894     }
895
896     QContactSaveRequest request;
897     request.setContacts(*contacts);
898
899     const bool hasFinished = runSyncRequest(&request, error);
900     qctPropagate(request.errorMap(), errorMap);
901
902     if (not hasFinished) {
903         return false;
904     }
905
906     const QList<QContact> savedContacts(request.contacts());
907
908     // only update the contact id, all other updates must be fetched
909     for(int i = 0; i < contacts->size(); ++i) {
910         QContact &requestContact = (*contacts)[i];
911         const QContact &savedContact = savedContacts[i];
912         setContactDisplayLabel(&requestContact, savedContact.displayLabel());
913         requestContact.setId(savedContact.id());
914     }
915
916     return QContactManager::NoError == request.error();
917 }
918
919 bool
920 QContactTrackerEngine::removeContact(const QContactLocalId &contactId,
921                                      QContactManager::Error *error)
922 {
923     return removeContacts(QList<QContactLocalId>() << contactId, 0, error);
924 }
925
926 bool
927 QContactTrackerEngine::removeContacts(const QList<QContactLocalId>& contactIds,
928                                       ErrorMap *errorMap, QContactManager::Error *error)
929 {
930     QContactRemoveRequest request;
931
932     request.setContactIds(contactIds);
933
934     runSyncRequest(&request, error);
935
936     qctPropagate(request.errorMap(), errorMap);
937
938     return QContactManager::NoError == request.error();
939 }
940
941 QList<QContactRelationship>
942 QContactTrackerEngine::relationships(const QString &relationshipType,
943                                      const QContactId &participantId, QContactRelationship::Role role,
944                                      QContactManager::Error *error) const
945 {
946     QList<QContactRelationship> result;
947
948     // Mapping of this call to QContactRelationshipFetchRequest is not directly possible
949     // as QContactRelationshipFetchRequest gets set first and second contact but not
950     // just one participantId and a role.
951     //
952     // So in the case that role is QContactRelationship::Either two separate requests need to be done.
953
954     if (role==QContactRelationship::First
955         || role==QContactRelationship::Either) {
956         QContactRelationshipFetchRequest request;
957
958         request.setRelationshipType(relationshipType);
959         request.setFirst(participantId);
960
961         QContactManager::Error fetchError = QContactManager::UnspecifiedError;
962         runSyncRequest(&request, &fetchError);
963         qctPropagate(fetchError, error);
964
965         if (fetchError != QContactManager::NoError) {
966             return QList<QContactRelationship>();
967         }
968
969         result += request.relationships();
970     }
971
972     if (role==QContactRelationship::Second
973         || role==QContactRelationship::Either) {
974         QContactRelationshipFetchRequest request;
975
976         request.setRelationshipType(relationshipType);
977         request.setSecond(participantId);
978
979         QContactManager::Error fetchError = QContactManager::UnspecifiedError;
980         runSyncRequest(&request, &fetchError);
981         qctPropagate(fetchError, error);
982
983         if (fetchError != QContactManager::NoError) {
984             return QList<QContactRelationship>();
985         }
986
987         result += request.relationships();
988     }
989
990     return result;
991 }
992
993 bool
994 QContactTrackerEngine::saveRelationship(QContactRelationship *relationship, QContactManager::Error *error)
995 {
996     if (0 == relationship) {
997         qctPropagate(QContactManager::BadArgumentError, error);
998         return false;
999     }
1000
1001     QList<QContactRelationship> relationships =
1002             QList<QContactRelationship>() << *relationship;
1003
1004     return saveRelationships(&relationships, 0, error);
1005 }
1006
1007 bool
1008 QContactTrackerEngine::saveRelationships(QList<QContactRelationship> *relationships,
1009                                          ErrorMap *errorMap, QContactManager::Error *error)
1010 {
1011     if (0 == relationships) {
1012         qctPropagate(QContactManager::BadArgumentError, error);
1013         return false;
1014     }
1015
1016     QContactRelationshipSaveRequest request;
1017     request.setRelationships(*relationships);
1018
1019     runSyncRequest(&request, error);
1020     qctPropagate(request.errorMap(), errorMap);
1021
1022     return QContactManager::NoError == request.error();
1023 }
1024
1025 bool
1026 QContactTrackerEngine::removeRelationship(const QContactRelationship &relationship, QContactManager::Error *error)
1027 {
1028     return removeRelationships(QList<QContactRelationship>() << relationship, 0, error);
1029 }
1030
1031 bool
1032 QContactTrackerEngine::removeRelationships(const QList<QContactRelationship> &relationships,
1033                                            ErrorMap *errorMap, QContactManager::Error *error)
1034 {
1035     QContactRelationshipRemoveRequest request;
1036     request.setRelationships(relationships);
1037
1038     runSyncRequest(&request, error);
1039     qctPropagate(request.errorMap(), errorMap);
1040
1041     return QContactManager::NoError == request.error();
1042 }
1043
1044
1045 /// returns the DisplayNameDetailList for the name order given, if found.
1046 /// Default is the one for QContactDisplayLabel__FirstNameLastNameOrder.
1047 static const DisplayLabelGeneratorList &
1048 findDisplayLabelDetails(const DisplayLabelGeneratorListMap &displayNameGeneratorListMap,
1049                         const QString &nameOrder)
1050 {
1051     DisplayLabelGeneratorListMap::ConstIterator it = displayNameGeneratorListMap.find(nameOrder);
1052
1053     // not found?
1054     if (it == displayNameGeneratorListMap.constEnd()) {
1055         // use default setting
1056         it = displayNameGeneratorListMap.find(QctSettings().nameOrder());
1057     }
1058     // should not happen, but better safe than sorry:
1059     if (it == displayNameGeneratorListMap.constEnd()) {
1060         it = displayNameGeneratorListMap.constBegin();
1061     }
1062
1063     return it.value();
1064 }
1065
1066 QContactFetchHint
1067 QContactTrackerEngine::normalizedFetchHint(QContactFetchHint fetchHint, const QString &nameOrder)
1068 {
1069     QStringList detailDefinitionHint = fetchHint.detailDefinitionsHint();
1070
1071     // Make sure the display name can be synthesized when needed
1072     if (detailDefinitionHint.contains(QContactDisplayLabel::DefinitionName)) {
1073         const DisplayLabelGeneratorList &displayLabelGenerators =
1074             findDisplayLabelDetails(d->m_displayNameGeneratorListMap, nameOrder);
1075
1076         foreach(const DisplayLabelGenerator &generator, displayLabelGenerators) {
1077             if (not detailDefinitionHint.contains(generator.requiredDetailName())) {
1078                 detailDefinitionHint += generator.requiredDetailName();
1079             }
1080         }
1081
1082         fetchHint.setDetailDefinitionsHint(detailDefinitionHint);
1083     }
1084
1085     return fetchHint;
1086 }
1087
1088 void
1089 QContactTrackerEngine::updateDisplayLabel(QContact &contact, const QString &nameOrder) const
1090 {
1091     const QContactDisplayLabel displayLabel = contact.detail<QContactDisplayLabel>();
1092
1093     if (displayLabel.label().isEmpty()) {
1094         setContactDisplayLabel(&contact, createDisplayLabel(contact, nameOrder));
1095     }
1096 }
1097
1098 QString
1099 QContactTrackerEngine::createDisplayLabel(const QContact &contact,
1100                                           const QString &nameOrder) const
1101 {
1102     const DisplayLabelGeneratorList &displayNameGeneratorList =
1103         findDisplayLabelDetails(d->m_displayNameGeneratorListMap, nameOrder);
1104
1105     QString label;
1106
1107     foreach(const DisplayLabelGenerator &generator, displayNameGeneratorList) {
1108         label = generator.createDisplayLabel(contact);
1109
1110         if (not label.isEmpty()) {
1111             break;
1112         }
1113     }
1114
1115     return label;
1116 }
1117
1118 // gives a ranking for the presence state: the higher the value,
1119 // the more "available" is a contact with that presence state
1120 static int
1121 availabilityRank(QContactPresence::PresenceState state)
1122 {
1123     switch(state) {
1124     case QContactPresence::PresenceUnknown:
1125         return 0;
1126     case QContactPresence::PresenceHidden:
1127     case QContactPresence::PresenceOffline:
1128         return 1;
1129     case QContactPresence::PresenceAway:
1130     case QContactPresence::PresenceExtendedAway:
1131         return 2;
1132     case QContactPresence::PresenceBusy:
1133         return 3;
1134     case QContactPresence::PresenceAvailable:
1135         return 4;
1136     }
1137
1138     return availabilityRank(QContactPresence::PresenceUnknown);
1139 }
1140
1141 template <class T> static void
1142 transfer(const T &key, const QContactDetail &source, QContactDetail &target)
1143 {
1144     QVariant value(source.variantValue(key));
1145
1146     if (not value.isNull()) {
1147         target.setValue(key, value);
1148     }
1149 }
1150
1151 void
1152 QContactTrackerEngine::updateGlobalPresence(QContact &contact)
1153 {
1154     const QList<QContactPresence> presenceDetails(contact.details<QContactPresence>());
1155     const QContactPresence *mostAvailable(0);
1156     const QContactPresence *mostRecent(0);
1157
1158     if (not presenceDetails.isEmpty()) {
1159         mostAvailable = mostRecent = &presenceDetails.first();
1160     }
1161
1162     foreach(const QContactPresence &detail, presenceDetails) {
1163         if (availabilityRank(detail.presenceState()) > availabilityRank(mostAvailable->presenceState())) {
1164             if (detail.timestamp() == mostRecent->timestamp()) {
1165                 mostRecent = &detail;
1166             }
1167
1168             mostAvailable = &detail;
1169         }
1170
1171         if (detail.timestamp() > mostRecent->timestamp()) {
1172             mostRecent = &detail;
1173         }
1174     }
1175
1176     QContactGlobalPresence global(contact.detail<QContactGlobalPresence>());
1177
1178     if (mostRecent && mostAvailable) {
1179         QSet<QString> linkedDetails;
1180         linkedDetails << mostRecent->detailUri();
1181         linkedDetails << mostAvailable->detailUri();
1182         global.setLinkedDetailUris(linkedDetails.toList());
1183
1184         transfer(QContactGlobalPresence::FieldNickname, *mostRecent, global);
1185         transfer(QContactGlobalPresence::FieldTimestamp, *mostRecent, global);
1186         transfer(QContactGlobalPresence::FieldCustomMessage, *mostRecent, global);
1187
1188         transfer(QContactGlobalPresence::FieldPresenceState, *mostAvailable, global);
1189         transfer(QContactGlobalPresence::FieldPresenceStateText, *mostAvailable, global);
1190         transfer(QContactGlobalPresence::FieldPresenceStateImageUrl, *mostAvailable, global);
1191
1192         contact.saveDetail(&global);
1193     } else {
1194         contact.removeDetail(&global);
1195     }
1196 }
1197
1198 typedef QPair<QContactAvatar, int> AvatarWithAvailability;
1199
1200 inline bool
1201 operator<(const AvatarWithAvailability &a, const AvatarWithAvailability &b)
1202 {
1203     // the lower the availability rank, the more significant the detail
1204     return a.second > b.second;
1205 }
1206
1207 void
1208 QContactTrackerEngine::updateAvatar(QContact &contact)
1209 {
1210     QContactPersonalAvatar personalAvatar = contact.detail<QContactPersonalAvatar>();
1211     const QList<QContactOnlineAvatar> currentOnlineAvatars = contact.details<QContactOnlineAvatar>();
1212
1213     // Function was triggered by an update of nco:photo
1214     if (not personalAvatar.isEmpty()) {
1215         QContactAvatar photoAvatar = personalAvatar.toAvatar();
1216
1217         // If there was already a nco:photo (no linked uri), remove it
1218         contact.removeDetail(&personalAvatar);
1219
1220         // nco:photo always goes first, so that QContact::detail<QContactAvatar> returns it
1221         contact.saveDetail(&photoAvatar);
1222     }
1223
1224     // Function was triggered by an update of a IMAddress' imAvatar
1225     if (not currentOnlineAvatars.empty()) {
1226         // find all available presence details
1227         QMap<QString, QContactPresence> presenceDetails;
1228
1229         foreach(const QContactPresence &detail, contact.details<QContactPresence>()) {
1230             const QString accountPath = parsePresenceIri(detail.detailUri());
1231
1232             if (not accountPath.isEmpty()) {
1233                 presenceDetails.insert(accountPath, detail);
1234             }
1235         }
1236
1237         // store online avatars with their availablity
1238         QList<AvatarWithAvailability> avatarsWithAvailability;
1239
1240         foreach(QContactOnlineAvatar avatar, currentOnlineAvatars) {
1241             contact.removeDetail(&avatar);
1242
1243             // FIXME: properly define schema for detailUri and linkedDetailUri
1244             QStringList linkedDetailUris = avatar.linkedDetailUris();
1245
1246             if (linkedDetailUris.isEmpty()) {
1247                 linkedDetailUris += QString();
1248             }
1249
1250             foreach(const QString &uri, linkedDetailUris) {
1251                 const QString accountPath = not uri.isEmpty()
1252                         ? parseTelepathyIri(uri)
1253                         : QString();
1254
1255                 int availability = availabilityRank(QContactPresence::PresenceUnknown);
1256
1257                 if (not accountPath.isEmpty()) {
1258                     const QContactPresence presence = presenceDetails.value(accountPath);
1259                     availability = availabilityRank(presence.presenceState());
1260                 } else {
1261                     qctWarn("QTrackerContactOnlineAvatar should always be linked with an online account");
1262                 }
1263
1264                 avatarsWithAvailability += qMakePair(avatar.toAvatar(), availability);
1265             }
1266         }
1267
1268         // sort avatars by their availablity
1269         qSort(avatarsWithAvailability);
1270
1271         // add regular avatar details to contact
1272         for(int i = 0; i < avatarsWithAvailability.count(); ++i) {
1273             contact.saveDetail(&avatarsWithAvailability[i].first);
1274         }
1275     }
1276 }
1277
1278 bool
1279 QContactTrackerEngine::isWeakSyncTarget(const QString &syncTarget) const
1280 {
1281     if (d->m_parameters.m_weakSyncTargets.contains(syncTarget)) {
1282         return true;
1283     }
1284
1285     return (d->m_parameters.m_weakSyncTargets.count() == 1 &&
1286             d->m_parameters.m_weakSyncTargets.first() == MangleAllSyncTargets);
1287 }
1288
1289 ////////////////////////////////////////////////////////////////////////////////////////////////////
1290 // Asynchronous data access methods
1291 ////////////////////////////////////////////////////////////////////////////////////////////////////
1292
1293 bool
1294 QContactTrackerEngine::checkSecurityTokens(QContactAbstractRequest *request)
1295 {
1296     // Plugin users often fail to provide all required security tokens.
1297     // Therefore we print warnings to educate them. If the security checks
1298     // should cause trouble they can be temporarly disabled by exporting
1299     // QT_CONTACTS_TRACKER="debug=no-sec".
1300
1301     if (d->m_mandatoryTokensFound) {
1302         return true;
1303     }
1304
1305     QStringList theGateKeepersBlameList;
1306     QStringList missingOptionalTokens;
1307
1308 #ifdef ENABLE_CREDENTIALS
1309
1310     static const QStringList requiredSecurityTokens =
1311             QStringList() << QLatin1String("TrackerReadAccess")
1312                           << QLatin1String("TrackerWriteAccess");
1313
1314     static const QStringList optionalSecurityTokens =
1315             QStringList() << QLatin1String("GRP::metadata-users");
1316
1317     foreach(const QString &credential, requiredSecurityTokens) {
1318         QString errorMessage;
1319
1320         if (not MssfQt::CredentialsManager::hasProcessCredential(0, credential, &errorMessage)) {
1321             theGateKeepersBlameList += credential;
1322             qctWarn(errorMessage);
1323         }
1324     }
1325
1326     foreach(const QString &credential, optionalSecurityTokens) {
1327         QString errorMessage;
1328
1329         if (not MssfQt::CredentialsManager::hasProcessCredential(0, credential, &errorMessage)) {
1330             missingOptionalTokens += credential;
1331             qctWarn(errorMessage);
1332         }
1333     }
1334
1335 #endif // ENABLE_CREDENTIALS
1336
1337     if (not theGateKeepersBlameList.isEmpty()) {
1338         qctWarn(QString::fromLatin1("\n"
1339                  "=============================================================================\n"
1340                  "WARNING /!\\ - MANDATORY SECURITY TOKENS ARE MISSIN\n"
1341                  "=============================================================================\n"
1342                  "Rejecting %2.\n"
1343                  "Please add an AEGIS manifest to your package requesting the following\n"
1344                  "security tokens: %1 for %3 [%4].\n"
1345                  "=============================================================================").
1346                 arg(theGateKeepersBlameList.join(QLatin1String(", ")),
1347                     QLatin1String(request->metaObject()->className()),
1348                     QCoreApplication::applicationFilePath(),
1349                     QString::number(QCoreApplication::applicationPid())));
1350
1351         return false;
1352     }
1353
1354     if (not missingOptionalTokens.isEmpty()) {
1355         qctWarn(QString::fromLatin1("\n"
1356                  "=============================================================================\n"
1357                  "WARNING /!\\ - OPTIONAL SECURITY TOKENS ARE MISSING\n"
1358                  "=============================================================================\n"
1359                  "Full functionality like tracker direct access is not available.\n"
1360                  "Please add an AEGIS manifest to your package requesting the following\n"
1361                  "security tokens: %1 for %2 [%3].\n"
1362                  "=============================================================================").
1363                 arg(missingOptionalTokens.join(QLatin1String(", ")),
1364                     QCoreApplication::applicationFilePath(),
1365                     QString::number(QCoreApplication::applicationPid())));
1366     }
1367
1368     d->m_mandatoryTokensFound = true;
1369
1370     return true;
1371 }
1372
1373 QctTask*
1374 QContactTrackerEngine::startRequestImpl(QContactAbstractRequest *request)
1375 {
1376     QCT_SYNCHRONIZED(&d->m_requestLifeGuard);
1377
1378     QScopedPointer<QctRequestTask> task(new QctRequestTask(this, request));
1379
1380     // Make sure mandatory tracker:ids have been resolved.
1381     if (task->dependencies().testFlag(QTrackerAbstractRequest::ResourceCache) &&
1382         not d->m_satisfiedDependencies.testFlag(QTrackerAbstractRequest::ResourceCache)) {
1383         d->m_queue->enqueue(new QctResourceCacheTask(schemas(), this));
1384         d->m_satisfiedDependencies |= QTrackerAbstractRequest::ResourceCache;
1385     }
1386
1387     if (task->dependencies().testFlag(QTrackerAbstractRequest::GuidAlgorithm) &&
1388         not d->m_satisfiedDependencies.testFlag(QTrackerAbstractRequest::GuidAlgorithm)) {
1389         d->m_queue->enqueue(new QctGuidAlgorithmTask(this, this));
1390         d->m_satisfiedDependencies |= QTrackerAbstractRequest::GuidAlgorithm;
1391     }
1392
1393     // the contacts engine must bring requests into Activate state as soon as it is dealing with them
1394     // the (secret) state machine only permits this transitions Inactive > Active > Canceled/Finished
1395     // because in case of an error the state can go to Canceled/Finished, set to Active now
1396     // before starting the worker
1397     updateRequestState(request, QContactAbstractRequest::ActiveState);
1398
1399     d->m_queue->enqueue(task.data());
1400
1401     return task.take();
1402 }
1403
1404 bool
1405 QContactTrackerEngine::startRequest(QContactAbstractRequest *request)
1406 {
1407     return (0 != startRequestImpl(request));
1408 }
1409
1410 QTrackerAbstractRequest *
1411 QContactTrackerEngine::createRequestWorker(QContactAbstractRequest *request)
1412 {
1413     // about if mandatory security tokens are missing
1414     if (not checkSecurityTokens(request)) {
1415         return 0;
1416     }
1417
1418     // ensure old worker got destroyed when request gets reused
1419     requestDestroyed(request);
1420
1421     QTrackerAbstractRequest *worker = 0;
1422     QElapsedTimer t; t.start();
1423
1424     // choose proper request implementation
1425     switch(request->type())
1426     {
1427     case QContactAbstractRequest::ContactFetchRequest:
1428         worker = new QTrackerContactFetchRequest(request, this);
1429         break;
1430
1431     case QContactAbstractRequest::ContactLocalIdFetchRequest:
1432         worker = new QTrackerContactIdFetchRequest(request, this);
1433         break;
1434
1435     case QContactAbstractRequest::ContactRemoveRequest:
1436         if (0 == qobject_cast<QctContactMergeRequest *>(request)) {
1437             worker = new QTrackerContactRemoveRequest(request, this);
1438         } else {
1439             worker = new QTrackerContactCopyAndRemoveRequest(request, this);
1440         }
1441         break;
1442
1443     case QContactAbstractRequest::ContactSaveRequest:
1444         if (0 == qobject_cast< QctUnmergeIMContactsRequest *> (request)) {
1445             worker = new QTrackerContactSaveRequest(request, this);
1446         } else {
1447             worker = new QTrackerContactSaveOrUnmergeRequest(request, this);
1448         }
1449         break;
1450
1451     case QContactAbstractRequest::RelationshipFetchRequest:
1452         worker = new QTrackerRelationshipFetchRequest(request, this);
1453         break;
1454
1455     case QContactAbstractRequest::RelationshipRemoveRequest:
1456         worker = new QTrackerRelationshipRemoveRequest(request, this);
1457         break;
1458
1459     case QContactAbstractRequest::RelationshipSaveRequest:
1460         worker = new QTrackerRelationshipSaveRequest(request, this);
1461         break;
1462
1463     case QContactAbstractRequest::DetailDefinitionFetchRequest:
1464         worker = new QTrackerDetailDefinitionFetchRequest(request, this);
1465         break;
1466
1467     case QContactAbstractRequest::InvalidRequest:
1468     case QContactAbstractRequest::DetailDefinitionRemoveRequest:
1469     case QContactAbstractRequest::DetailDefinitionSaveRequest:
1470     case QContactAbstractRequest::ContactFetchByIdRequest:
1471         break;
1472     }
1473
1474     if (0 == worker) {
1475         qctWarn(QString::fromLatin1("Unsupported request type: %1").
1476                 arg(QLatin1String(request->metaObject()->className())));
1477         return 0;
1478     }
1479
1480     if (hasDebugFlag(ShowNotes)) {
1481         qDebug() << Q_FUNC_INFO << "time elapsed while constructing request workers:"<< request << t.elapsed();
1482     }
1483
1484     if (hasDebugFlag(ShowNotes)) {
1485         qDebug() << Q_FUNC_INFO << "running" << worker->metaObject()->className();
1486     }
1487
1488     // XXX The unit tests directly access the engine. Therefore requests created by our unit
1489     // tests don't have a manager attached. This prevents ~QContactAbstractRequest() to call our
1490     // engine's requestDestroyed(QContactAbstractRequest*) method, which results in memory leaks
1491     // within our our unit tests. To prevent those leaks we watch the request's destroyed()
1492     // signal and forward those signals to requestDestroyed(QContactAbstractRequest*) when
1493     // a request without contact manager is found.
1494     //
1495     // XXX This all of course is an ugly workaround. We probably should change the unit tests
1496     // to create use QContactManager accessing the engine via a static plugin.
1497     if (0 == request->manager()) {
1498         connect(request, SIGNAL(destroyed(QObject*)),
1499                 this, SLOT(onRequestDestroyed(QObject*)), Qt::DirectConnection);
1500     }
1501
1502     // track original request for this worker
1503     QCT_SYNCHRONIZED_WRITE(&d->m_tableLock);
1504
1505     d->m_workersByRequest.insert(request, worker);
1506     d->m_requestsByWorker.insert(worker, request);
1507
1508     return worker;
1509 }
1510
1511 QctRequestLocker
1512 QContactTrackerEngine::request(const QTrackerAbstractRequest *worker) const
1513 {
1514     QScopedPointer<QMutexLocker> locker(new QMutexLocker(&d->m_requestLifeGuard));
1515     QCT_SYNCHRONIZED_READ(&d->m_tableLock);
1516     return QctRequestLocker(locker.take(), d->m_requestsByWorker.value(worker));
1517 }
1518
1519 void
1520 QContactTrackerEngine::connectSignals()
1521 {
1522     if (d->m_changeListener) {
1523         connect(d->m_changeListener,
1524                 SIGNAL(contactsAdded(QList<QContactLocalId>)),
1525                 SIGNAL(contactsAdded(QList<QContactLocalId>)));
1526         connect(d->m_changeListener,
1527                 SIGNAL(contactsChanged(QList<QContactLocalId>)),
1528                 SIGNAL(contactsChanged(QList<QContactLocalId>)));
1529         connect(d->m_changeListener,
1530                 SIGNAL(contactsRemoved(QList<QContactLocalId>)),
1531                 SIGNAL(contactsRemoved(QList<QContactLocalId>)));
1532         connect(d->m_changeListener,
1533                 SIGNAL(relationshipsAdded(QList<QContactLocalId>)),
1534                 SIGNAL(relationshipsAdded(QList<QContactLocalId>)));
1535         connect(d->m_changeListener,
1536                 SIGNAL(relationshipsRemoved(QList<QContactLocalId>)),
1537                 SIGNAL(relationshipsRemoved(QList<QContactLocalId>)));
1538     }
1539 }
1540
1541 void
1542 QContactTrackerEngine::disconnectSignals()
1543 {
1544     if (d->m_changeListener) {
1545         d->m_changeListener->disconnect(this);
1546         d->m_changeListener = 0;
1547     }
1548 }
1549
1550 void
1551 QContactTrackerEngine::connectNotify(const char *signal)
1552 {
1553     if (0 == d->m_changeListener) {
1554         // Create the change listener on demand:
1555         // Creating the listener is expensive as we must subscribe to some DBus signals.
1556         // Also watching DBus without any specific need wastes energy by wakeing up processes.
1557
1558         typedef QHash<QString, QctTrackerChangeListener *> ListenersByUri;
1559         static QThreadStorage<ListenersByUri *> listeners;
1560
1561         if (not listeners.hasLocalData()) {
1562             listeners.setLocalData(new ListenersByUri);
1563         }
1564
1565         const QString uri = managerUri();
1566         d->m_changeListener = listeners.localData()->value(uri);
1567
1568         if (0 == d->m_changeListener) {
1569             d->m_changeListener = new QctTrackerChangeListener(this, qApp);
1570             listeners.localData()->insert(uri, d->m_changeListener);
1571         }
1572
1573         connectSignals();
1574     }
1575
1576     QContactManagerEngine::connectNotify(signal);
1577 }
1578
1579 void
1580 QContactTrackerEngine::requestDestroyed(QContactAbstractRequest *request)
1581 {
1582     QCT_SYNCHRONIZED(&d->m_requestLifeGuard);
1583     QCT_SYNCHRONIZED_WRITE(&d->m_tableLock);
1584
1585     if (0 == request) {
1586         return;
1587     }
1588
1589     QTrackerAbstractRequest *const worker = d->m_workersByRequest.take(request);
1590
1591     if (0 != worker) {
1592         d->m_requestsByWorker.remove(worker);
1593     }
1594 }
1595
1596 void
1597 QContactTrackerEngine::onRequestDestroyed(QObject *req)
1598 {
1599     // dynamic_cast<> does not work at this point (has a 0 result, or even crashes) because the
1600     // derived parts of the class have already been destroyed by the time the base QObject's
1601     // destructor has emitted this signal. So we do a regular static case, because
1602     // requestDestroyed(QContactAbstractRequest*) just compares the pointer value anyway.
1603     requestDestroyed(static_cast<QContactAbstractRequest *>(req));
1604 }
1605
1606 bool
1607 QContactTrackerEngine::waitForRequestFinished(QContactAbstractRequest *request, int msecs)
1608 {
1609     qctWarn(QString::fromLatin1("\n"
1610             "=============================================================================\n"
1611             "WARNING /!\\ - NEVER EVER CALL THIS FUNCTION FROM PRODUCTION CODE!!!\n"
1612             "=============================================================================\n"
1613             "QContactAbstractRequest::waitForFinished(), respectively\n"
1614             "QContactManagerEngine::waitForRequestFinished() must spin your\n"
1615             "application's event loop. Doing so will cause HAVOC AND PAIN for\n"
1616             "any non-trivial program.\n"
1617             "\n"
1618             "So please refactor your asynchronious code, or use the synchronious\n"
1619             "API if blocking your application is acceptable.\n"
1620             "\n"
1621             "WE MEAN IT!!!\n"
1622             "\n"
1623             "Offending application is %1 [%2].\n"
1624             "=============================================================================").
1625             arg(QCoreApplication::applicationFilePath(),
1626                 QString::number(QCoreApplication::applicationPid())));
1627
1628     return waitForRequestFinishedImpl(request, msecs);
1629 }
1630
1631 bool
1632 QContactTrackerEngine::waitForRequestFinishedImpl(QContactAbstractRequest *request, int msecs)
1633 {
1634     if (0 == request) {
1635         return false;
1636     }
1637
1638     RequestEventLoop eventLoop(request, msecs);
1639
1640     if (not eventLoop.isFinished()) {
1641         eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
1642     }
1643
1644     return eventLoop.isFinished();
1645 }
1646
1647 bool
1648 QContactTrackerEngine::runSyncRequest(QContactAbstractRequest *request,
1649                                       QContactManager::Error *error) const
1650 {
1651     QctSyncThread &thread = QctThreadManager::defaultThread();
1652     QctSyncRequestTask task(this, request, requestTimeout());
1653     request->moveToThread(&thread);
1654     thread.launchAndWait(&task);
1655
1656     if (not task.isFinished()) {
1657         qctPropagate(QContactManager::UnspecifiedError, error);
1658         return false;
1659     }
1660
1661     qctPropagate(request->error(), error);
1662
1663     return true;
1664 }
1665
1666 ////////////////////////////////////////////////////////////////////////////////////////////////////
1667 // Synthesized contact details
1668 ////////////////////////////////////////////////////////////////////////////////////////////////////
1669
1670 QString
1671 QContactTrackerEngine::synthesizedDisplayLabel(const QContact& contact,
1672                                                QContactManager::Error* error) const
1673 {
1674     qctPropagate(QContactManager::NoError, error);
1675
1676     return createDisplayLabel(contact);
1677 }
1678
1679 QContact
1680 QContactTrackerEngine::compatibleContact(const QContact &original, QContactManager::Error* error) const
1681 {
1682     QContact contact = original;
1683
1684     foreach(const QTrackerContactDetail &detail, schema(contact.type()).details()) {
1685         QList<QContactDetail> contactDetails = contact.details(detail.name());
1686
1687         if (contactDetails.empty()) {
1688             continue;
1689         }
1690
1691         // Check that detail respects our schema's cardinality
1692         if (not detail.isUnique()) {
1693             continue;
1694         }
1695
1696         if (contactDetails.count() < 2) {
1697             continue;
1698         }
1699
1700         qctWarn(QString::fromLatin1("Dropping odd details: %2 detail must be unique").
1701                 arg(detail.name()));
1702
1703         for(int i = 1; i < contactDetails.count(); ++i) {
1704             contact.removeDetail(&contactDetails[i]);
1705         }
1706     }
1707
1708     // Check fields contents
1709     foreach(const QTrackerContactDetail &detail, schema(contact.type()).details()) {
1710         QList<QContactDetail> contactDetails = contact.details(detail.name());
1711
1712         foreach (QContactDetail contactDetail, contactDetails) {
1713             const QVariantMap detailValues = contactDetail.variantValues();
1714
1715             for (QVariantMap::ConstIterator it = detailValues.constBegin();
1716                  it != detailValues.constEnd(); ++it) {
1717                 const QTrackerContactDetailField *field = detail.field(it.key());
1718
1719                 if (field == 0) {
1720                     // We can't validate custom fields, skip
1721                     continue;
1722                 }
1723
1724                 QVariant computedValue;
1725
1726                 if (not field->makeValue(it.value(), computedValue)) {
1727                     // Apparently this is not an error, we just prune the field...
1728                     // makeValue already prints a debug message if it returns false,
1729                     // so we can stay silent here. No idea how UI can provide any
1730                     // useful feedback out of that.
1731                     contactDetail.removeValue(it.key());
1732                     break;
1733                 }
1734
1735                 // The computed value is only useful to generate SPARQL queries,
1736                 // we should not save it back to the field.
1737             }
1738
1739             contact.saveDetail(&contactDetail);
1740         }
1741     }
1742
1743     qctPropagate(QContactManager::NoError, error);
1744
1745     return contact;
1746 }
1747
1748 ////////////////////////////////////////////////////////////////////////////////////////////////////
1749 // Unsupported functions
1750 ////////////////////////////////////////////////////////////////////////////////////////////////////
1751
1752 bool
1753 QContactTrackerEngine::validateContact(const QContact&, QContactManager::Error* error) const
1754 {
1755     REPORT_UNSUPPORTED_FUNCTION(error);
1756     return false;
1757 }
1758
1759 bool
1760 QContactTrackerEngine::validateDefinition(const QContactDetailDefinition&,
1761                                           QContactManager::Error* error) const
1762 {
1763     REPORT_UNSUPPORTED_FUNCTION(error);
1764     return false;
1765 }
1766
1767 bool
1768 QContactTrackerEngine::saveDetailDefinition(const QContactDetailDefinition&, const QString&,
1769                                             QContactManager::Error* error)
1770 {
1771     REPORT_UNSUPPORTED_FUNCTION(error);
1772     return false;
1773 }
1774
1775 bool
1776 QContactTrackerEngine::removeDetailDefinition(const QString&, const QString&,
1777                                               QContactManager::Error* error)
1778 {
1779     REPORT_UNSUPPORTED_FUNCTION(error);
1780     return false;
1781 }
1782
1783 bool
1784 QContactTrackerEngine::cancelRequest(QContactAbstractRequest *request)
1785 {
1786     if (0 == request) {
1787         return false;
1788     }
1789
1790     QCT_SYNCHRONIZED(&d->m_requestLifeGuard);
1791     QCT_SYNCHRONIZED_READ(&d->m_tableLock);
1792
1793     QTrackerAbstractRequest *const worker = d->m_workersByRequest.value(request);
1794
1795     if (0 != worker) {
1796         return worker->cancel();
1797     }
1798     return false;
1799 }
1800
1801 bool
1802 QContactTrackerEngine::isRelationshipTypeSupported(const QString& relationshipType,
1803                                                    const QString& contactType) const
1804 {
1805     if (relationshipType == QContactRelationship::HasMember
1806             && supportedContactTypes().contains(contactType)
1807             && (contactType == QContactType::TypeContact
1808                 || contactType == QContactType::TypeGroup)) {
1809         return true;
1810     }
1811
1812     return false;
1813 }