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