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