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