Changes: Beautify online avatar conversion
[qtcontacts-tracker:hasselmms-qtcontacts-tracker.git] / src / engine / contactmanagerengine.cpp
1 /*********************************************************************************
2  ** This file is part of QtContacts tracker storage plugin
3  **
4  ** Copyright (c) 2009-2011 Nokia Corporation and/or its subsidiary(-ies).
5  **
6  ** Contact:  Nokia Corporation (info@qt.nokia.com)
7  **
8  ** GNU Lesser General Public License Usage
9  ** This file may be used under the terms of the GNU Lesser General Public License
10  ** version 2.1 as published by the Free Software Foundation and appearing in the
11  ** file LICENSE.LGPL included in the packaging of this file.  Please review the
12  ** following information to ensure the GNU Lesser General Public License version
13  ** 2.1 requirements will be met:
14  ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
15  **
16  ** In addition, as a special exception, Nokia gives you certain additional rights.
17  ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included
18  ** in the file LGPL_EXCEPTION.txt in this package.
19  **
20  ** Other Usage
21  ** Alternatively, this file may be used in accordance with the terms and
22  ** conditions contained in a signed written agreement between you and Nokia.
23  *********************************************************************************/
24
25 #include "contactmanagerengine.h"
26 #include "contactmanagerengine_p.h"
27 #include "displaylabelgenerator.h"
28 #include "tasks.h"
29
30 #include "contactfetchworker.h"
31 #include "contactfetchbyidworker.h"
32 #include "contactidfetchworker.h"
33 #include "contactmergeworker.h"
34 #include "detaildefinitionfetchworker.h"
35 #include "relationshipfetchworker.h"
36 #include "relationshipremoveworker.h"
37 #include "relationshipsaveworker.h"
38 #include "unmergeimcontactsworker.h"
39
40 #include <dao/querybuilder.h>
41 #include <lib/constants.h>
42 #include <lib/contactmergerequest.h>
43 #include <lib/customdetails.h>
44 #include <lib/garbagecollector.h>
45 #include <lib/presenceutils.h>
46 #include <lib/queue.h>
47 #include <lib/settings.h>
48 #include <lib/threadutils.h>
49 #include <lib/sparqlresolver.h>
50 #include <lib/threadlocaldata.h>
51 #include <lib/trackerchangelistener.h>
52 #include <lib/unmergeimcontactsrequest.h>
53
54 #ifdef ENABLE_CREDENTIALS
55 #include <CredentialsManager>
56 #endif // ENABLE_CREDENTIALS
57
58 ////////////////////////////////////////////////////////////////////////////////////////////////////
59
60 CUBI_USE_NAMESPACE_RESOURCES
61 QTM_USE_NAMESPACE
62
63 ////////////////////////////////////////////////////////////////////////////////////////////////////
64
65 #define REPORT_UNSUPPORTED_FUNCTION(error) do {\
66     qctWarn(QString::fromLatin1("Method not implemented yet: %1"). \
67             arg(QLatin1String(Q_FUNC_INFO))); \
68     qctPropagate(QContactManager::NotSupportedError, error); \
69 } while(0)
70
71 ////////////////////////////////////////////////////////////////////////////////////////////////////
72
73 typedef QPair<QContactAvatar, int> QctAvatarWithAvailability;
74
75 ////////////////////////////////////////////////////////////////////////////////////////////////////
76
77 static QContactManager::Error *const noErrorResult = 0;
78 const QString QctContactManagerEngine::DefaultSyncTarget = QContactSyncTarget__SyncTargetAddressBook;
79 const QStringList QctContactManagerEngine::DefaultWeakSyncTargets = QStringList() << QContactSyncTarget__SyncTargetTelepathy;
80
81 ////////////////////////////////////////////////////////////////////////////////////////////////////
82
83 // added here as list was not working because of
84 // [warn ] QObject::connect: Cannot queue arguments of type 'QContactAbstractRequest::State'
85 // (Make sure 'QContactAbstractRequest::State' is registered using qRegisterMetaType().)
86 Q_DECLARE_METATYPE(QContactAbstractRequest::State)
87
88 ////////////////////////////////////////////////////////////////////////////////////////////////////
89 // Instances which manage contact engine instances
90 ////////////////////////////////////////////////////////////////////////////////////////////////////
91
92 QctContactManagerEngine::QctContactManagerEngine(const QMap<QString, QString> &parameters,
93                                                  const QString &managerName, int interfaceVersion,
94                                                  QObject *parent)
95     : d(new QctContactManagerEngineData(parameters, managerName, interfaceVersion))
96 {
97     // workaround for Qt type system madness
98     qRegisterMetaType<QContactAbstractRequest::State>();
99
100     // workaround for QTMOBILITY-1526
101     if (0 != parent) {
102         setParent(parent);
103     }
104
105     connectSignals();
106     registerGcQuery();
107 }
108
109 QctContactManagerEngine::QctContactManagerEngine(const QctContactManagerEngine &other)
110     : d(other.d)
111 {
112     d.detach();
113     connectSignals();
114 }
115
116 QctContactManagerEngine::~QctContactManagerEngine()
117 {
118     disconnectSignals();
119 }
120
121 QctContactManagerEngine&
122 QctContactManagerEngine::operator=(const QctContactManagerEngine &other)
123 {
124     disconnectSignals();
125
126     d = other.d;
127     d.detach();
128
129     connectSignals();
130
131     return *this;
132 }
133
134 ////////////////////////////////////////////////////////////////////////////////////////////////////
135 // Methods which describe the contact engine's capabilities
136 ////////////////////////////////////////////////////////////////////////////////////////////////////
137
138 QMap<QString, QString>
139 QctContactManagerEngine::managerParameters() const
140 {
141     // Manager parameters appear within the contact manager's URI. The set of parameters exposed
142     // in the manager URI must be minimal since the manager URI becomes part of each contact id.
143     // Exposing too many parameters would cause the same contact having a different contact id
144     // when read from different contact manager instances with slightly different settings like
145     // different timeouts or change listener modes.
146     return d->m_parameters.m_managerParameters;
147 }
148
149 QString
150 QctContactManagerEngine::managerName() const
151 {
152     return d->m_parameters.m_engineName;
153 }
154
155 int
156 QctContactManagerEngine::managerVersion() const
157 {
158     return d->m_parameters.m_engineVersion;
159 }
160
161 bool
162 QctContactManagerEngine::hasFeature(QContactManager::ManagerFeature feature,
163                                     const QString &contactType) const
164 {
165     if (not supportedContactTypes().contains(contactType)) {
166         return false;
167     }
168
169     switch (feature) {
170     case QContactManager::Anonymous:
171     case QContactManager::ChangeLogs:
172         return true;
173
174     case QContactManager::Groups:
175     case QContactManager::Relationships:
176         return supportedContactTypes().contains(QContactType::TypeGroup);
177
178     default:
179         return false;
180     }
181 }
182
183 bool
184 QctContactManagerEngine::isFilterSupported(const QContactFilter &filter) const
185 {
186     return QctQueryBuilder::isFilterSupported(filter);
187 }
188
189 QList<QVariant::Type>
190 QctContactManagerEngine::supportedDataTypes() const
191 {
192     if (d->m_supportedDataTypes.isEmpty()) {
193         QSet<QVariant::Type> typeSet;
194
195         foreach(const QctContactDetailSchema &schema, schemas()) {
196             typeSet += schema.supportedDataTypes();
197         }
198
199         d->m_supportedDataTypes = typeSet.toList();
200     }
201
202     return d->m_supportedDataTypes;
203 }
204
205 QStringList
206 QctContactManagerEngine::supportedContactTypes() const
207 {
208     return schemas().keys();
209 }
210
211 QContactDetailDefinitionMap
212 QctContactManagerEngine::detailDefinitions(const QString &contactType,
213                                            QContactManager::Error *error) const
214 {
215     // Check if the requested contact type is known.
216     const QctContactDetailSchemaMap::ConstIterator schema = schemas().find(contactType);
217
218     if (schema == schemas().constEnd()) {
219         qctPropagate(QContactManager::InvalidContactTypeError, error);
220         return QContactDetailDefinitionMap();
221     }
222
223     // List definitions of RDF detail schema.
224     QContactDetailDefinitionMap definitions = schema->detailDefinitions();
225
226     // Find the definition in custom detail schema.
227     const QctCustomContactDetailMap::ConstIterator contactTypeDetails
228             = d->m_customDetails.find(contactType);
229
230     if (contactTypeDetails != d->m_customDetails.constEnd()) {
231         for(QContactDetailDefinitionMap::ConstIterator it = contactTypeDetails->constBegin();
232             it != contactTypeDetails->constEnd(); ++it) {
233             definitions.insert(it.key(), it.value());
234         }
235     }
236
237     // Report the mangled result.
238     qctPropagate(QContactManager::NoError, error);
239     return definitions;
240 }
241
242 QContactDetailDefinition
243 QctContactManagerEngine::detailDefinition(const QString &definitionName,
244                                           const QString &contactType,
245                                           QContactManager::Error *error) const
246 {
247     // Check if the requested contact type is known.
248     const QctContactDetailSchemaMap::ConstIterator schema = schemas().find(contactType);
249
250     if (schema == schemas().constEnd()) {
251         qctPropagate(QContactManager::InvalidContactTypeError, error);
252         return QContactDetailDefinition();
253     }
254
255     // Find the the definition in RDF detail schema.
256     const QContactDetailDefinitionMap &definitions = schema->detailDefinitions();
257     const QContactDetailDefinitionMap::ConstIterator detail = definitions.find(definitionName);
258
259     if (definitions.constEnd() != detail) {
260         qctPropagate(QContactManager::NoError, error);
261         return detail.value();
262     }
263
264     // Find the definition in custom detail schema.
265     const QctCustomContactDetailMap::ConstIterator contactTypeDetails
266             = d->m_customDetails.find(contactType);
267
268     if (contactTypeDetails != d->m_customDetails.constEnd()) {
269         const QContactDetailDefinitionMap::ConstIterator customDetail
270                 = contactTypeDetails->find(definitionName);
271
272         if (customDetail != contactTypeDetails->constEnd()) {
273             qctPropagate(QContactManager::NoError, error);
274             return customDetail.value();
275         }
276     }
277
278     // Nothing found. Bad luck.
279     qctPropagate(QContactManager::DoesNotExistError, error);
280     return QContactDetailDefinition();
281 }
282
283 bool
284 QctContactManagerEngine::saveDetailDefinition(const QContactDetailDefinition &definition,
285                                               const QString &contactType,
286                                               QContactManager::Error *error)
287 {
288     // Check if the requested contact type is known.
289     const QctContactDetailSchemaMap::ConstIterator schema = schemas().find(contactType);
290
291     if (schema == schemas().constEnd()) {
292         qctPropagate(QContactManager::InvalidContactTypeError, error);
293         return false;
294     }
295
296     // Check if the definition would overwrite a schema definition.
297     // They are fixed and cannot be overwritten since the ontology is fixed.
298     const QContactDetailDefinitionMap &definitions = schema->detailDefinitions();
299     const QContactDetailDefinitionMap::ConstIterator detail = definitions.find(definition.name());
300
301     if (detail != definitions.constEnd()) {
302         qctPropagate(QContactManager::PermissionsError, error);
303         return false;
304     }
305
306     // Store the custom detail definition. Note: We don't persist custom details right now.
307     d->m_customDetails[contactType].insert(definition.name(), definition);
308     qctPropagate(QContactManager::NoError, error);
309     return true;
310 }
311
312 bool
313 QctContactManagerEngine::removeDetailDefinition(const QString &definitionName,
314                                                 const QString &contactType,
315                                                 QContactManager::Error *error)
316 {
317     // Check if the requested contact type is known.
318     const QctContactDetailSchemaMap::ConstIterator schema = schemas().find(contactType);
319
320     if (schema == schemas().constEnd()) {
321         qctPropagate(QContactManager::InvalidContactTypeError, error);
322         return false;
323     }
324
325     // Check if the definition would overwrite a schema definition.
326     // They are fixed and cannot be overwritten since the ontology is fixed.
327     const QContactDetailDefinitionMap &definitions = schema->detailDefinitions();
328     const QContactDetailDefinitionMap::ConstIterator detail = definitions.find(definitionName);
329
330     if (detail != definitions.constEnd()) {
331         qctPropagate(QContactManager::PermissionsError, error);
332         return false;
333     }
334
335     // Remove the custom detail definition. Note: We don't persist custom details right now.
336     const QctCustomContactDetailMap::Iterator customDetailMap = d->m_customDetails.find(contactType);
337
338     if (customDetailMap == d->m_customDetails.constEnd() ||
339         customDetailMap->remove(definitionName) == 0) {
340         qctPropagate(QContactManager::DoesNotExistError, error);
341         return true;
342     }
343
344     qctPropagate(QContactManager::NoError, error);
345     return true;
346 }
347
348 const QctContactDetailSchemaMap &
349 QctContactManagerEngine::schemas() const
350 {
351     return d->m_parameters.m_detailSchemas;
352 }
353
354 const QctContactDetailSchema &
355 QctContactManagerEngine::schema(const QString &contactType) const
356 {
357     QctContactDetailSchemaMap::ConstIterator schema = schemas().find(contactType);
358
359     if (schema == schemas().constEnd()) {
360         qctFail(QString::fromLatin1("Unexpected contact type %1. Aborting.").arg(contactType));
361         schema = schemas().constBegin();
362     }
363
364     return schema.value();
365 }
366
367 bool
368 QctContactManagerEngine::setSelfContactId(const QContactLocalId&,
369                                           QContactManager::Error *error)
370 {
371     REPORT_UNSUPPORTED_FUNCTION(error);
372     return false;
373 }
374
375 QContactLocalId
376 QctContactManagerEngine::selfContactId(QContactManager::Error *error) const
377 {
378     if (0 == d->m_selfContactId) {
379         QctTrackerIdResolver resolver(QStringList() << nco::default_contact_me::iri());
380
381         if (resolver.lookupAndWait()) {
382             d->m_selfContactId = resolver.trackerIds().first();
383         }
384     }
385
386     qctPropagate(d->m_selfContactId ? QContactManager::NoError
387                                     : QContactManager::DoesNotExistError, error);
388
389     return d->m_selfContactId;
390 }
391
392 const QctContactManagerEngine::DebugFlags &
393 QctContactManagerEngine::debugFlags() const
394 {
395     return d->m_parameters.m_debugFlags;
396 }
397
398 bool
399 QctContactManagerEngine::hasDebugFlag(DebugFlag flag) const
400 {
401     return debugFlags().testFlag(flag);
402 }
403
404 int
405 QctContactManagerEngine::concurrencyLevel() const
406 {
407     return d->m_parameters.m_concurrencyLevel;
408 }
409
410 int
411 QctContactManagerEngine::batchSize() const
412 {
413     return d->m_parameters.m_batchSize;
414 }
415
416 int
417 QctContactManagerEngine::gcLimit() const
418 {
419     return d->m_parameters.m_gcLimit;
420 }
421
422 int
423 QctContactManagerEngine::requestTimeout() const
424 {
425     return d->m_parameters.m_requestTimeout;
426 }
427
428 int
429 QctContactManagerEngine::trackerTimeout() const
430 {
431     return d->m_parameters.m_trackerTimeout;
432 }
433
434 int
435 QctContactManagerEngine::coalescingDelay() const
436 {
437     return d->m_parameters.m_coalescingDelay;
438 }
439
440 QctGuidAlgorithm &
441 QctContactManagerEngine::guidAlgorithm() const
442 {
443     return *d->m_parameters.m_guidAlgorithm;
444 }
445
446 const QString &
447 QctContactManagerEngine::syncTarget() const
448 {
449     return d->m_parameters.m_syncTarget;
450 }
451
452 const QStringList &
453 QctContactManagerEngine::weakSyncTargets() const
454 {
455     return d->m_parameters.m_weakSyncTargets;
456 }
457
458 bool
459 QctContactManagerEngine::mangleAllSyncTargets() const
460 {
461     return d->m_parameters.m_mangleAllSyncTargets;
462 }
463
464 bool
465 QctContactManagerEngine::isRestrictive() const
466 {
467     return d->m_parameters.m_isRestrictive;
468 }
469
470 Cubi::Options::SparqlOptions
471 QctContactManagerEngine::selectQueryOptions() const
472 {
473     return d->m_parameters.m_selectQueryOptions;
474 }
475
476 Cubi::Options::SparqlOptions
477 QctContactManagerEngine::updateQueryOptions() const
478 {
479     return d->m_parameters.m_updateQueryOptions;
480 }
481
482 ////////////////////////////////////////////////////////////////////////////////////////////////////
483 // Synchronous data access methods
484 ////////////////////////////////////////////////////////////////////////////////////////////////////
485
486 QList<QContactLocalId>
487 QctContactManagerEngine::contactIds(const QList<QContactSortOrder> &sortOrders,
488                                     QContactManager::Error *error) const
489 {
490     return contactIds(QContactFilter(), sortOrders, error);
491 }
492
493 QList<QContactLocalId>
494 QctContactManagerEngine::contactIds(const QContactFilter &filter,
495                                     const QList<QContactSortOrder> &sortOrders,
496                                     QContactManager::Error *error) const
497 {
498     QContactLocalIdFetchRequest request;
499
500     request.setFilter(filter);
501     request.setSorting(sortOrders);
502
503     runSyncRequest(&request, error);
504
505     return request.ids();
506 }
507
508 QList<QContact>
509 QctContactManagerEngine::contacts(const QContactFilter &filter,
510                                   const QList<QContactSortOrder> &sortOrders,
511                                   const QContactFetchHint &fetchHint,
512                                   QContactManager::Error *error) const
513 {
514     QContactFetchRequest request;
515
516     request.setFetchHint(fetchHint);
517     request.setFilter(filter);
518     request.setSorting(sortOrders);
519
520     runSyncRequest(&request, error);
521
522     return request.contacts();
523 }
524
525 QList<QContact>
526 QctContactManagerEngine::contacts(const QList<QContactLocalId> &localIds,
527                                   const QContactFetchHint &fetchHint,
528                                   ErrorMap *errorMap,
529                                   QContactManager::Error *error) const
530 {
531     QContactFetchByIdRequest request;
532
533     request.setLocalIds(localIds);
534     request.setFetchHint(fetchHint);
535
536     runSyncRequest(&request, error);
537
538     qctPropagate(request.errorMap(), errorMap);
539
540     return request.contacts();
541 }
542
543 QContact
544 QctContactManagerEngine::contact(const QContactLocalId &contactId,
545                                  const QContactFetchHint &fetchHint,
546                                  QContactManager::Error *error) const
547 {
548     static bool warningNotShownYet = true;
549
550     if (warningNotShownYet) {
551         if (not hasDebugFlag(SkipNagging)) {
552             qctWarn(QString::fromLatin1("\n"
553                     "=============================================================================\n"
554                     "WARNING /!\\ - AVOID CALLING THIS FUNCTION FROM PRODUCTION CODE!!!\n"
555                     "=============================================================================\n"
556                     "QContactManager::contact() is blocking on D-Bus roundtrip while accessing\n"
557                     "tracker. Please consider using batch API (QContactManager::contacts()),\n"
558                     "or even better use the asynchronous QContactFetchRequest API, instead of\n"
559                     "fetching contacts one by one.\n"
560                     "\n"
561                     "Please note that reading 100 ids and 100 contact by ids is ~100 times\n"
562                     "slower than reading 100 contacts at once with QContactFetchRequest.\n"
563                     "\n"
564                     "Offending application is %1 [%2].\n"
565                     "=============================================================================").
566                     arg(QCoreApplication::applicationFilePath(),
567                         QString::number(QCoreApplication::applicationPid())));
568         }
569
570         warningNotShownYet = false;
571     }
572
573     return contactImpl(contactId, fetchHint, error);
574 }
575
576 // Used in tests, removed warning while decided if to provide sync api.
577 // Until then customers are advised to use async.
578 QContact
579 QctContactManagerEngine::contactImpl(const QContactLocalId &contactId,
580                                      const QContactFetchHint &fetchHint,
581                                      QContactManager::Error *error) const
582 {
583     QContactLocalIdFilter idFilter;
584     idFilter.setIds(QList<QContactLocalId>() << contactId);
585
586     QContactFetchRequest request;
587     request.setFetchHint(fetchHint);
588     request.setFilter(idFilter);
589
590     if (not runSyncRequest(&request, error)) {
591         return QContact();
592     }
593
594     QList<QContact> contacts(request.contacts());
595
596     if (contacts.isEmpty()) {
597         qctPropagate(QContactManager::DoesNotExistError, error);
598         return QContact();
599     }
600
601     if (contacts.count() > 1) {
602         qctWarn(QString::fromLatin1("Expected only one contact, but got %1").arg(contacts.count()));
603     }
604
605     return contacts.first();
606 }
607
608 bool
609 QctContactManagerEngine::saveContact(QContact *contact, QContactManager::Error *error)
610 {
611     if (0 == contact) {
612         qctPropagate(QContactManager::BadArgumentError, error);
613         return false;
614     }
615
616     QList<QContact> contactList = QList<QContact>() << *contact;
617     const bool success = saveContacts(&contactList, 0, error);
618
619     if (success) {
620         setContactDisplayLabel(contact, contactList.first().displayLabel());
621         contact->setId(contactList.first().id());
622     }
623
624     return success;
625 }
626
627 bool
628 QctContactManagerEngine::saveContacts(QList<QContact> *contacts,
629                                       ErrorMap *errorMap, QContactManager::Error *error)
630 {
631     return saveContacts(contacts, QStringList(), errorMap, error);
632 }
633
634 bool
635 QctContactManagerEngine::saveContacts(QList<QContact> *contacts,
636                                       const QStringList &definitionMask,
637                                       ErrorMap *errorMap, QContactManager::Error *error)
638 {
639     if (0 == contacts) {
640         qctPropagate(QContactManager::BadArgumentError, error);
641         return false;
642     }
643
644     QContactSaveRequest request;
645     request.setContacts(*contacts);
646     request.setDefinitionMask(definitionMask);
647
648     const bool hasFinished = runSyncRequest(&request, error);
649     qctPropagate(request.errorMap(), errorMap);
650
651     if (not hasFinished) {
652         return false;
653     }
654
655     QList<QContact>::Iterator outputIter = contacts->begin();
656
657     // only update the contact id and the display label, all other updates must be fetched
658     foreach(const QContact &savedContact, request.contacts()) {
659         setContactDisplayLabel(&(*outputIter), savedContact.displayLabel());
660         outputIter->setId(savedContact.id());
661         outputIter++;
662     }
663
664     return QContactManager::NoError == request.error();
665 }
666
667
668 bool
669 QctContactManagerEngine::removeContact(const QContactLocalId &contactId,
670                                        QContactManager::Error *error)
671 {
672     return removeContacts(QList<QContactLocalId>() << contactId, 0, error);
673 }
674
675 bool
676 QctContactManagerEngine::removeContacts(const QList<QContactLocalId> &contactIds,
677                                         ErrorMap *errorMap, QContactManager::Error *error)
678 {
679     QContactRemoveRequest request;
680
681     request.setContactIds(contactIds);
682
683     runSyncRequest(&request, error);
684
685     qctPropagate(request.errorMap(), errorMap);
686
687     return QContactManager::NoError == request.error();
688 }
689
690 QList<QContactRelationship>
691 QctContactManagerEngine::relationships(const QString &relationshipType,
692                                        const QContactId &participantId,
693                                        QContactRelationship::Role role,
694                                        QContactManager::Error *error) const
695 {
696     // Mapping of this call to QContactRelationshipFetchRequest is not directly possible
697     // as QContactRelationshipFetchRequest gets set first and second contact but not
698     // just one participantId and a role.
699     //
700     // So in the case that role is QContactRelationship::Either two separate requests need to be done.
701     // As a contact is not allowed to be on the both side of any relationship this should not result in
702     // duplicates.
703     //
704     // If participantId is the default-constructed QContactId, all relationships of the given type shall
705     // be returned and the role ignored. The first request is used for that.
706
707
708     if (role == QContactRelationship::Either && participantId != QContactId()) {
709         QContactManager::Error internalError;
710         QList<QContactRelationship> result;
711
712         internalError = QContactManager::UnspecifiedError;
713         result = relationships(relationshipType, participantId,
714                                QContactRelationship::First,
715                                &internalError);
716
717         if (internalError != QContactManager::NoError) {
718             qctPropagate(internalError, error);
719             return QList<QContactRelationship>();
720         }
721
722         internalError = QContactManager::UnspecifiedError;
723         result += relationships(relationshipType, participantId,
724                                 QContactRelationship::Second,
725                                 &internalError);
726
727         if (internalError != QContactManager::NoError) {
728             qctPropagate(internalError, error);
729             return QList<QContactRelationship>();
730         }
731
732         return result;
733     }
734
735     QContactRelationshipFetchRequest request;
736
737     request.setRelationshipType(relationshipType);
738
739     switch(role) {
740     case QContactRelationship::First:
741         request.setFirst(participantId);
742         break;
743     case QContactRelationship::Second:
744         request.setSecond(participantId);
745         break;
746     case QContactRelationship::Either:
747         break;
748     }
749
750     runSyncRequest(&request, error);
751     return request.relationships();
752 }
753
754 bool
755 QctContactManagerEngine::saveRelationship(QContactRelationship *relationship,
756                                           QContactManager::Error *error)
757 {
758     if (0 == relationship) {
759         qctPropagate(QContactManager::BadArgumentError, error);
760         return false;
761     }
762
763     QList<QContactRelationship> relationships =
764             QList<QContactRelationship>() << *relationship;
765
766     const bool success = saveRelationships(&relationships, 0, error);
767
768     *relationship = relationships.first();
769
770     return success;
771 }
772
773 bool
774 QctContactManagerEngine::saveRelationships(QList<QContactRelationship> *relationships,
775                                            ErrorMap *errorMap, QContactManager::Error *error)
776 {
777     if (0 == relationships) {
778         qctPropagate(QContactManager::BadArgumentError, error);
779         return false;
780     }
781
782     QContactRelationshipSaveRequest request;
783     request.setRelationships(*relationships);
784
785     runSyncRequest(&request, error);
786
787     *relationships = request.relationships();
788     qctPropagate(request.errorMap(), errorMap);
789
790     return QContactManager::NoError == request.error();
791 }
792
793 bool
794 QctContactManagerEngine::removeRelationship(const QContactRelationship &relationship,
795                                             QContactManager::Error *error)
796 {
797     return removeRelationships(QList<QContactRelationship>() << relationship, 0, error);
798 }
799
800 bool
801 QctContactManagerEngine::removeRelationships(const QList<QContactRelationship> &relationships,
802                                              ErrorMap *errorMap, QContactManager::Error *error)
803 {
804     QContactRelationshipRemoveRequest request;
805     request.setRelationships(relationships);
806
807     runSyncRequest(&request, error);
808     qctPropagate(request.errorMap(), errorMap);
809
810     return QContactManager::NoError == request.error();
811 }
812
813 /// returns the DisplayNameDetailList for the name order given, if found.
814 /// Default is the one for QContactDisplayLabel__FirstNameLastNameOrder.
815 static const QList<QctDisplayLabelGenerator> &
816 findDisplayNameGenerators(const QString &nameOrder)
817 {
818     const QctSettings *const settings = QctThreadLocalData::instance()->settings();
819
820     QctDisplayLabelGenerator::ListOptions options = 0;
821
822     if (nameOrder == QContactDisplayLabel__FieldOrderFirstName) {
823         options |= QctDisplayLabelGenerator::FirstNameLastName;
824     } else if (nameOrder == QContactDisplayLabel__FieldOrderLastName) {
825         options |= QctDisplayLabelGenerator::LastNameFirstName;
826     } else {
827         if (nameOrder != settings->nameOrder()) {
828             // use configured name-order setting when an unknown value was passed
829             return findDisplayNameGenerators(settings->nameOrder());
830         }
831
832         // use a default value when the name-order setting itself is wrong
833         return findDisplayNameGenerators(QctSettings::DefaultNameOrder);
834     }
835
836     if (settings->preferNickname()) {
837         options |= QctDisplayLabelGenerator::PreferNickname;
838     }
839
840     return QctDisplayLabelGenerator::generators(options);
841 }
842
843 QContactFetchHint
844 QctContactManagerEngine::normalizedFetchHint(QContactFetchHint fetchHint, const QString &nameOrder)
845 {
846     QStringList detailDefinitionHint = fetchHint.detailDefinitionsHint();
847
848     // Make sure the display name can be synthesized when needed
849     if (detailDefinitionHint.contains(QContactDisplayLabel::DefinitionName)) {
850         foreach(const QctDisplayLabelGenerator &generator, findDisplayNameGenerators(nameOrder)) {
851             if (not detailDefinitionHint.contains(generator.requiredDetailName())) {
852                 detailDefinitionHint += generator.requiredDetailName();
853             }
854         }
855
856         fetchHint.setDetailDefinitionsHint(detailDefinitionHint);
857     }
858
859     return fetchHint;
860 }
861
862 void
863 QctContactManagerEngine::updateDisplayLabel(QContact &contact,
864                                             const QString &nameOrder) const
865 {
866     setContactDisplayLabel(&contact, createDisplayLabel(contact, nameOrder));
867 }
868
869 QString
870 QctContactManagerEngine::createDisplayLabel(const QContact &contact,
871                                             const QString &nameOrder) const
872 {
873     QString label;
874
875     foreach(const QctDisplayLabelGenerator &generator, findDisplayNameGenerators(nameOrder)) {
876         label = generator.createDisplayLabel(contact);
877
878         if (not label.isEmpty()) {
879             break;
880         }
881     }
882
883     return label;
884 }
885
886 template <class T> static void
887 transfer(const T &key, const QContactDetail &source, QContactDetail &target)
888 {
889     QVariant value(source.variantValue(key));
890
891     if (not value.isNull()) {
892         target.setValue(key, value);
893     }
894 }
895
896 inline bool
897 operator<(const QctAvatarWithAvailability &a, const QctAvatarWithAvailability &b)
898 {
899     if (a.second != b.second) {
900         // the lower the availability rank, the more significant the detail
901         return a.second > b.second;
902     }
903
904     const QString accountA = a.first.linkedDetailUris().first();
905     const QString accountB = b.first.linkedDetailUris().first();
906
907     if (accountA != accountB) {
908         return accountA < accountB;
909     }
910
911     const QString subTypeA = a.first.value(QContactAvatar__FieldSubType);
912     const QString subTypeB = b.first.value(QContactAvatar__FieldSubType);
913
914     if (subTypeA != subTypeB) {
915         return subTypeA < subTypeB;
916     }
917
918     return a.first.imageUrl() < b.first.imageUrl();
919 }
920
921 template<int N> static void
922 copyFieldValue(QContactDetail &dst, const QContactDetail &src, const QLatin1Constant<N>& key)
923 {
924     dst.setValue(key, src.variantValue(key));
925 }
926
927 void
928 QctContactManagerEngine::updateAvatar(QContact &contact)
929 {
930     QContactPersonalAvatar personalAvatar = contact.detail<QContactPersonalAvatar>();
931
932     const QList<QContactDetail> currentOnlineAvatars
933             = contact.details(QContactOnlineAvatar::DefinitionName)
934             + contact.details(QContactSocialAvatar::DefinitionName);
935
936     // Function was triggered by an update of nco:photo
937     if (not personalAvatar.isEmpty()) {
938         QContactAvatar photoAvatar = personalAvatar.toAvatar();
939
940         // If there was already a nco:photo (no linked uri), remove it
941         contact.removeDetail(&personalAvatar);
942
943         // nco:photo always goes first, so that QContact::detail<QContactAvatar> returns it
944         contact.saveDetail(&photoAvatar);
945     }
946
947     // Function was triggered by an update of a IMAddress' imAvatar
948     if (not currentOnlineAvatars.empty()) {
949         // find all available presence details
950         QMap<QString, QContactPresence> presenceDetails;
951
952         foreach(const QContactPresence &detail, contact.details<QContactPresence>()) {
953             const QString accountPath = parsePresenceIri(detail.detailUri());
954
955             if (not accountPath.isEmpty()) {
956                 presenceDetails.insert(accountPath, detail);
957             }
958         }
959
960         // store online avatars with their availablity
961         QList<QctAvatarWithAvailability> avatarsWithAvailability;
962
963         foreach(QContactDetail onlineAvatar, currentOnlineAvatars) {
964             contact.removeDetail(&onlineAvatar);
965
966             // FIXME: properly define schema for detailUri and linkedDetailUri
967             QStringList linkedDetailUris = onlineAvatar.linkedDetailUris();
968
969             if (linkedDetailUris.isEmpty()) {
970                 linkedDetailUris += QString();
971             }
972
973             foreach(const QString &uri, linkedDetailUris) {
974                 const QString accountPath = not uri.isEmpty()
975                         ? parseTelepathyIri(uri)
976                         : QString();
977
978                 int availability = qctAvailabilityRank(QContactPresence::PresenceUnknown);
979
980                 if (not accountPath.isEmpty()) {
981                     const QContactPresence presence = presenceDetails.value(accountPath);
982                     availability = qctAvailabilityRank(presence.presenceState());
983                 } else {
984                     qctWarn("QctContactOnlineAvatar should always be linked with an online account");
985                 }
986
987                 QContactAvatar avatarDetail;
988
989                 copyFieldValue(avatarDetail, onlineAvatar, QContactAvatar::FieldLinkedDetailUris);
990                 copyFieldValue(avatarDetail, onlineAvatar, QContactAvatar::FieldImageUrl);
991                 copyFieldValue(avatarDetail, onlineAvatar, QContactAvatar__FieldSubType);
992
993                 avatarsWithAvailability += qMakePair(avatarDetail, availability);
994             }
995         }
996
997         // sort avatars by their availablity
998         qSort(avatarsWithAvailability);
999
1000         // add regular avatar details to contact
1001         for(int i = 0; i < avatarsWithAvailability.count(); ++i) {
1002             contact.saveDetail(&avatarsWithAvailability[i].first);
1003         }
1004     }
1005 }
1006
1007 bool
1008 QctContactManagerEngine::isWeakSyncTarget(const QString &syncTarget) const
1009 {
1010     return mangleAllSyncTargets() || weakSyncTargets().contains(syncTarget);
1011 }
1012
1013 QString
1014 QctContactManagerEngine::gcQueryId() const
1015 {
1016     return d->m_gcQueryId;
1017 }
1018
1019 // TODO: we could cache the return value in this function
1020 QString
1021 QctContactManagerEngine::cleanupQueryString() const
1022 {
1023     // Notice FILTER in WHERE clause - garbage collector needs to verify parenting outside this graph too.
1024     // reason - IMAccount hasIMAddress in contactsd private graph so no need to delete it. The same could apply
1025     // to any other property - it could have been added to parent in some other graph.
1026     // Camera geotagging creates addresses outside qct graph and have no "parent" contact pointing
1027     // to them through nco:hasAddress
1028     static const QString obsoleteResourcesPrefix = QLatin1String
1029             ("\n"
1030              "DELETE\n"
1031              "{\n"
1032              "  GRAPH <%1>\n"
1033              "  {\n"
1034              "    ?resource a <%2> .\n"
1035              "  }\n"
1036              "}\n"
1037              "WHERE\n"
1038              "{\n"
1039              "  GRAPH <%1>\n"
1040              "  {\n"
1041              "    ?resource a <%3> .\n"
1042              "  }\n");
1043     static const QString imAccountStopPattern = QLatin1String
1044             ("    FILTER(NOT EXISTS { ?resource a nco:IMAccount }) .\n");
1045     static const QString obsoleteResourcesPattern = QLatin1String
1046             ("    FILTER(NOT EXISTS { ?parent <%1> ?resource }) .\n");
1047     static const QString obsoleteResourcesSuffix = QLatin1String
1048             ("}\n");
1049
1050     QString queryString;
1051
1052 #ifndef QT_NO_DEBUG
1053     queryString += QLatin1String
1054             ("\n"
1055              "#--------------------------------------.\n"
1056              "# Drop drop obsolete custom properties |\n"
1057              "#--------------------------------------'\n");
1058 #endif // QT_NO_DEBUG
1059
1060     // collect obsolete resource types and their predicates
1061     typedef QMap<QString, bool> PredicateMap;
1062     typedef QMap<QString, PredicateMap> ResourceTypePredicateMap;
1063     ResourceTypePredicateMap obsoleteResources;
1064
1065     foreach(const QctContactDetailSchema &schema, schemas()) {
1066         foreach(const QctContactDetail &detail, schema.details()) {
1067             foreach(const QctPropertyInfoList &chain, detail.possessedChains()) {
1068                 const QctPropertyInfoBase &pi = chain.last();
1069
1070                 if (pi.rangeIri() == rdfs::Resource::iri()) {
1071                     qctWarn(QString::fromLatin1("Not cleaning up obsolete resources for %1 property"
1072                                                 "since the property's range is too generic (%2).").
1073                             arg(qctIriAlias(pi.iri()), qctIriAlias(pi.rangeIri())));
1074                     continue;
1075                 }
1076
1077                 obsoleteResources[pi.rangeIri()].insert(pi.iri(), true);
1078             }
1079         }
1080     }
1081
1082     // glue everything together
1083     queryString += obsoleteResourcesPrefix.arg(QtContactsTrackerDefaultGraphIri,
1084                                                nao::Property::iri(),
1085                                                nao::Property::iri());
1086     queryString += obsoleteResourcesPattern.arg(nao::hasProperty::iri());
1087     queryString += obsoleteResourcesSuffix;
1088
1089     for(ResourceTypePredicateMap::ConstIterator
1090         t = obsoleteResources.constBegin(); t != obsoleteResources.constEnd(); ++t) {
1091         queryString += obsoleteResourcesPrefix.arg(QtContactsTrackerDefaultGraphIri,
1092                                                    rdfs::Resource::iri(),
1093                                                    t.key());
1094
1095         if (nco::IMAddress::iri() == t.key()) {
1096             // FIXME: Remove this workaround for NB#206404 - Saving a contact using
1097             // qtcontacts-tracker causes nco:IMAccount to be removed.
1098             queryString += imAccountStopPattern;
1099         }
1100
1101         foreach(const QString &predicate, t.value().keys()) {
1102             queryString += obsoleteResourcesPattern.arg(predicate);
1103         }
1104
1105         queryString += obsoleteResourcesSuffix;
1106     }
1107
1108     return queryString;
1109 }
1110
1111 void
1112 QctContactManagerEngine::enqueueTask(QctTask *task)
1113 {
1114     d->m_queue->enqueue(task);
1115 }
1116
1117 ////////////////////////////////////////////////////////////////////////////////////////////////////
1118 // Asynchronous data access methods
1119 ////////////////////////////////////////////////////////////////////////////////////////////////////
1120
1121 bool
1122 QctContactManagerEngine::checkSecurityTokens(QContactAbstractRequest *request)
1123 {
1124     // Plugin users often fail to provide all required security tokens.
1125     // Therefore we print warnings to educate them. If the security checks
1126     // should cause trouble they can be temporarly disabled by exporting
1127     // QT_CONTACTS_TRACKER="debug=no-sec".
1128
1129     if (d->m_mandatoryTokensFound) {
1130         return true;
1131     }
1132
1133     QStringList theGateKeepersBlameList;
1134     QStringList missingOptionalTokens;
1135
1136 #ifdef ENABLE_CREDENTIALS
1137
1138     static const QStringList requiredSecurityTokens =
1139             QStringList() << QLatin1String("TrackerReadAccess")
1140                           << QLatin1String("TrackerWriteAccess");
1141
1142     static const QStringList optionalSecurityTokens =
1143             QStringList() << QLatin1String("GRP::metadata-users");
1144
1145     foreach(const QString &credential, requiredSecurityTokens) {
1146         QString errorMessage;
1147
1148         if (not MssfQt::CredentialsManager::hasProcessCredential(0, credential, &errorMessage)) {
1149             theGateKeepersBlameList += credential;
1150             qctWarn(errorMessage);
1151         }
1152     }
1153
1154     foreach(const QString &credential, optionalSecurityTokens) {
1155         QString errorMessage;
1156
1157         if (not MssfQt::CredentialsManager::hasProcessCredential(0, credential, &errorMessage)) {
1158             missingOptionalTokens += credential;
1159             qctWarn(errorMessage);
1160         }
1161     }
1162
1163 #endif // ENABLE_CREDENTIALS
1164
1165     if (not theGateKeepersBlameList.isEmpty()) {
1166         qctWarn(QString::fromLatin1("\n"
1167                  "=============================================================================\n"
1168                  "WARNING /!\\ - MANDATORY SECURITY TOKENS ARE MISSING\n"
1169                  "=============================================================================\n"
1170                  "Rejecting %2.\n"
1171                  "Please add an AEGIS manifest to your package requesting the following\n"
1172                  "security tokens: %1 for %3 [%4].\n"
1173                  "=============================================================================").
1174                 arg(theGateKeepersBlameList.join(QLatin1String(", ")),
1175                     QLatin1String(request->metaObject()->className()),
1176                     QCoreApplication::applicationFilePath(),
1177                     QString::number(QCoreApplication::applicationPid())));
1178
1179         return false;
1180     }
1181
1182     if (not missingOptionalTokens.isEmpty()) {
1183         qctWarn(QString::fromLatin1("\n"
1184                  "=============================================================================\n"
1185                  "WARNING /!\\ - OPTIONAL SECURITY TOKENS ARE MISSING\n"
1186                  "=============================================================================\n"
1187                  "Full functionality like tracker direct access is not available.\n"
1188                  "Please add an AEGIS manifest to your package requesting the following\n"
1189                  "security tokens: %1 for %2 [%3].\n"
1190                  "=============================================================================").
1191                 arg(missingOptionalTokens.join(QLatin1String(", ")),
1192                     QCoreApplication::applicationFilePath(),
1193                     QString::number(QCoreApplication::applicationPid())));
1194     }
1195
1196     d->m_mandatoryTokensFound = true;
1197
1198     return true;
1199 }
1200
1201 QctTask *
1202 QctContactManagerEngine::startRequestImpl(QContactAbstractRequest *request)
1203 {
1204     QCT_SYNCHRONIZED(&d->m_requestLifeGuard);
1205
1206     QWeakPointer<QContactAbstractRequest> guardedRequest = request;
1207
1208     // The worker puts the request under protection of the engine's request life-guard.
1209     QScopedPointer<QctAbstractWorker> worker(createRequestWorker(guardedRequest.data()));
1210
1211     if (worker.isNull()) {
1212         return 0;
1213     }
1214
1215     QScopedPointer<QctRequestTask> task(new QctRequestTask(guardedRequest.data(), worker.take()));
1216
1217     // Check if some evil client managed to delete the request from some other thread.
1218     if (guardedRequest.isNull()) {
1219         return 0;
1220     }
1221
1222     // Make sure mandatory tracker:ids have been resolved.
1223     if (task->dependencies().testFlag(QctAbstractWorker::ResourceCache) &&
1224         not d->m_satisfiedDependencies.testFlag(QctAbstractWorker::ResourceCache)) {
1225         // Queue will take ownership of the task
1226         // We don't explicitly parent the taks when creating them, because this method
1227         // might be called from a thread which is not the one where the engine was created.
1228         // In that case, we would get the "QObject: Cannot create children for a parent that
1229         // is in a different thread" warning, and the parent would be NULL anyway...
1230         enqueueTask(new QctResourceCacheTask(schemas()));
1231         d->m_satisfiedDependencies |= QctAbstractWorker::ResourceCache;
1232     }
1233
1234     // Make sure GUID algorithms are available.
1235     if (task->dependencies().testFlag(QctAbstractWorker::GuidAlgorithm) &&
1236         not d->m_satisfiedDependencies.testFlag(QctAbstractWorker::GuidAlgorithm)) {
1237         // Queue will take ownership of the task
1238         // See above why we don't set the parent here
1239         enqueueTask(new QctGuidAlgorithmTask(this));
1240         d->m_satisfiedDependencies |= QctAbstractWorker::GuidAlgorithm;
1241     }
1242
1243     // The contacts engine must bring requests into activate state as soon as it is dealing
1244     // with them. We cannot queue the task first, since the queue might be empty right now,
1245     // causing the task to start work from inactive state. This would be ridicilous.
1246     updateRequestState(guardedRequest.data(), QContactAbstractRequest::ActiveState);
1247
1248     // Check if some evil client managed to delete the request from some slot (or thread).
1249     if (guardedRequest.isNull()) {
1250         return 0;
1251     }
1252
1253     // Transfer task ownership to the queue.
1254     QctRequestTask *const result = task.data();
1255     d->m_queue->enqueue(task.take());
1256     return result;
1257 }
1258
1259 bool
1260 QctContactManagerEngine::startRequest(QContactAbstractRequest *request)
1261 {
1262     startRequestImpl(request);
1263     return true; // this always works
1264 }
1265
1266 QctAbstractWorker *
1267 QctContactManagerEngine::createRequestWorker(QContactAbstractRequest *request)
1268 {
1269     // about if mandatory security tokens are missing
1270     if (not checkSecurityTokens(request)) {
1271         return 0;
1272     }
1273
1274     // ensure old worker got destroyed when request gets reused
1275     requestDestroyed(request);
1276
1277     QctAbstractWorker *worker = 0;
1278     QElapsedTimer t; t.start();
1279
1280     // choose proper request implementation
1281     switch(request->type())
1282     {
1283     case QContactAbstractRequest::ContactFetchRequest:
1284         worker = new QctContactFetchWorker(request, this);
1285         break;
1286
1287     case QContactAbstractRequest::ContactFetchByIdRequest:
1288         worker = new QctContactFetchByIdWorker(request, this);
1289         break;
1290
1291     case QContactAbstractRequest::ContactLocalIdFetchRequest:
1292         worker = new QctContactIdFetchWorker(request, this);
1293         break;
1294
1295     case QContactAbstractRequest::ContactRemoveRequest:
1296         if (0 == qobject_cast<QctContactMergeRequest *>(request)) {
1297             worker = new QctContactRemoveWorker(request, this);
1298         } else {
1299             worker = new QctContactMergeWorker(request, this);
1300         }
1301         break;
1302
1303     case QContactAbstractRequest::ContactSaveRequest:
1304         if (0 == qobject_cast< QctUnmergeIMContactsRequest *> (request)) {
1305             worker = new QctContactSaveWorker(request, this);
1306         } else {
1307             worker = new QctUnmergeIMContactsWorker(request, this);
1308         }
1309         break;
1310
1311     case QContactAbstractRequest::RelationshipFetchRequest:
1312         worker = new QctRelationshipFetchWorker(request, this);
1313         break;
1314
1315     case QContactAbstractRequest::RelationshipRemoveRequest:
1316         worker = new QctRelationshipRemoveWorker(request, this);
1317         break;
1318
1319     case QContactAbstractRequest::RelationshipSaveRequest:
1320         worker = new QctRelationshipSaveWorker(request, this);
1321         break;
1322
1323     case QContactAbstractRequest::DetailDefinitionFetchRequest:
1324         worker = new QctDetailDefinitionFetchWorker(request, this);
1325         break;
1326
1327     case QContactAbstractRequest::InvalidRequest:
1328     case QContactAbstractRequest::DetailDefinitionRemoveRequest:
1329     case QContactAbstractRequest::DetailDefinitionSaveRequest:
1330         break;
1331     }
1332
1333     if (0 == worker) {
1334         qctWarn(QString::fromLatin1("Unsupported request type: %1").
1335                 arg(QLatin1String(request->metaObject()->className())));
1336         return 0;
1337     }
1338
1339     if (hasDebugFlag(ShowNotes)) {
1340         qDebug() << Q_FUNC_INFO << "time elapsed while constructing request workers:"<< request << t.elapsed();
1341     }
1342
1343     if (hasDebugFlag(ShowNotes)) {
1344         qDebug() << Q_FUNC_INFO << "running" << worker->metaObject()->className();
1345     }
1346
1347     // XXX The unit tests directly access the engine. Therefore requests created by our unit
1348     // tests don't have a manager attached. This prevents ~QContactAbstractRequest() to call our
1349     // engine's requestDestroyed(QContactAbstractRequest*) method, which results in memory leaks
1350     // within our our unit tests. To prevent those leaks we watch the request's destroyed()
1351     // signal and forward those signals to requestDestroyed(QContactAbstractRequest*) when
1352     // a request without contact manager is found.
1353     //
1354     // XXX This all of course is an ugly workaround. We probably should change the unit tests
1355     // to create use QContactManager accessing the engine via a static plugin.
1356     if (0 == request->manager()) {
1357         connect(request, SIGNAL(destroyed(QObject*)),
1358                 this, SLOT(onRequestDestroyed(QObject*)), Qt::DirectConnection);
1359     }
1360
1361     // track original request for this worker
1362     QCT_SYNCHRONIZED_WRITE(&d->m_tableLock);
1363
1364     d->m_workersByRequest.insert(request, worker);
1365     d->m_requestsByWorker.insert(worker, request);
1366
1367     return worker;
1368 }
1369
1370 QctRequestLocker
1371 QctContactManagerEngine::request(const QctAbstractWorker *worker) const
1372 {
1373     QScopedPointer<QMutexLocker> locker(new QMutexLocker(&d->m_requestLifeGuard));
1374     QCT_SYNCHRONIZED_READ(&d->m_tableLock);
1375     return QctRequestLocker(locker.take(), d->m_requestsByWorker.value(worker));
1376 }
1377
1378 void
1379 QctContactManagerEngine::connectSignals()
1380 {
1381     if (d->m_changeListener) {
1382         connect(d->m_changeListener,
1383                 SIGNAL(contactsAdded(QList<QContactLocalId>)),
1384                 SIGNAL(contactsAdded(QList<QContactLocalId>)));
1385         connect(d->m_changeListener,
1386                 SIGNAL(contactsChanged(QList<QContactLocalId>)),
1387                 SIGNAL(contactsChanged(QList<QContactLocalId>)));
1388         connect(d->m_changeListener,
1389                 SIGNAL(contactsRemoved(QList<QContactLocalId>)),
1390                 SIGNAL(contactsRemoved(QList<QContactLocalId>)));
1391         connect(d->m_changeListener,
1392                 SIGNAL(relationshipsAdded(QList<QContactLocalId>)),
1393                 SIGNAL(relationshipsAdded(QList<QContactLocalId>)));
1394         connect(d->m_changeListener,
1395                 SIGNAL(relationshipsRemoved(QList<QContactLocalId>)),
1396                 SIGNAL(relationshipsRemoved(QList<QContactLocalId>)));
1397     }
1398 }
1399
1400 void
1401 QctContactManagerEngine::disconnectSignals()
1402 {
1403     if (d->m_changeListener) {
1404         d->m_changeListener->disconnect(this);
1405         d->m_changeListener = 0;
1406     }
1407 }
1408
1409 void
1410 QctContactManagerEngine::registerGcQuery()
1411 {
1412     d->m_gcQueryId = QString::fromLatin1("com.nokia.qtcontacts-tracker");
1413     QctGarbageCollector::registerQuery(d->m_gcQueryId, cleanupQueryString());
1414 }
1415
1416 void
1417 QctContactManagerEngine::connectNotify(const char *signal)
1418 {
1419     if (0 == d->m_changeListener) {
1420         // Create the change listener on demand since:
1421         //
1422         // 1) Creating the listener is expensive as we must subscribe to some DBus signals.
1423         // 2) Watching DBus without any specific need wastes energy by wakeing up processes.
1424         //
1425         // Share the change listener for the reasons listed above.
1426         //
1427         // Still only share per thread (instead of process) to avoid nasty situations like the
1428         // random, listener owning thread terminating before other threads using the listener.
1429
1430         // Must monitor individual contact classes instead of nco:Contact
1431         // to avoid bogus notifications for:
1432         //
1433         //  - QContactOrganization details, implemented via nco:OrganizationContact
1434         //  - incoming calls which cause call-ui to create temporary(?) nco:Contact instances
1435         //
1436         // Additionally, nco:Contact does not have the tracker:notify property set to true, whereas
1437         // nco:PersonContact and nco:ContactGroup do have the property, so only those classes will
1438         // receive notifications of changes.
1439         QSet<QString> contactClassIris;
1440
1441         foreach (const QctContactDetailSchema &schema, d->m_parameters.m_detailSchemas) {
1442             foreach(const QString &iri, schema.contactClassIris()) {
1443                 if (iri != nco::Contact::iri()) {
1444                     contactClassIris += iri;
1445                 }
1446             }
1447         }
1448
1449         // Compute change filtering mode.
1450         QctTrackerChangeListener::ChangeFilterMode changeFilterMode = QctTrackerChangeListener::AllChanges;
1451
1452         if (d->m_parameters.m_omitPresenceChanges) {
1453             changeFilterMode = QctTrackerChangeListener::IgnorePresenceChanges;
1454         }
1455
1456         // Compute debug flags for the listener.
1457         QctTrackerChangeListener::DebugFlags debugFlags = QctTrackerChangeListener::NoDebug;
1458
1459         if (d->m_parameters.m_debugFlags.testFlag(QctContactManagerEngine::ShowSignals)) {
1460             debugFlags |= QctTrackerChangeListener::PrintSignals;
1461         }
1462
1463         // Compute cache id for the listener.
1464         // Cannot use the manager URI because it doesn't expose all relevant parameters.
1465         const QString listenerId = (QStringList(contactClassIris.toList()) <<
1466                                     QString::number(d->m_parameters.m_coalescingDelay) <<
1467                                     QString::number(changeFilterMode) <<
1468                                     QString::number(debugFlags)).join(QLatin1String(";"));
1469
1470         // Check if there already exists a listener for that computed id.
1471         QctThreadLocalData *const threadLocalData = QctThreadLocalData::instance();
1472         d->m_changeListener = threadLocalData->trackerChangeListener(listenerId);
1473
1474         if (0 == d->m_changeListener) {
1475             // Create new listener when needed.
1476             d->m_changeListener = new QctTrackerChangeListener(contactClassIris.toList(), QThread::currentThread());
1477             d->m_changeListener->setCoalescingDelay(d->m_parameters.m_coalescingDelay);
1478             d->m_changeListener->setChangeFilterMode(changeFilterMode);
1479             d->m_changeListener->setDebugFlags(debugFlags);
1480
1481             threadLocalData->setTrackerChangeListener(listenerId, d->m_changeListener);
1482         }
1483
1484         // Monitor the choosen listener's signals.
1485         connectSignals();
1486     }
1487
1488     QContactManagerEngine::connectNotify(signal);
1489 }
1490
1491 void
1492 QctContactManagerEngine::requestDestroyed(QContactAbstractRequest *req)
1493 {
1494     dropRequest(QctRequestLocker(new QMutexLocker(&d->m_requestLifeGuard), req));
1495 }
1496
1497 void
1498 QctContactManagerEngine::dropRequest(const QctRequestLocker &req)
1499 {
1500     QCT_SYNCHRONIZED_WRITE(&d->m_tableLock);
1501
1502     if (req.isNull()) {
1503         return;
1504     }
1505
1506     QctAbstractWorker *const worker = d->m_workersByRequest.take(req.data());
1507
1508     if (0 != worker) {
1509         d->m_requestsByWorker.remove(worker);
1510     }
1511 }
1512
1513 void
1514 QctContactManagerEngine::onRequestDestroyed(QObject *req)
1515 {
1516     // dynamic_cast<> does not work at this point (has a 0 result, or even crashes) because the
1517     // derived parts of the class have already been destroyed by the time the base QObject's
1518     // destructor has emitted this signal. So we do a regular static case, because
1519     // requestDestroyed(QContactAbstractRequest*) just compares the pointer value anyway.
1520     requestDestroyed(static_cast<QContactAbstractRequest *>(req));
1521 }
1522
1523 bool
1524 QctContactManagerEngine::waitForRequestFinished(QContactAbstractRequest *request, int msecs)
1525 {
1526     if (not hasDebugFlag(SkipNagging)) {
1527         qctWarn(QString::fromLatin1("\n"
1528             "=============================================================================\n"
1529             "WARNING /!\\ - NEVER EVER CALL THIS FUNCTION FROM PRODUCTION CODE!!!\n"
1530             "=============================================================================\n"
1531             "QContactAbstractRequest::waitForFinished(), respectively\n"
1532             "QContactManagerEngine::waitForRequestFinished() must spin your\n"
1533             "application's event loop. Doing so will cause HAVOC AND PAIN for\n"
1534             "any non-trivial program.\n"
1535             "\n"
1536             "So please refactor your asynchronious code, or use the synchronious\n"
1537             "API if blocking your application is acceptable.\n"
1538             "\n"
1539             "WE MEAN IT!!!\n"
1540             "\n"
1541             "Offending application is %1 [%2].\n"
1542             "=============================================================================").
1543             arg(QCoreApplication::applicationFilePath(),
1544                 QString::number(QCoreApplication::applicationPid())));
1545     }
1546
1547     return waitForRequestFinishedImpl(request, msecs);
1548 }
1549
1550 bool
1551 QctContactManagerEngine::waitForRequestFinishedImpl(QContactAbstractRequest *request, int msecs)
1552 {
1553     if (0 == request) {
1554         return false;
1555     }
1556
1557     QctRequestEventLoop eventLoop(request, msecs);
1558
1559     if (not eventLoop.isFinished()) {
1560         eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
1561     }
1562
1563     return eventLoop.isFinished();
1564 }
1565
1566 bool
1567 QctContactManagerEngine::runSyncRequest(QContactAbstractRequest *request,
1568                                         QContactManager::Error *error) const
1569 {
1570     // Copy the engine to prevent forwarding side-effects to the caller's engine.
1571     // It costs a bit, but guess that's a justified penalty to all sync API users.
1572     QctContactManagerEngine taskEngine(*this);
1573
1574     QctTask *const task = taskEngine.startRequestImpl(request);
1575
1576     if (0 != task && not QctTaskWaiter(task).wait(requestTimeout())) {
1577         qctPropagate(QContactManager::UnspecifiedError, error);
1578         return false;
1579     }
1580
1581     qctPropagate(request->error(), error);
1582
1583     return true;
1584 }
1585
1586 ////////////////////////////////////////////////////////////////////////////////////////////////////
1587 // Synthesized contact details
1588 ////////////////////////////////////////////////////////////////////////////////////////////////////
1589
1590 QString
1591 QctContactManagerEngine::synthesizedDisplayLabel(const QContact &contact,
1592                                                  QContactManager::Error *error) const
1593 {
1594     qctPropagate(QContactManager::NoError, error);
1595
1596     return createDisplayLabel(contact);
1597 }
1598
1599 QContact
1600 QctContactManagerEngine::compatibleContact(const QContact &original,
1601                                            QContactManager::Error *error) const
1602 {
1603     QContact contact = original;
1604
1605     foreach(const QctContactDetail &detail, schema(contact.type()).details()) {
1606         QList<QContactDetail> contactDetails = contact.details(detail.name());
1607
1608         if (contactDetails.empty()) {
1609             continue;
1610         }
1611
1612         // Check that detail respects our schema's cardinality
1613         if (not detail.isUnique()) {
1614             continue;
1615         }
1616
1617         if (contactDetails.count() < 2) {
1618             continue;
1619         }
1620
1621         qctWarn(QString::fromLatin1("Dropping odd details: %2 detail must be unique").
1622                 arg(detail.name()));
1623
1624         for(int i = 1; i < contactDetails.count(); ++i) {
1625             contact.removeDetail(&contactDetails[i]);
1626         }
1627     }
1628
1629     // Check fields contents
1630     foreach(const QctContactDetail &detail, schema(contact.type()).details()) {
1631         QList<QContactDetail> contactDetails = contact.details(detail.name());
1632
1633         foreach (QContactDetail contactDetail, contactDetails) {
1634             const QVariantMap detailValues = contactDetail.variantValues();
1635
1636             for (QVariantMap::ConstIterator it = detailValues.constBegin();
1637                  it != detailValues.constEnd(); ++it) {
1638                 const QctContactDetailField *field = detail.field(it.key());
1639
1640                 if (field == 0) {
1641                     // We can't validate custom fields, skip
1642                     continue;
1643                 }
1644
1645                 QVariant computedValue;
1646
1647                 if (not field->makeValue(it.value(), computedValue)) {
1648                     // Apparently this is not an error, we just prune the field...
1649                     // makeValue already prints a debug message if it returns false,
1650                     // so we can stay silent here. No idea how UI can provide any
1651                     // useful feedback out of that.
1652                     contactDetail.removeValue(it.key());
1653                     break;
1654                 }
1655
1656                 // The computed value is only useful to generate SPARQL queries,
1657                 // we should not save it back to the field.
1658             }
1659
1660             contact.saveDetail(&contactDetail);
1661         }
1662     }
1663
1664     qctPropagate(QContactManager::NoError, error);
1665
1666     return contact;
1667 }
1668
1669 ////////////////////////////////////////////////////////////////////////////////////////////////////
1670 // Unsupported functions
1671 ////////////////////////////////////////////////////////////////////////////////////////////////////
1672
1673 bool
1674 QctContactManagerEngine::validateContact(const QContact&, QContactManager::Error *error) const
1675 {
1676     REPORT_UNSUPPORTED_FUNCTION(error);
1677     return false;
1678 }
1679
1680 bool
1681 QctContactManagerEngine::validateDefinition(const QContactDetailDefinition&,
1682                                             QContactManager::Error *error) const
1683 {
1684     REPORT_UNSUPPORTED_FUNCTION(error);
1685     return false;
1686 }
1687
1688 bool
1689 QctContactManagerEngine::cancelRequest(QContactAbstractRequest *request)
1690 {
1691     if (0 == request) {
1692         return false;
1693     }
1694
1695     QCT_SYNCHRONIZED(&d->m_requestLifeGuard);
1696     QCT_SYNCHRONIZED_READ(&d->m_tableLock);
1697
1698     QctAbstractWorker *const worker = d->m_workersByRequest.value(request);
1699
1700     if (0 != worker) {
1701         return worker->cancel();
1702     }
1703     return false;
1704 }
1705
1706 bool
1707 QctContactManagerEngine::isRelationshipTypeSupported(const QString &relationshipType,
1708                                                      const QString &contactType) const
1709 {
1710     if (relationshipType == QContactRelationship::HasMember
1711             && supportedContactTypes().contains(contactType)
1712             && (contactType == QContactType::TypeContact
1713                 || contactType == QContactType::TypeGroup)) {
1714         return true;
1715     }
1716
1717     return false;
1718 }