Move tracker plugin sources into src folder.
[qtcontacts-tracker:qtcontacts-tracker.git] / src / qcontacttrackerbackend.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the Qt Mobility Components.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qcontacttrackerbackend_p.h"
43
44 #include <QtTracker/Tracker>
45 #include <QtTracker/ontologies/nco.h>
46 #include <QtTracker/ontologies/nie.h>
47 #include <QtTracker/ontologies/nao.h>
48 #include <QRegExp>
49 #include <QDir>
50 #include <QFile>
51 #include <QSet>
52 #include <QList>
53
54 #include "qtcontacts.h"
55
56 #include "trackerchangelistener.h"
57 #include "qtrackercontactsaverequest.h"
58 #include <qtrackerrelationshipfetchrequest.h>
59 #include <qtrackerrelationshipsaverequest.h>
60 #include <qtrackercontactidfetchrequest.h>
61
62
63 QContactManagerEngine* ContactTrackerFactory::engine(const QMap<QString, QString>& parameters, QContactManager::Error& error)
64 {
65     Q_UNUSED(error);
66     QString version = QLatin1String(VERSION_INFO);
67     return new QContactTrackerEngine(managerName(), version.toInt(), parameters);
68 }
69
70 QString ContactTrackerFactory::managerName() const
71 {
72     return QString("tracker");
73 }
74 Q_EXPORT_PLUGIN2(qtcontacts_tracker, ContactTrackerFactory);
75
76 QContactTrackerEngine::QContactTrackerEngine(const QString& engineName, int engineVersion, const QMap<QString, QString>& parameters)
77     : d(new QContactTrackerEngineData),
78     contactArchiveFile("removed"),
79     contactArchiveDir(QDir::homePath()+"/.contacts")
80 {
81     Q_UNUSED(parameters);
82     d->m_engineName = engineName;
83     d->m_engineVersion = engineVersion;
84     connectToSignals();
85 }
86
87 QContactTrackerEngine::QContactTrackerEngine(const QMap<QString, QString>& parameters)
88     : d(new QContactTrackerEngineData),
89     contactArchiveFile("removed"),
90     contactArchiveDir(QDir::homePath()+"/.contacts")
91 {
92     Q_UNUSED(parameters);
93     connectToSignals();
94 }
95
96 QContactTrackerEngine::QContactTrackerEngine(const QContactTrackerEngine& other)
97     : QContactManagerEngine(), d(other.d)
98 {
99     Q_UNUSED(other);
100     connectToSignals();
101 }
102
103 void QContactTrackerEngine::connectToSignals()
104 {
105     TrackerChangeListener *listener = new TrackerChangeListener(this, this);
106     connect(listener, SIGNAL(contactsAdded(const QList<QContactLocalId>&)), SIGNAL(contactsAdded(const QList<QContactLocalId>&)));
107     connect(listener, SIGNAL(contactsChanged(const QList<QContactLocalId>&)), SIGNAL(contactsChanged(const QList<QContactLocalId>&)));
108     connect(listener, SIGNAL(contactsRemoved(const QList<QContactLocalId>&)), SIGNAL(contactsRemoved(const QList<QContactLocalId>&)));
109 }
110
111 QContactTrackerEngine& QContactTrackerEngine::operator=(const QContactTrackerEngine& other)
112 {
113     d = other.d;
114     return *this;
115 }
116
117 QContactTrackerEngine::~QContactTrackerEngine()
118 {
119 }
120
121 QContactManagerEngine* QContactTrackerEngine::clone()
122 {
123     // this engine allows sharing - so we increase the reference count.
124     d->m_refCount.ref();
125     return this;
126 }
127
128 void QContactTrackerEngine::deref()
129 {
130     if (!d->m_refCount.deref())
131         delete this;
132 }
133
134 QList<QContactLocalId> QContactTrackerEngine::contactIds(const QList<QContactSortOrder>& sortOrders, QContactManager::Error& error) const
135 {
136     return contactIds(QContactFilter(), sortOrders, error);
137 }
138
139 QList<QContactLocalId> QContactTrackerEngine::contactIds(const QContactFilter& filter, const QList<QContactSortOrder>& sortOrders, QContactManager::Error& error) const
140 {
141     QContactLocalIdFetchRequest request;
142     request.setFilter(filter);
143     request.setSorting(sortOrders);
144
145     QContactTrackerEngine engine(*this);
146     engine.startRequest(&request);
147     // 10 seconds should be enough
148     engine.waitForRequestFinished(&request, 10000);
149     if(!request.isFinished()) {
150         error = QContactManager::UnspecifiedError;
151     }
152     else {
153         // leave the code for now while not all other code is fixed
154         error = request.error();
155     }
156     return request.ids();
157 }
158
159 QList<QContact> QContactTrackerEngine::contacts(const QList<QContactSortOrder>& sortOrders, const QStringList& definitionRestrictions, QContactManager::Error& error) const
160 {
161     return contacts(QContactFilter(), sortOrders, definitionRestrictions, error);
162 }
163
164 QList<QContact> QContactTrackerEngine::contacts(const QContactFilter& filter, const QList<QContactSortOrder>& sortOrders, const QStringList& definitionRestrictions, QContactManager::Error& error) const
165 {
166     // the rest of the code is for internal usage, unit tests etc.
167     QContactFetchRequest request;
168     request.setDefinitionRestrictions(definitionRestrictions);
169     request.setFilter(filter);
170     request.setSorting(sortOrders);
171
172     QContactTrackerEngine engine(*this);
173     engine.startRequest(&request);
174     // 10 seconds should be enough
175     engine.waitForRequestFinished(&request, 10000);
176
177     if( !request.isFinished()) {
178         error = QContactManager::UnspecifiedError;
179     }
180     else {
181         // leave the code for now while not all other code is fixed
182         error = request.error();
183     }
184     return request.contacts();
185 }
186
187 QContact QContactTrackerEngine::contact(const QContactLocalId& contactId, const QStringList& definitionRestrictions, QContactManager::Error& error) const
188 {
189     // plan to keep this warning for a while - as message to customers using the API
190     qWarning() << "QContactManager::contact()" << "api is blocking on dbus roundtrip while accessing tracker. Please, consider using asynchronous API QContactFetchRequest and not fetching contacts by id \n"
191             "- reading 100 ids and 100 contact by ids is ~100 times slower then reading 100 contacts at once with QContactFetchRequest.";
192     return contact_impl(contactId, definitionRestrictions, error);
193 }
194
195 QContactLocalId QContactTrackerEngine::selfContactId(QContactManager::Error& error) const
196 {
197     error = QContactManager::NoError;
198     return QContactLocalId(0xFFFFFFFF);
199 }
200
201 // used in tests, removed warning while decided if to provide sync api. Until then customers are advised to use async
202 QContact QContactTrackerEngine::contact_impl(const QContactLocalId& contactId, const QStringList& definitionRestrictions, QContactManager::Error& error ) const
203 {
204     QContactLocalIdFilter idlist;
205     QList<QContactLocalId> ids; ids << contactId;
206     idlist.setIds(ids);
207     QContactFetchRequest request;
208     QStringList fields = definitionRestrictions;
209     if (definitionRestrictions.isEmpty())
210     {
211         fields << QContactAvatar::DefinitionName
212                 << QContactBirthday::DefinitionName
213                 << QContactAddress::DefinitionName
214                 << QContactEmailAddress::DefinitionName
215                 << QContactDisplayLabel::DefinitionName
216                 << QContactGender::DefinitionName
217                 << QContactAnniversary::DefinitionName
218                 << QContactName::DefinitionName
219                 << QContactOnlineAccount::DefinitionName
220                 << QContactOrganization::DefinitionName
221                 << QContactPhoneNumber::DefinitionName
222                 << QContactOnlineAccount::DefinitionName
223                 << QContactUrl::DefinitionName;
224     }
225
226     request.setDefinitionRestrictions(fields);
227     request.setFilter(idlist);
228
229     QContactTrackerEngine engine(*this);
230     engine.startRequest(&request);
231     // 10 seconds should be enough
232     engine.waitForRequestFinished(&request, 10000);
233
234     if( !request.isFinished()) {
235         error = QContactManager::UnspecifiedError;
236         return QContact();
237     }
238     else if(request.contacts().size() == 0)
239     {
240         error = QContactManager::DoesNotExistError;
241         return QContact();
242     }
243     else {
244         // leave the code for now while not all other code is fixed
245         error = request.error();
246         return request.contacts()[0];
247     }
248
249 }
250
251 bool QContactTrackerEngine::waitForRequestFinished(QContactAbstractRequest* req, int msecs)
252 {
253     Q_ASSERT(req);
254     if(!req->isActive())
255     {
256         return req->isFinished(); // might be already finished
257     }
258     QTime t;
259     t.start();
260     while(t.elapsed() < msecs || msecs == 0) // 0 for infinite
261     {
262         usleep(10000);
263         QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
264         if(req->isFinished())
265             return true;
266     }
267     qDebug() << Q_FUNC_INFO <<"Status Finished" << req->isFinished();
268     return req->isFinished();
269
270 }
271
272 bool QContactTrackerEngine::saveContact( QContact* contact, QContactManager::Error& error)
273 {
274     // Signal emitted from TrackerChangeListener
275     QContactSaveRequest request;
276     QList<QContact> contacts(QList<QContact>()<<*contact);
277     request.setContacts(contacts);
278     QContactTrackerEngine engine(*this);
279     engine.startRequest(&request);
280     // 10 seconds should be enough
281     engine.waitForRequestFinished(&request, 10000);
282     error = request.error();
283     Q_ASSERT(request.contacts().size() == 1);
284     *contact = request.contacts()[0];
285
286     if( request.isFinished() && error == QContactManager::NoError)
287         return true;
288     else
289         return false;
290 }
291
292 bool QContactTrackerEngine::removeContact(const QContactLocalId& contactId, QContactManager::Error& error)
293 {
294     error = QContactManager::NoError;
295
296     // TODO: Do with LiveNodes when they support strict querying.
297     RDFVariable RDFContact = RDFVariable::fromType<nco::PersonContact>();
298     RDFContact.property<nco::contactUID>() = LiteralValue(QString::number(contactId));
299     RDFSelect query;
300
301     query.addColumn("contact_uri", RDFContact);
302     LiveNodes ncoContacts = ::tracker()->modelQuery(query);
303     if(ncoContacts->rowCount() == 0) {
304         error = QContactManager::DoesNotExistError;
305         return false;
306     }
307
308
309     Live< nco::PersonContact> ncoContact = ncoContacts->liveNode(0);
310     LiveNodes contactMediums = ncoContact->getHasContactMediums();
311     foreach(Live<nco::ContactMedium> media, contactMediums) {
312         media->remove();
313     }
314     ncoContact->remove();
315
316     //Temporary workaround to get removed- signal
317     QList<QContactLocalId> removed;
318     removed << contactId;
319     emit contactsRemoved(removed);
320
321     return true;
322 }
323
324 bool QContactTrackerEngine::saveContacts(QList<QContact>* contacts, QMap<int, QContactManager::Error>* errorMap, QContactManager::Error& error)
325 {
326     // @todo: Handle errors per saved contact.
327     Q_UNUSED(errorMap)
328
329     error = QContactManager::NoError;
330
331     if(contacts == 0) {
332         error = QContactManager::BadArgumentError;
333         return false;
334     }
335
336     // Signal emitted from TrackerChangeListener
337     QContactSaveRequest request;
338     QList<QContact> contactList;
339     for (int i = 0; i < contacts->size(); ++i) {
340         contactList.append(contacts->at(i));
341     }
342     request.setContacts(contactList);
343     QContactTrackerEngine engine(*this);
344     engine.startRequest(&request);
345     /// @todo what should be the delay
346     engine.waitForRequestFinished(&request, 1000*contacts->size());
347     /// @todo what should we do in case request.isFinished() == false
348     if (request.isFinished() == false) {
349         qWarning() << "QContactTrackerEngine::saveContacts:" << "request not finished";
350     }
351     error = request.error();
352     for (int i = 0; i < contacts->size(); ++i) {
353         (*contacts)[i] = request.contacts().at(i);
354     }
355
356     // Returns false if we have any errors - true if everything went ok.
357     return (request.errorMap().isEmpty() && error == QContactManager::NoError);
358 }
359
360 bool QContactTrackerEngine::removeContacts(QList<QContactLocalId>* contactIds, QMap<int, QContactManager::Error>* errorMap, QContactManager::Error& error)
361 {
362     // Cannot report errors - giving up.
363     if(!errorMap) {
364         error = QContactManager::BadArgumentError;
365         return false;
366     }
367
368     // let's clear the error hash so there is nothing old haunting us.
369     errorMap->clear();
370
371     if (!contactIds) {
372         error = QContactManager::BadArgumentError;
373         return false;
374     }
375
376     for (int i = 0; i < contactIds->count(); i++) {
377         QContactManager::Error lastError;
378         removeContact(contactIds->at(i), lastError);
379         if (lastError == QContactManager::NoError) {
380             (*contactIds)[i] = 0;
381         }
382         else {
383             errorMap->insert(i, lastError);
384         }
385     }
386
387     // Returns true if no errors were encountered - false if there was errors.
388     // emit signals removed as they are fired from QContactManager
389     return (errorMap->isEmpty());
390 }
391
392 QMap<QString, QContactDetailDefinition> QContactTrackerEngine::detailDefinitions(const QString& contactType,
393                                                                                  QContactManager::Error& error) const
394 {
395     if (contactType != QContactType::TypeContact) {
396         error = QContactManager::InvalidContactTypeError;
397         return QMap<QString, QContactDetailDefinition>();
398     }
399
400     // lazy initialisation of schema definitions.
401     if (d->m_definitions.isEmpty()) {
402         // none in the list?  get the schema definitions, and modify them to match our capabilities.
403         d->m_definitions = QContactManagerEngine::schemaDefinitions().value(QContactType::TypeContact);
404
405         // modification: name is unique
406         QContactDetailDefinition nameDef = d->m_definitions.value(QContactName::DefinitionName);
407         nameDef.setUnique(true);
408         d->m_definitions.insert(QContactName::DefinitionName, nameDef);
409
410         // modification: avatar is unique.
411         QContactDetailDefinition avatarDef = d->m_definitions.value(QContactAvatar::DefinitionName);
412         avatarDef.setUnique(true);
413         d->m_definitions.insert(QContactAvatar::DefinitionName, avatarDef);
414
415         // modification: url is unique.
416         {
417             const QContactDetailDefinition urlDef = d->m_definitions.value(
418                     QContactUrl::DefinitionName);
419             QContactDetailDefinition newUrlDef;
420
421             QMap<QString, QContactDetailFieldDefinition> urlFieldNames = urlDef.fields();
422             QMap<QString, QContactDetailFieldDefinition> &fields(urlFieldNames);
423             QContactDetailFieldDefinition f;
424
425             f.setDataType(QVariant::String);
426             QVariantList subTypes;
427             // removing social networking url
428             subTypes << QString(QLatin1String(QContactUrl::SubTypeFavourite));
429             subTypes << QString(QLatin1String(QContactUrl::SubTypeHomePage));
430             f.setAllowableValues(subTypes);
431             fields.insert(QContactUrl::FieldSubType, f);
432             newUrlDef.setFields(fields);
433             newUrlDef.setUnique(true);
434             newUrlDef.setName(QContactUrl::DefinitionName);
435             d->m_definitions.insert(QContactUrl::DefinitionName, newUrlDef);
436         }
437
438         // QContactOnlineAccount custom fields
439         {
440             const QContactDetailDefinition accDef = d->m_definitions.value(QContactOnlineAccount::DefinitionName);
441             QContactDetailDefinition newAccountDefinition;
442
443             QMap<QString, QContactDetailFieldDefinition> accountFieldName = accDef.fields();
444             QMap<QString, QContactDetailFieldDefinition> &fields(accountFieldName);
445             QContactDetailFieldDefinition f;
446
447             f.setDataType(QVariant::String);
448             fields.insert("Account", f);
449             fields.insert("AccountPath", f);
450             newAccountDefinition.setFields(fields);
451             newAccountDefinition.setName(QContactOnlineAccount::DefinitionName);
452             d->m_definitions.insert(QContactOnlineAccount::DefinitionName, newAccountDefinition);
453         }
454     }
455
456     error = QContactManager::NoError;
457     return d->m_definitions;
458 }
459
460 /*!
461  * \reimp
462  */
463 bool QContactTrackerEngine::hasFeature(QContactManager::ManagerFeature feature, const QString& contactType) const
464 {
465     if (!supportedContactTypes().contains(contactType)) {
466         return false;
467     }
468
469     switch (feature) {
470         case QContactManager::Groups:
471         case QContactManager::ActionPreferences:
472         case QContactManager::Relationships:
473         case QContactManager::SelfContact:
474             return true;
475         case QContactManager::ArbitraryRelationshipTypes:
476             return true;
477         case QContactManager::MutableDefinitions:
478             return true;
479         case QContactManager::ChangeLogs:
480             return true;
481         default:
482             return false;
483     }
484 }
485
486
487 /*!
488  * \reimp
489  */
490 /*!
491  * Definition identifiers which are natively (fast) filterable
492  * on the default backend store managed by the manager from which the capabilities object was accessed
493  */
494 bool QContactTrackerEngine::isFilterSupported(const QContactFilter& filter) const
495 {
496     switch (filter.type()) {
497         case QContactFilter::InvalidFilter:
498         case QContactFilter::DefaultFilter:
499         case QContactFilter::LocalIdFilter:
500         case QContactFilter::ContactDetailFilter:
501         case QContactFilter::ContactDetailRangeFilter:
502         case QContactFilter::ActionFilter:
503         case QContactFilter::ChangeLogFilter:
504         case QContactFilter::RelationshipFilter:
505
506 // not yet done
507 //        case QContactFilter::IntersectionFilter:
508 //        case QContactFilter::UnionFilter:
509             return true;
510         default:
511             return false;
512     }
513 }
514
515 /*!
516  * Returns the list of data types supported by the Tracker engine
517  */
518 QList<QVariant::Type> QContactTrackerEngine::supportedDataTypes() const
519 {
520     // TODO: Check supported datatypes for Tracker backend.
521     QList<QVariant::Type> st;
522     st.append(QVariant::String);
523     st.append(QVariant::Date);
524     st.append(QVariant::DateTime);
525     return st;
526 }
527
528 /*!
529  * Returns the name of the Tracker engine
530  */
531 QString QContactTrackerEngine::managerName() const
532 {
533     return d->m_engineName;
534 }
535
536 /*!
537  * Returns the manager version of this engine
538  */
539 int QContactTrackerEngine::managerVersion() const
540 {
541     return d->m_engineVersion;
542 }
543
544 RDFVariable QContactTrackerEngine::contactDetail2Rdf(const RDFVariable& rdfContact, const QString& definitionName,
545                                                       const QString& fieldName) const
546 {
547     if (definitionName == QContactName::DefinitionName) {
548         if (fieldName == QContactName::FieldFirst) {
549             return rdfContact.property<nco::nameGiven>();
550         }
551         else if (fieldName == QContactName::FieldLast) {
552             return rdfContact.property<nco::nameFamily>();
553         }
554         else if (fieldName == QContactName::FieldMiddle) {
555             return rdfContact.property<nco::nameAdditional>();
556         }
557         else if (fieldName == QContactName::FieldPrefix) {
558             return rdfContact.property<nco::nameHonorificPrefix>();
559         }
560         else if (fieldName == QContactName::FieldSuffix) {
561             return rdfContact.property<nco::nameHonorificSuffix>();
562         }
563         else if (fieldName == QContactNickname::FieldNickname) {
564             return rdfContact.property<nco::nickname>();
565         }
566     }
567     return RDFVariable();
568 }
569
570 /*! \reimp */
571 void QContactTrackerEngine::requestDestroyed(QContactAbstractRequest* req)
572 {
573     if( d->m_requests.contains(req) )
574     {
575         QTrackerContactAsyncRequest *request = d->m_requests.take(req);
576         delete request;
577     }
578 }
579
580 /*! \reimp */
581 bool QContactTrackerEngine::startRequest(QContactAbstractRequest* req)
582 {
583     QTrackerContactAsyncRequest *request = 0;
584     switch (req->type())
585     {
586         case QContactAbstractRequest::ContactLocalIdFetchRequest:
587             request = new QTrackerContactIdFetchRequest(req, this);
588             break;
589         case QContactAbstractRequest::ContactFetchRequest:
590             request = new QTrackerContactFetchRequest(req, this);
591             break;
592         case QContactAbstractRequest::ContactSaveRequest:
593             request = new QTrackerContactSaveRequest(req, this);
594             break;
595         case QContactAbstractRequest::RelationshipFetchRequest:
596             request = new QTrackerRelationshipFetchRequest(req, this);
597             break;
598         case QContactAbstractRequest::RelationshipSaveRequest:
599             request = new QTrackerRelationshipSaveRequest(req, this);
600             break;
601         default:
602             return false;
603     }
604     d->m_requests[req] = request;
605     return true;
606 }
607
608 /*! \reimp */
609 QString QContactTrackerEngine::synthesizedDisplayLabel(const QContact& contact, QContactManager::Error& error) const
610 {
611     QString label = QContactManagerEngine::synthesizedDisplayLabel(contact, error);
612     if (label.isEmpty())
613         label = contact.detail<QContactNickname>().nickname();
614     if(label.isEmpty())
615         label = contact.detail<QContactOnlineAccount>().nickname();
616
617     qDebug() << Q_FUNC_INFO << label;
618     return label;
619 }