Changes: Move implementations for contactmanagerengine_p.h to separate .cpp file
[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 void
922 QctContactManagerEngine::updateAvatar(QContact &contact)
923 {
924     QContactPersonalAvatar personalAvatar = contact.detail<QContactPersonalAvatar>();
925
926     const QList<QContactDetail> currentOnlineAvatars
927             = contact.details(QContactOnlineAvatar::DefinitionName)
928             + contact.details(QContactSocialAvatar::DefinitionName);
929
930     // Function was triggered by an update of nco:photo
931     if (not personalAvatar.isEmpty()) {
932         QContactAvatar photoAvatar = personalAvatar.toAvatar();
933
934         // If there was already a nco:photo (no linked uri), remove it
935         contact.removeDetail(&personalAvatar);
936
937         // nco:photo always goes first, so that QContact::detail<QContactAvatar> returns it
938         contact.saveDetail(&photoAvatar);
939     }
940
941     // Function was triggered by an update of a IMAddress' imAvatar
942     if (not currentOnlineAvatars.empty()) {
943         // find all available presence details
944         QMap<QString, QContactPresence> presenceDetails;
945
946         foreach(const QContactPresence &detail, contact.details<QContactPresence>()) {
947             const QString accountPath = parsePresenceIri(detail.detailUri());
948
949             if (not accountPath.isEmpty()) {
950                 presenceDetails.insert(accountPath, detail);
951             }
952         }
953
954         // store online avatars with their availablity
955         QList<QctAvatarWithAvailability> avatarsWithAvailability;
956
957         foreach(QContactDetail onlineAvatar, currentOnlineAvatars) {
958             contact.removeDetail(&onlineAvatar);
959
960             // FIXME: properly define schema for detailUri and linkedDetailUri
961             QStringList linkedDetailUris = onlineAvatar.linkedDetailUris();
962
963             if (linkedDetailUris.isEmpty()) {
964                 linkedDetailUris += QString();
965             }
966
967             foreach(const QString &uri, linkedDetailUris) {
968                 const QString accountPath = not uri.isEmpty()
969                         ? parseTelepathyIri(uri)
970                         : QString();
971
972                 int availability = qctAvailabilityRank(QContactPresence::PresenceUnknown);
973
974                 if (not accountPath.isEmpty()) {
975                     const QContactPresence presence = presenceDetails.value(accountPath);
976                     availability = qctAvailabilityRank(presence.presenceState());
977                 } else {
978                     qctWarn("QctContactOnlineAvatar should always be linked with an online account");
979                 }
980
981                 QContactAvatar avatarDetail;
982
983                 avatarDetail.setLinkedDetailUris(onlineAvatar.linkedDetailUris());
984                 avatarDetail.setImageUrl(onlineAvatar.value(QContactOnlineAvatar::FieldImageUrl));
985                 avatarDetail.setValue(QContactAvatar__FieldSubType, onlineAvatar.value(QContactOnlineAvatar::FieldSubType));
986
987                 avatarsWithAvailability += qMakePair(avatarDetail, availability);
988             }
989         }
990
991         // sort avatars by their availablity
992         qSort(avatarsWithAvailability);
993
994         // add regular avatar details to contact
995         for(int i = 0; i < avatarsWithAvailability.count(); ++i) {
996             contact.saveDetail(&avatarsWithAvailability[i].first);
997         }
998     }
999 }
1000
1001 bool
1002 QctContactManagerEngine::isWeakSyncTarget(const QString &syncTarget) const
1003 {
1004     return mangleAllSyncTargets() || weakSyncTargets().contains(syncTarget);
1005 }
1006
1007 QString
1008 QctContactManagerEngine::gcQueryId() const
1009 {
1010     return d->m_gcQueryId;
1011 }
1012
1013 // TODO: we could cache the return value in this function
1014 QString
1015 QctContactManagerEngine::cleanupQueryString() const
1016 {
1017     // Notice FILTER in WHERE clause - garbage collector needs to verify parenting outside this graph too.
1018     // reason - IMAccount hasIMAddress in contactsd private graph so no need to delete it. The same could apply
1019     // to any other property - it could have been added to parent in some other graph.
1020     // Camera geotagging creates addresses outside qct graph and have no "parent" contact pointing
1021     // to them through nco:hasAddress
1022     static const QString obsoleteResourcesPrefix = QLatin1String
1023             ("\n"
1024              "DELETE\n"
1025              "{\n"
1026              "  GRAPH <%1>\n"
1027              "  {\n"
1028              "    ?resource a <%2> .\n"
1029              "  }\n"
1030              "}\n"
1031              "WHERE\n"
1032              "{\n"
1033              "  GRAPH <%1>\n"
1034              "  {\n"
1035              "    ?resource a <%3> .\n"
1036              "  }\n");
1037     static const QString imAccountStopPattern = QLatin1String
1038             ("    FILTER(NOT EXISTS { ?resource a nco:IMAccount }) .\n");
1039     static const QString obsoleteResourcesPattern = QLatin1String
1040             ("    FILTER(NOT EXISTS { ?parent <%1> ?resource }) .\n");
1041     static const QString obsoleteResourcesSuffix = QLatin1String
1042             ("}\n");
1043
1044     QString queryString;
1045
1046 #ifndef QT_NO_DEBUG
1047     queryString += QLatin1String
1048             ("\n"
1049              "#--------------------------------------.\n"
1050              "# Drop drop obsolete custom properties |\n"
1051              "#--------------------------------------'\n");
1052 #endif // QT_NO_DEBUG
1053
1054     // collect obsolete resource types and their predicates
1055     typedef QMap<QString, bool> PredicateMap;
1056     typedef QMap<QString, PredicateMap> ResourceTypePredicateMap;
1057     ResourceTypePredicateMap obsoleteResources;
1058
1059     foreach(const QctContactDetailSchema &schema, schemas()) {
1060         foreach(const QctContactDetail &detail, schema.details()) {
1061             foreach(const QctPropertyInfoList &chain, detail.possessedChains()) {
1062                 const QctPropertyInfoBase &pi = chain.last();
1063
1064                 if (pi.rangeIri() == rdfs::Resource::iri()) {
1065                     qctWarn(QString::fromLatin1("Not cleaning up obsolete resources for %1 property"
1066                                                 "since the property's range is too generic (%2).").
1067                             arg(qctIriAlias(pi.iri()), qctIriAlias(pi.rangeIri())));
1068                     continue;
1069                 }
1070
1071                 obsoleteResources[pi.rangeIri()].insert(pi.iri(), true);
1072             }
1073         }
1074     }
1075
1076     // glue everything together
1077     queryString += obsoleteResourcesPrefix.arg(QtContactsTrackerDefaultGraphIri,
1078                                                nao::Property::iri(),
1079                                                nao::Property::iri());
1080     queryString += obsoleteResourcesPattern.arg(nao::hasProperty::iri());
1081     queryString += obsoleteResourcesSuffix;
1082
1083     for(ResourceTypePredicateMap::ConstIterator
1084         t = obsoleteResources.constBegin(); t != obsoleteResources.constEnd(); ++t) {
1085         queryString += obsoleteResourcesPrefix.arg(QtContactsTrackerDefaultGraphIri,
1086                                                    rdfs::Resource::iri(),
1087                                                    t.key());
1088
1089         if (nco::IMAddress::iri() == t.key()) {
1090             // FIXME: Remove this workaround for NB#206404 - Saving a contact using
1091             // qtcontacts-tracker causes nco:IMAccount to be removed.
1092             queryString += imAccountStopPattern;
1093         }
1094
1095         foreach(const QString &predicate, t.value().keys()) {
1096             queryString += obsoleteResourcesPattern.arg(predicate);
1097         }
1098
1099         queryString += obsoleteResourcesSuffix;
1100     }
1101
1102     return queryString;
1103 }
1104
1105 void
1106 QctContactManagerEngine::enqueueTask(QctTask *task)
1107 {
1108     d->m_queue->enqueue(task);
1109 }
1110
1111 ////////////////////////////////////////////////////////////////////////////////////////////////////
1112 // Asynchronous data access methods
1113 ////////////////////////////////////////////////////////////////////////////////////////////////////
1114
1115 bool
1116 QctContactManagerEngine::checkSecurityTokens(QContactAbstractRequest *request)
1117 {
1118     // Plugin users often fail to provide all required security tokens.
1119     // Therefore we print warnings to educate them. If the security checks
1120     // should cause trouble they can be temporarly disabled by exporting
1121     // QT_CONTACTS_TRACKER="debug=no-sec".
1122
1123     if (d->m_mandatoryTokensFound) {
1124         return true;
1125     }
1126
1127     QStringList theGateKeepersBlameList;
1128     QStringList missingOptionalTokens;
1129
1130 #ifdef ENABLE_CREDENTIALS
1131
1132     static const QStringList requiredSecurityTokens =
1133             QStringList() << QLatin1String("TrackerReadAccess")
1134                           << QLatin1String("TrackerWriteAccess");
1135
1136     static const QStringList optionalSecurityTokens =
1137             QStringList() << QLatin1String("GRP::metadata-users");
1138
1139     foreach(const QString &credential, requiredSecurityTokens) {
1140         QString errorMessage;
1141
1142         if (not MssfQt::CredentialsManager::hasProcessCredential(0, credential, &errorMessage)) {
1143             theGateKeepersBlameList += credential;
1144             qctWarn(errorMessage);
1145         }
1146     }
1147
1148     foreach(const QString &credential, optionalSecurityTokens) {
1149         QString errorMessage;
1150
1151         if (not MssfQt::CredentialsManager::hasProcessCredential(0, credential, &errorMessage)) {
1152             missingOptionalTokens += credential;
1153             qctWarn(errorMessage);
1154         }
1155     }
1156
1157 #endif // ENABLE_CREDENTIALS
1158
1159     if (not theGateKeepersBlameList.isEmpty()) {
1160         qctWarn(QString::fromLatin1("\n"
1161                  "=============================================================================\n"
1162                  "WARNING /!\\ - MANDATORY SECURITY TOKENS ARE MISSING\n"
1163                  "=============================================================================\n"
1164                  "Rejecting %2.\n"
1165                  "Please add an AEGIS manifest to your package requesting the following\n"
1166                  "security tokens: %1 for %3 [%4].\n"
1167                  "=============================================================================").
1168                 arg(theGateKeepersBlameList.join(QLatin1String(", ")),
1169                     QLatin1String(request->metaObject()->className()),
1170                     QCoreApplication::applicationFilePath(),
1171                     QString::number(QCoreApplication::applicationPid())));
1172
1173         return false;
1174     }
1175
1176     if (not missingOptionalTokens.isEmpty()) {
1177         qctWarn(QString::fromLatin1("\n"
1178                  "=============================================================================\n"
1179                  "WARNING /!\\ - OPTIONAL SECURITY TOKENS ARE MISSING\n"
1180                  "=============================================================================\n"
1181                  "Full functionality like tracker direct access is not available.\n"
1182                  "Please add an AEGIS manifest to your package requesting the following\n"
1183                  "security tokens: %1 for %2 [%3].\n"
1184                  "=============================================================================").
1185                 arg(missingOptionalTokens.join(QLatin1String(", ")),
1186                     QCoreApplication::applicationFilePath(),
1187                     QString::number(QCoreApplication::applicationPid())));
1188     }
1189
1190     d->m_mandatoryTokensFound = true;
1191
1192     return true;
1193 }
1194
1195 QctTask *
1196 QctContactManagerEngine::startRequestImpl(QContactAbstractRequest *request)
1197 {
1198     QCT_SYNCHRONIZED(&d->m_requestLifeGuard);
1199
1200     QWeakPointer<QContactAbstractRequest> guardedRequest = request;
1201
1202     // The worker puts the request under protection of the engine's request life-guard.
1203     QScopedPointer<QctAbstractWorker> worker(createRequestWorker(guardedRequest.data()));
1204
1205     if (worker.isNull()) {
1206         return 0;
1207     }
1208
1209     QScopedPointer<QctRequestTask> task(new QctRequestTask(guardedRequest.data(), worker.take()));
1210
1211     // Check if some evil client managed to delete the request from some other thread.
1212     if (guardedRequest.isNull()) {
1213         return 0;
1214     }
1215
1216     // Make sure mandatory tracker:ids have been resolved.
1217     if (task->dependencies().testFlag(QctAbstractWorker::ResourceCache) &&
1218         not d->m_satisfiedDependencies.testFlag(QctAbstractWorker::ResourceCache)) {
1219         // Queue will take ownership of the task
1220         // We don't explicitly parent the taks when creating them, because this method
1221         // might be called from a thread which is not the one where the engine was created.
1222         // In that case, we would get the "QObject: Cannot create children for a parent that
1223         // is in a different thread" warning, and the parent would be NULL anyway...
1224         enqueueTask(new QctResourceCacheTask(schemas()));
1225         d->m_satisfiedDependencies |= QctAbstractWorker::ResourceCache;
1226     }
1227
1228     // Make sure GUID algorithms are available.
1229     if (task->dependencies().testFlag(QctAbstractWorker::GuidAlgorithm) &&
1230         not d->m_satisfiedDependencies.testFlag(QctAbstractWorker::GuidAlgorithm)) {
1231         // Queue will take ownership of the task
1232         // See above why we don't set the parent here
1233         enqueueTask(new QctGuidAlgorithmTask(this));
1234         d->m_satisfiedDependencies |= QctAbstractWorker::GuidAlgorithm;
1235     }
1236
1237     // The contacts engine must bring requests into activate state as soon as it is dealing
1238     // with them. We cannot queue the task first, since the queue might be empty right now,
1239     // causing the task to start work from inactive state. This would be ridicilous.
1240     updateRequestState(guardedRequest.data(), QContactAbstractRequest::ActiveState);
1241
1242     // Check if some evil client managed to delete the request from some slot (or thread).
1243     if (guardedRequest.isNull()) {
1244         return 0;
1245     }
1246
1247     // Transfer task ownership to the queue.
1248     QctRequestTask *const result = task.data();
1249     d->m_queue->enqueue(task.take());
1250     return result;
1251 }
1252
1253 bool
1254 QctContactManagerEngine::startRequest(QContactAbstractRequest *request)
1255 {
1256     startRequestImpl(request);
1257     return true; // this always works
1258 }
1259
1260 QctAbstractWorker *
1261 QctContactManagerEngine::createRequestWorker(QContactAbstractRequest *request)
1262 {
1263     // about if mandatory security tokens are missing
1264     if (not checkSecurityTokens(request)) {
1265         return 0;
1266     }
1267
1268     // ensure old worker got destroyed when request gets reused
1269     requestDestroyed(request);
1270
1271     QctAbstractWorker *worker = 0;
1272     QElapsedTimer t; t.start();
1273
1274     // choose proper request implementation
1275     switch(request->type())
1276     {
1277     case QContactAbstractRequest::ContactFetchRequest:
1278         worker = new QctContactFetchWorker(request, this);
1279         break;
1280
1281     case QContactAbstractRequest::ContactFetchByIdRequest:
1282         worker = new QctContactFetchByIdWorker(request, this);
1283         break;
1284
1285     case QContactAbstractRequest::ContactLocalIdFetchRequest:
1286         worker = new QctContactIdFetchWorker(request, this);
1287         break;
1288
1289     case QContactAbstractRequest::ContactRemoveRequest:
1290         if (0 == qobject_cast<QctContactMergeRequest *>(request)) {
1291             worker = new QctContactRemoveWorker(request, this);
1292         } else {
1293             worker = new QctContactMergeWorker(request, this);
1294         }
1295         break;
1296
1297     case QContactAbstractRequest::ContactSaveRequest:
1298         if (0 == qobject_cast< QctUnmergeIMContactsRequest *> (request)) {
1299             worker = new QctContactSaveWorker(request, this);
1300         } else {
1301             worker = new QctUnmergeIMContactsWorker(request, this);
1302         }
1303         break;
1304
1305     case QContactAbstractRequest::RelationshipFetchRequest:
1306         worker = new QctRelationshipFetchWorker(request, this);
1307         break;
1308
1309     case QContactAbstractRequest::RelationshipRemoveRequest:
1310         worker = new QctRelationshipRemoveWorker(request, this);
1311         break;
1312
1313     case QContactAbstractRequest::RelationshipSaveRequest:
1314         worker = new QctRelationshipSaveWorker(request, this);
1315         break;
1316
1317     case QContactAbstractRequest::DetailDefinitionFetchRequest:
1318         worker = new QctDetailDefinitionFetchWorker(request, this);
1319         break;
1320
1321     case QContactAbstractRequest::InvalidRequest:
1322     case QContactAbstractRequest::DetailDefinitionRemoveRequest:
1323     case QContactAbstractRequest::DetailDefinitionSaveRequest:
1324         break;
1325     }
1326
1327     if (0 == worker) {
1328         qctWarn(QString::fromLatin1("Unsupported request type: %1").
1329                 arg(QLatin1String(request->metaObject()->className())));
1330         return 0;
1331     }
1332
1333     if (hasDebugFlag(ShowNotes)) {
1334         qDebug() << Q_FUNC_INFO << "time elapsed while constructing request workers:"<< request << t.elapsed();
1335     }
1336
1337     if (hasDebugFlag(ShowNotes)) {
1338         qDebug() << Q_FUNC_INFO << "running" << worker->metaObject()->className();
1339     }
1340
1341     // XXX The unit tests directly access the engine. Therefore requests created by our unit
1342     // tests don't have a manager attached. This prevents ~QContactAbstractRequest() to call our
1343     // engine's requestDestroyed(QContactAbstractRequest*) method, which results in memory leaks
1344     // within our our unit tests. To prevent those leaks we watch the request's destroyed()
1345     // signal and forward those signals to requestDestroyed(QContactAbstractRequest*) when
1346     // a request without contact manager is found.
1347     //
1348     // XXX This all of course is an ugly workaround. We probably should change the unit tests
1349     // to create use QContactManager accessing the engine via a static plugin.
1350     if (0 == request->manager()) {
1351         connect(request, SIGNAL(destroyed(QObject*)),
1352                 this, SLOT(onRequestDestroyed(QObject*)), Qt::DirectConnection);
1353     }
1354
1355     // track original request for this worker
1356     QCT_SYNCHRONIZED_WRITE(&d->m_tableLock);
1357
1358     d->m_workersByRequest.insert(request, worker);
1359     d->m_requestsByWorker.insert(worker, request);
1360
1361     return worker;
1362 }
1363
1364 QctRequestLocker
1365 QctContactManagerEngine::request(const QctAbstractWorker *worker) const
1366 {
1367     QScopedPointer<QMutexLocker> locker(new QMutexLocker(&d->m_requestLifeGuard));
1368     QCT_SYNCHRONIZED_READ(&d->m_tableLock);
1369     return QctRequestLocker(locker.take(), d->m_requestsByWorker.value(worker));
1370 }
1371
1372 void
1373 QctContactManagerEngine::connectSignals()
1374 {
1375     if (d->m_changeListener) {
1376         connect(d->m_changeListener,
1377                 SIGNAL(contactsAdded(QList<QContactLocalId>)),
1378                 SIGNAL(contactsAdded(QList<QContactLocalId>)));
1379         connect(d->m_changeListener,
1380                 SIGNAL(contactsChanged(QList<QContactLocalId>)),
1381                 SIGNAL(contactsChanged(QList<QContactLocalId>)));
1382         connect(d->m_changeListener,
1383                 SIGNAL(contactsRemoved(QList<QContactLocalId>)),
1384                 SIGNAL(contactsRemoved(QList<QContactLocalId>)));
1385         connect(d->m_changeListener,
1386                 SIGNAL(relationshipsAdded(QList<QContactLocalId>)),
1387                 SIGNAL(relationshipsAdded(QList<QContactLocalId>)));
1388         connect(d->m_changeListener,
1389                 SIGNAL(relationshipsRemoved(QList<QContactLocalId>)),
1390                 SIGNAL(relationshipsRemoved(QList<QContactLocalId>)));
1391     }
1392 }
1393
1394 void
1395 QctContactManagerEngine::disconnectSignals()
1396 {
1397     if (d->m_changeListener) {
1398         d->m_changeListener->disconnect(this);
1399         d->m_changeListener = 0;
1400     }
1401 }
1402
1403 void
1404 QctContactManagerEngine::registerGcQuery()
1405 {
1406     d->m_gcQueryId = QString::fromLatin1("com.nokia.qtcontacts-tracker");
1407     QctGarbageCollector::registerQuery(d->m_gcQueryId, cleanupQueryString());
1408 }
1409
1410 void
1411 QctContactManagerEngine::connectNotify(const char *signal)
1412 {
1413     if (0 == d->m_changeListener) {
1414         // Create the change listener on demand since:
1415         //
1416         // 1) Creating the listener is expensive as we must subscribe to some DBus signals.
1417         // 2) Watching DBus without any specific need wastes energy by wakeing up processes.
1418         //
1419         // Share the change listener for the reasons listed above.
1420         //
1421         // Still only share per thread (instead of process) to avoid nasty situations like the
1422         // random, listener owning thread terminating before other threads using the listener.
1423
1424         // Must monitor individual contact classes instead of nco:Contact
1425         // to avoid bogus notifications for:
1426         //
1427         //  - QContactOrganization details, implemented via nco:OrganizationContact
1428         //  - incoming calls which cause call-ui to create temporary(?) nco:Contact instances
1429         //
1430         // Additionally, nco:Contact does not have the tracker:notify property set to true, whereas
1431         // nco:PersonContact and nco:ContactGroup do have the property, so only those classes will
1432         // receive notifications of changes.
1433         QSet<QString> contactClassIris;
1434
1435         foreach (const QctContactDetailSchema &schema, d->m_parameters.m_detailSchemas) {
1436             foreach(const QString &iri, schema.contactClassIris()) {
1437                 if (iri != nco::Contact::iri()) {
1438                     contactClassIris += iri;
1439                 }
1440             }
1441         }
1442
1443         // Compute change filtering mode.
1444         QctTrackerChangeListener::ChangeFilterMode changeFilterMode = QctTrackerChangeListener::AllChanges;
1445
1446         if (d->m_parameters.m_omitPresenceChanges) {
1447             changeFilterMode = QctTrackerChangeListener::IgnorePresenceChanges;
1448         }
1449
1450         // Compute debug flags for the listener.
1451         QctTrackerChangeListener::DebugFlags debugFlags = QctTrackerChangeListener::NoDebug;
1452
1453         if (d->m_parameters.m_debugFlags.testFlag(QctContactManagerEngine::ShowSignals)) {
1454             debugFlags |= QctTrackerChangeListener::PrintSignals;
1455         }
1456
1457         // Compute cache id for the listener.
1458         // Cannot use the manager URI because it doesn't expose all relevant parameters.
1459         const QString listenerId = (QStringList(contactClassIris.toList()) <<
1460                                     QString::number(d->m_parameters.m_coalescingDelay) <<
1461                                     QString::number(changeFilterMode) <<
1462                                     QString::number(debugFlags)).join(QLatin1String(";"));
1463
1464         // Check if there already exists a listener for that computed id.
1465         QctThreadLocalData *const threadLocalData = QctThreadLocalData::instance();
1466         d->m_changeListener = threadLocalData->trackerChangeListener(listenerId);
1467
1468         if (0 == d->m_changeListener) {
1469             // Create new listener when needed.
1470             d->m_changeListener = new QctTrackerChangeListener(contactClassIris.toList(), QThread::currentThread());
1471             d->m_changeListener->setCoalescingDelay(d->m_parameters.m_coalescingDelay);
1472             d->m_changeListener->setChangeFilterMode(changeFilterMode);
1473             d->m_changeListener->setDebugFlags(debugFlags);
1474
1475             threadLocalData->setTrackerChangeListener(listenerId, d->m_changeListener);
1476         }
1477
1478         // Monitor the choosen listener's signals.
1479         connectSignals();
1480     }
1481
1482     QContactManagerEngine::connectNotify(signal);
1483 }
1484
1485 void
1486 QctContactManagerEngine::requestDestroyed(QContactAbstractRequest *req)
1487 {
1488     dropRequest(QctRequestLocker(new QMutexLocker(&d->m_requestLifeGuard), req));
1489 }
1490
1491 void
1492 QctContactManagerEngine::dropRequest(const QctRequestLocker &req)
1493 {
1494     QCT_SYNCHRONIZED_WRITE(&d->m_tableLock);
1495
1496     if (req.isNull()) {
1497         return;
1498     }
1499
1500     QctAbstractWorker *const worker = d->m_workersByRequest.take(req.data());
1501
1502     if (0 != worker) {
1503         d->m_requestsByWorker.remove(worker);
1504     }
1505 }
1506
1507 void
1508 QctContactManagerEngine::onRequestDestroyed(QObject *req)
1509 {
1510     // dynamic_cast<> does not work at this point (has a 0 result, or even crashes) because the
1511     // derived parts of the class have already been destroyed by the time the base QObject's
1512     // destructor has emitted this signal. So we do a regular static case, because
1513     // requestDestroyed(QContactAbstractRequest*) just compares the pointer value anyway.
1514     requestDestroyed(static_cast<QContactAbstractRequest *>(req));
1515 }
1516
1517 bool
1518 QctContactManagerEngine::waitForRequestFinished(QContactAbstractRequest *request, int msecs)
1519 {
1520     if (not hasDebugFlag(SkipNagging)) {
1521         qctWarn(QString::fromLatin1("\n"
1522             "=============================================================================\n"
1523             "WARNING /!\\ - NEVER EVER CALL THIS FUNCTION FROM PRODUCTION CODE!!!\n"
1524             "=============================================================================\n"
1525             "QContactAbstractRequest::waitForFinished(), respectively\n"
1526             "QContactManagerEngine::waitForRequestFinished() must spin your\n"
1527             "application's event loop. Doing so will cause HAVOC AND PAIN for\n"
1528             "any non-trivial program.\n"
1529             "\n"
1530             "So please refactor your asynchronious code, or use the synchronious\n"
1531             "API if blocking your application is acceptable.\n"
1532             "\n"
1533             "WE MEAN IT!!!\n"
1534             "\n"
1535             "Offending application is %1 [%2].\n"
1536             "=============================================================================").
1537             arg(QCoreApplication::applicationFilePath(),
1538                 QString::number(QCoreApplication::applicationPid())));
1539     }
1540
1541     return waitForRequestFinishedImpl(request, msecs);
1542 }
1543
1544 bool
1545 QctContactManagerEngine::waitForRequestFinishedImpl(QContactAbstractRequest *request, int msecs)
1546 {
1547     if (0 == request) {
1548         return false;
1549     }
1550
1551     QctRequestEventLoop eventLoop(request, msecs);
1552
1553     if (not eventLoop.isFinished()) {
1554         eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
1555     }
1556
1557     return eventLoop.isFinished();
1558 }
1559
1560 bool
1561 QctContactManagerEngine::runSyncRequest(QContactAbstractRequest *request,
1562                                         QContactManager::Error *error) const
1563 {
1564     // Copy the engine to prevent forwarding side-effects to the caller's engine.
1565     // It costs a bit, but guess that's a justified penalty to all sync API users.
1566     QctContactManagerEngine taskEngine(*this);
1567
1568     QctTask *const task = taskEngine.startRequestImpl(request);
1569
1570     if (0 != task && not QctTaskWaiter(task).wait(requestTimeout())) {
1571         qctPropagate(QContactManager::UnspecifiedError, error);
1572         return false;
1573     }
1574
1575     qctPropagate(request->error(), error);
1576
1577     return true;
1578 }
1579
1580 ////////////////////////////////////////////////////////////////////////////////////////////////////
1581 // Synthesized contact details
1582 ////////////////////////////////////////////////////////////////////////////////////////////////////
1583
1584 QString
1585 QctContactManagerEngine::synthesizedDisplayLabel(const QContact &contact,
1586                                                  QContactManager::Error *error) const
1587 {
1588     qctPropagate(QContactManager::NoError, error);
1589
1590     return createDisplayLabel(contact);
1591 }
1592
1593 QContact
1594 QctContactManagerEngine::compatibleContact(const QContact &original,
1595                                            QContactManager::Error *error) const
1596 {
1597     QContact contact = original;
1598
1599     foreach(const QctContactDetail &detail, schema(contact.type()).details()) {
1600         QList<QContactDetail> contactDetails = contact.details(detail.name());
1601
1602         if (contactDetails.empty()) {
1603             continue;
1604         }
1605
1606         // Check that detail respects our schema's cardinality
1607         if (not detail.isUnique()) {
1608             continue;
1609         }
1610
1611         if (contactDetails.count() < 2) {
1612             continue;
1613         }
1614
1615         qctWarn(QString::fromLatin1("Dropping odd details: %2 detail must be unique").
1616                 arg(detail.name()));
1617
1618         for(int i = 1; i < contactDetails.count(); ++i) {
1619             contact.removeDetail(&contactDetails[i]);
1620         }
1621     }
1622
1623     // Check fields contents
1624     foreach(const QctContactDetail &detail, schema(contact.type()).details()) {
1625         QList<QContactDetail> contactDetails = contact.details(detail.name());
1626
1627         foreach (QContactDetail contactDetail, contactDetails) {
1628             const QVariantMap detailValues = contactDetail.variantValues();
1629
1630             for (QVariantMap::ConstIterator it = detailValues.constBegin();
1631                  it != detailValues.constEnd(); ++it) {
1632                 const QctContactDetailField *field = detail.field(it.key());
1633
1634                 if (field == 0) {
1635                     // We can't validate custom fields, skip
1636                     continue;
1637                 }
1638
1639                 QVariant computedValue;
1640
1641                 if (not field->makeValue(it.value(), computedValue)) {
1642                     // Apparently this is not an error, we just prune the field...
1643                     // makeValue already prints a debug message if it returns false,
1644                     // so we can stay silent here. No idea how UI can provide any
1645                     // useful feedback out of that.
1646                     contactDetail.removeValue(it.key());
1647                     break;
1648                 }
1649
1650                 // The computed value is only useful to generate SPARQL queries,
1651                 // we should not save it back to the field.
1652             }
1653
1654             contact.saveDetail(&contactDetail);
1655         }
1656     }
1657
1658     qctPropagate(QContactManager::NoError, error);
1659
1660     return contact;
1661 }
1662
1663 ////////////////////////////////////////////////////////////////////////////////////////////////////
1664 // Unsupported functions
1665 ////////////////////////////////////////////////////////////////////////////////////////////////////
1666
1667 bool
1668 QctContactManagerEngine::validateContact(const QContact&, QContactManager::Error *error) const
1669 {
1670     REPORT_UNSUPPORTED_FUNCTION(error);
1671     return false;
1672 }
1673
1674 bool
1675 QctContactManagerEngine::validateDefinition(const QContactDetailDefinition&,
1676                                             QContactManager::Error *error) const
1677 {
1678     REPORT_UNSUPPORTED_FUNCTION(error);
1679     return false;
1680 }
1681
1682 bool
1683 QctContactManagerEngine::cancelRequest(QContactAbstractRequest *request)
1684 {
1685     if (0 == request) {
1686         return false;
1687     }
1688
1689     QCT_SYNCHRONIZED(&d->m_requestLifeGuard);
1690     QCT_SYNCHRONIZED_READ(&d->m_tableLock);
1691
1692     QctAbstractWorker *const worker = d->m_workersByRequest.value(request);
1693
1694     if (0 != worker) {
1695         return worker->cancel();
1696     }
1697     return false;
1698 }
1699
1700 bool
1701 QctContactManagerEngine::isRelationshipTypeSupported(const QString &relationshipType,
1702                                                      const QString &contactType) const
1703 {
1704     if (relationshipType == QContactRelationship::HasMember
1705             && supportedContactTypes().contains(contactType)
1706             && (contactType == QContactType::TypeContact
1707                 || contactType == QContactType::TypeGroup)) {
1708         return true;
1709     }
1710
1711     return false;
1712 }