plugins/contacts/maemo5/qcontactabook.cpp
changeset 0 876b1a06bc25
child 5 603d3f8b6302
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/contacts/maemo5/qcontactabook.cpp	Wed Aug 25 15:49:42 2010 +0300
@@ -0,0 +1,1903 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcontactmaemo5backend_p.h"
+#include "qcontactabook_p.h"
+
+#include <QEventLoop>
+#include <libebook/e-book-util.h>
+
+#include <libmcclient/mc-account-manager.h>
+
+/* Error handling Macros */
+#define FATAL_IF_ERROR(x) if(x) { \
+                            QString message(x->message); \
+                            g_error_free(x); \
+                            qFatal(qPrintable(message)); \
+                          }
+
+/* Casting Macros */
+#define A_CONTACT(x) reinterpret_cast<OssoABookContact*>(x)
+#define A_ROSTER(x) reinterpret_cast<OssoABookRoster*>(x)
+#define CONST_CHAR(x) static_cast<const char*>(x)
+#define FREE(x) free((void*)x)
+
+struct cbSharedData{
+  QContactIDsHash* hash;
+  QContactABook *that;
+};
+
+struct jobSharedData{
+   QContactABook* that;
+   bool *result;
+   char *uid;
+   QContactManager::Error *error;
+};
+
+/* QContactABook */
+QContactABook::QContactABook(QObject* parent) :QObject(parent), m_cbSD(0), m_deleteJobSD(0), m_saveJobSD(0)
+{
+  //Initialize QContactDetail context list
+  initAddressBook();
+}
+
+QContactABook::~QContactABook()
+{
+  OssoABookAggregator *roster = reinterpret_cast<OssoABookAggregator*>(m_abookAgregator);
+  if (g_signal_handler_is_connected(roster, m_contactAddedHandlerId))
+      g_signal_handler_disconnect(roster, m_contactAddedHandlerId);
+  if (g_signal_handler_is_connected(roster, m_contactChangedHandlerId))
+      g_signal_handler_disconnect(roster, m_contactChangedHandlerId);
+  if (g_signal_handler_is_connected(roster, m_contactRemovedHandlerId))
+      g_signal_handler_disconnect(roster, m_contactRemovedHandlerId);
+
+  // XXX FIXME: memory leak?
+  //g_object_unref(m_abookAgregator);
+  delete m_cbSD;
+  m_cbSD = 0;
+  delete m_deleteJobSD;
+  m_deleteJobSD = 0;
+  delete m_saveJobSD;
+  m_saveJobSD = 0;
+}
+
+static void contactsAddedCB(OssoABookRoster *roster, OssoABookContact **contacts, gpointer data)
+{
+  QCM5_DEBUG << "CONTACT ADDED";
+  Q_UNUSED(roster)
+  
+  cbSharedData* d = static_cast<cbSharedData*>(data);
+  if (!d){
+    return;
+  }
+  
+  OssoABookContact **p;
+  QList<QContactLocalId> contactIds;
+  
+  for (p = contacts; *p; ++p) {
+    if (osso_abook_contact_is_roster_contact(*p))
+      continue;
+    
+    // Add a new localID to the local ID hash
+    const char* uid = CONST_CHAR(e_contact_get_const(E_CONTACT(*p), E_CONTACT_UID));
+    QContactLocalId id = d->hash->append(uid);
+    
+    if (id)
+      contactIds << id;
+  }
+
+  if (!contactIds.isEmpty())
+    d->that->_contactsAdded(contactIds);
+}
+
+static void contactsChangedCB(OssoABookRoster *roster, OssoABookContact **contacts, gpointer data)
+{
+  QCM5_DEBUG << "CONTACT CHANGED";
+  Q_UNUSED(roster)
+  
+  cbSharedData* d = static_cast<cbSharedData*>(data);
+  if (!d){
+    return;
+  }
+  
+  OssoABookContact **p;
+  QList<QContactLocalId> contactIds;
+  
+  for (p = contacts; *p; ++p) {
+    if (osso_abook_contact_is_roster_contact(*p))
+      continue;
+    
+    const char* uid = CONST_CHAR(e_contact_get_const(E_CONTACT(*p), E_CONTACT_UID));
+    QContactLocalId id = d->hash->find(uid);
+    //FREE(uid);
+    if (id)
+      contactIds << id;
+  }
+
+  if (!contactIds.isEmpty())
+    d->that->_contactsChanged(contactIds);
+}
+
+static void contactsRemovedCB(OssoABookRoster *roster, const char **ids, gpointer data)
+{
+  QCM5_DEBUG << "CONTACT REMOVED";
+  Q_UNUSED(roster)
+  
+  cbSharedData* d = static_cast<cbSharedData*>(data);
+  if (!d){
+    return;
+  }
+  
+  const char **p;
+  QList<QContactLocalId> contactIds;
+  
+  for (p = ids; *p; ++p) {
+      QContactLocalId id = d->hash->take(*p);
+      if (id) {
+        QCM5_DEBUG << "Contact" << id << "has been removed";
+        contactIds << id;
+      }
+  }
+  
+  if (!contactIds.isEmpty())
+    d->that->_contactsRemoved(contactIds);
+}
+
+void QContactABook::initAddressBook(){
+  /* Open AddressBook */
+  GError *gError = NULL;
+  OssoABookRoster* roster = NULL;
+  
+  roster = osso_abook_aggregator_get_default(&gError);
+  FATAL_IF_ERROR(gError)
+  
+  osso_abook_waitable_run((OssoABookWaitable *) roster, g_main_context_default(), &gError);
+  FATAL_IF_ERROR(gError)
+  
+  if (!osso_abook_waitable_is_ready ((OssoABookWaitable *) roster, &gError))
+    FATAL_IF_ERROR(gError)
+  
+  m_abookAgregator = reinterpret_cast<OssoABookAggregator*>(roster);
+  
+  /* Initialize local Id Hash */
+  initLocalIdHash();
+  
+  /* Initialize callbacks shared data */
+  m_cbSD = new cbSharedData;
+  m_cbSD->hash = &m_localIds;
+  m_cbSD->that = this;
+   
+  /* Setup signals */
+  m_contactAddedHandlerId = g_signal_connect(roster, "contacts-added",
+                   G_CALLBACK (contactsAddedCB), m_cbSD);
+  m_contactChangedHandlerId = g_signal_connect(roster, "contacts-changed",
+                   G_CALLBACK (contactsChangedCB), m_cbSD);
+  m_contactRemovedHandlerId = g_signal_connect(roster, "contacts-removed",
+                   G_CALLBACK (contactsRemovedCB), m_cbSD);
+  
+  //TEST Lists supported fields
+  if (QCM5_DEBUG_ENABLED){
+    EBook *book = NULL;
+    GList *l;
+    book = osso_abook_roster_get_book(roster);
+    e_book_get_supported_fields (book, &l, NULL);
+    while (l) {
+      qDebug() << "SUPPORTED FIELD" << (const char*)l->data;
+      l = l->next;  
+    }
+    g_list_free(l);
+  }
+}
+
+/*! Fill LocalId Hash associating an internal QContactLocalId to any
+ *  master contact ID.
+ *  NOTE: master contact IDs are string like "1" or "osso-abook-tmc1".
+ */
+void QContactABook::initLocalIdHash()
+{  
+   GList *contactList = NULL;
+   GList *node;
+   
+   contactList = osso_abook_aggregator_list_master_contacts(m_abookAgregator);
+
+   if (!contactList) {
+     QCM5_DEBUG << "There are no Master contacts. LocalId hash is empty.";
+     return;
+   }
+   
+   for (node = contactList; node != NULL; node = g_list_next (node)) {
+     EContact *contact = E_CONTACT(node->data);
+     const char* data = CONST_CHAR(e_contact_get_const(contact, E_CONTACT_UID));
+     QByteArray eContactUID(data);
+     //FREE(data);
+     m_localIds << eContactUID; //FIXME MemLeak
+     QCM5_DEBUG << "eContactID " << eContactUID << "has been stored in m_localIDs with key" << m_localIds[eContactUID];
+     
+     // Useful for debugging.
+     if (QCM5_DEBUG_ENABLED)
+       e_vcard_dump_structure((EVCard*)contact);
+   }
+   
+   g_list_free(contactList);
+}
+
+//TODO Use native filters
+QList<QContactLocalId> QContactABook::contactIds(const QContactFilter& filter, const QList<QContactSortOrder>& sortOrders, QContactManager::Error* error) const
+{
+  Q_UNUSED(sortOrders)
+  Q_UNUSED(filter);
+  *error = QContactManager::NoError;
+  return m_localIds.keys();
+
+  /*
+  // Sorting
+  //NOTE Native sorting is possible thanks to g_list_sort.
+  //     It's limited just to one filter.
+  //     Multi filters support need non native sorting.
+  //     Native filtering needs a lot of coding since we need
+  //     detailDefinitionName * detailFieldName functions
+  //     to compare couple of contacts
+  if (sortOrders.count()){
+    QCM5_DEBUG << "Sorting...";
+    // We don't need 
+    // Fill Ids
+    QList<QContactLocalId> Ids;
+    {
+      QList<QContactSortOrder> so;
+      QContactManager::Error e;
+      Ids = contactIds(filter, so, &e);
+    }
+      
+    // Fill Contact List
+    QList<QContact> contacts;
+    foreach(QContactLocalId id, Ids){
+      QContact *c;
+      QContactManager::Error e;
+      c = getQContact(id, &e);
+      if (e == QContactManager::NoError)
+	contacts << *c;
+      else
+        *error = e;
+    }
+    
+    // Non native sorting
+    return QContactManagerEngine::sortContacts(contacts, sortOrders);
+  }
+  
+  switch(filter.type()){
+    case QContactFilter::DefaultFilter: {
+      rtn = m_localIds.keys();
+    } break;
+    default: { 
+      EBookQuery* query = convert(filter);
+      GList* l = osso_abook_aggregator_find_contacts(m_abookAgregator, query);
+      if (query)
+        e_book_query_unref(query);
+      
+      while (l){
+        EContact *contact = E_CONTACT(l->data);
+        const char* data = CONST_CHAR(e_contact_get_const(contact, E_CONTACT_UID));
+        QByteArray localId(data);
+        m_localIds << localId;
+        rtn.append(m_localIds[localId]);
+        QCM5_DEBUG << "eContactID " << localId << "has been stored in m_localIDs with key" << m_localIds[localId];
+        l = g_list_delete_link(l, l);
+      }
+    }
+  }
+  *error = QContactManager::NoError;
+  return rtn;
+  */
+}
+
+QContact* QContactABook::getQContact(const QContactLocalId& contactId, QContactManager::Error* error) const
+{
+  QContact *rtn;
+  OssoABookContact* aContact = getAContact(contactId, error);
+  if (!aContact) {
+    return new QContact;
+  }
+  
+  //Convert aContact => qContact
+  rtn = convert(E_CONTACT(aContact));
+  
+  QContactId cId;
+  cId.setLocalId(contactId);
+  rtn->setId(cId);
+  
+  return rtn;
+}
+
+static QContactManager::Error getErrorFromStatus(const EBookStatus status){
+  switch (status) {
+    case E_BOOK_ERROR_OK:
+      return QContactManager::NoError;
+    case E_BOOK_ERROR_INVALID_ARG:
+      return QContactManager::BadArgumentError;
+    case E_BOOK_ERROR_BUSY:
+      return QContactManager::LockedError;        
+    case E_BOOK_ERROR_PERMISSION_DENIED:
+    case E_BOOK_ERROR_AUTHENTICATION_FAILED:
+    case E_BOOK_ERROR_AUTHENTICATION_REQUIRED:
+      return QContactManager::PermissionsError;
+    case E_BOOK_ERROR_CONTACT_NOT_FOUND:
+      return QContactManager::DoesNotExistError;
+    case E_BOOK_ERROR_CONTACT_ID_ALREADY_EXISTS:
+      return QContactManager::AlreadyExistsError;
+    case E_BOOK_ERROR_NO_SPACE:
+      return QContactManager::OutOfMemoryError;
+#if 0
+    case E_BOOK_ERROR_REPOSITORY_OFFLINE:
+    case E_BOOK_ERROR_NO_SUCH_BOOK:
+    case E_BOOK_ERROR_NO_SELF_CONTACT:
+    case E_BOOK_ERROR_SOURCE_NOT_LOADED:
+    case E_BOOK_ERROR_SOURCE_ALREADY_LOADED:
+    case E_BOOK_ERROR_PROTOCOL_NOT_SUPPORTED:
+    case E_BOOK_ERROR_CANCELLED:
+    case E_BOOK_ERROR_COULD_NOT_CANCEL:
+    case E_BOOK_ERROR_TLS_NOT_AVAILABLE:
+    case E_BOOK_ERROR_CORBA_EXCEPTION:
+    case E_BOOK_ERROR_NO_SUCH_SOURCE:
+    case E_BOOK_ERROR_OFFLINE_UNAVAILABLE:
+    case E_BOOK_ERROR_OTHER_ERROR:
+    case E_BOOK_ERROR_INVALID_SERVER_VERSION:
+#endif
+    default:
+      return QContactManager::UnspecifiedError;
+  }
+}
+
+static void delContactCB(EBook *book, EBookStatus status, gpointer closure)
+{
+  Q_UNUSED(book);
+  QCM5_DEBUG << "Contact Removed";
+  
+  jobSharedData *sd = static_cast<jobSharedData*>(closure);
+  if (!sd)
+    return;
+  
+  *sd->result = (status != E_BOOK_ERROR_OK &&
+                 status != E_BOOK_ERROR_CONTACT_NOT_FOUND) ? false : true;
+  *sd->error = getErrorFromStatus(status);
+  
+  sd->that->_jobRemovingCompleted();
+}
+
+bool QContactABook::removeContact(const QContactLocalId& contactId, QContactManager::Error* error)
+{
+  Q_UNUSED(error);
+  QMutexLocker locker(&m_delContactMutex);
+
+  bool ok = false;
+  
+  OssoABookRoster *roster = A_ROSTER(m_abookAgregator);
+  EBook *book = osso_abook_roster_get_book(roster);
+  OssoABookContact *aContact = getAContact(contactId, error);
+  if (!OSSO_ABOOK_IS_CONTACT(aContact)){
+    QCM5_DEBUG << "Specified contact is not a valid ABook contact";
+    return false;
+  }
+  
+  // ASync => Sync
+  QEventLoop loop;                           
+  connect(this, SIGNAL(jobRemovingCompleted()), &loop, SLOT(quit()));
+  
+  // Prepare shared data
+  if (m_deleteJobSD){
+    delete m_deleteJobSD;
+    m_deleteJobSD = 0;
+  }
+  m_deleteJobSD = new jobSharedData;
+  m_deleteJobSD->that = this;
+  m_deleteJobSD->result = &ok;
+  m_deleteJobSD->error = error;
+  
+  //Remove photos
+  EContactPhoto *photo = NULL;
+  GFile *file = NULL;
+  photo = (EContactPhoto*) e_contact_get(E_CONTACT (aContact), E_CONTACT_PHOTO);
+  if (photo) {
+    if (photo->type == E_CONTACT_PHOTO_TYPE_URI && photo->data.uri) {
+      file = g_file_new_for_uri(photo->data.uri);
+      g_file_delete(file, NULL, NULL);
+      g_object_unref (file);
+    }
+    e_contact_photo_free (photo);
+  }
+  
+  //Remove all roster contacts from their roster
+  GList* rosterContacts = NULL;
+  rosterContacts = osso_abook_contact_get_roster_contacts(aContact);
+  const char *masterUid = CONST_CHAR(e_contact_get_const(E_CONTACT(aContact), E_CONTACT_UID));
+  char *contactUidCopy = strdup(masterUid);
+  while(rosterContacts){
+    OssoABookContact *rosterContact = A_CONTACT(rosterContacts->data);
+    osso_abook_contact_reject_for_uid(rosterContact, masterUid, NULL);
+    rosterContacts = rosterContacts->next;
+  }  
+  
+  // Remove contact
+  e_book_async_remove_contact(book, E_CONTACT(aContact),
+                              delContactCB, m_deleteJobSD);
+  
+  loop.exec(QEventLoop::AllEvents|QEventLoop::WaitForMoreEvents);
+
+  // update our list of ids...
+  QContactLocalId id = m_localIds[contactUidCopy];
+  m_localIds.remove(contactUidCopy);
+  if (contactUidCopy)
+    free(contactUidCopy);
+  
+  if (id)
+    _contactsRemoved(QList<QContactLocalId>() << id);
+  
+  return ok;
+}
+
+static void commitContactCB(EBook* book, EBookStatus  status, gpointer user_data)
+{
+  Q_UNUSED(book)
+  jobSharedData *sd = static_cast<jobSharedData*>(user_data);
+  if (!sd)
+    return;
+  
+  *sd->result = (status == E_BOOK_ERROR_OK) ? true : false;
+  *sd->error = getErrorFromStatus(status);
+  sd->that->_jobSavingCompleted();
+}
+
+static void addContactCB(EBook* book, EBookStatus  status, const char  *uid, gpointer user_data)
+{
+  jobSharedData *sd = static_cast<jobSharedData*>(user_data);
+  if (!sd)
+    return;
+  
+  if (uid)
+    sd->uid = strdup(uid);
+
+  //### FIXME IS THIS LINE REALLY NEEDED: osso_abook_contact_set_roster(OssoABookContact *contact, OssoABookRoster *roster)
+  *sd->result = (status == E_BOOK_ERROR_OK) ? true : false;
+  commitContactCB(book, status, user_data);
+}
+
+bool QContactABook::saveContact(QContact* contact, QContactManager::Error* error)
+{
+  QMutexLocker locker(&m_saveContactMutex);
+  
+  if (!contact) {
+    *error = QContactManager::BadArgumentError;
+    return false;
+  }
+
+  bool ok = false;
+  
+  OssoABookContact *aContact = NULL;
+  const char *uid;
+  EBook *book;
+  {
+    OssoABookRoster* roster = reinterpret_cast<OssoABookRoster*>(m_abookAgregator);
+    book = osso_abook_roster_get_book(roster);
+  }
+  
+  // Convert QContact to AContact
+  aContact = convert(contact, error);
+  if (!aContact){
+    return false;
+  }  
+
+  // ASync => Sync
+  QEventLoop loop;
+  connect(this, SIGNAL(jobSavingCompleted()), &loop, SLOT(quit()));
+
+  // Prepare shared data
+  if (m_saveJobSD){
+    delete m_saveJobSD;
+    m_saveJobSD = 0;
+  }
+  m_saveJobSD = new jobSharedData;
+  m_saveJobSD->that = this;
+  m_saveJobSD->result = &ok;
+  m_saveJobSD->error = error;
+  m_saveJobSD->uid = 0;
+  
+  // Add/Commit the contact
+  uid = CONST_CHAR(e_contact_get_const(E_CONTACT (aContact), E_CONTACT_UID)); 
+  if (uid) {
+    m_saveJobSD->uid = strdup(uid);
+    osso_abook_contact_async_commit(aContact, book, commitContactCB, m_saveJobSD);
+  } else {
+    osso_abook_contact_async_add(aContact, book, addContactCB, m_saveJobSD);
+  }
+  
+  loop.exec(QEventLoop::AllEvents|QEventLoop::WaitForMoreEvents);
+
+  // save the newly saved contact's id in the hash.
+  m_localIds << m_saveJobSD->uid;
+
+  // set the id of the contact.
+  QContactId cId;
+  cId.setLocalId(m_localIds[m_saveJobSD->uid]);
+  contact->setId(cId);
+  if (m_saveJobSD->uid)
+      free(m_saveJobSD->uid);
+  
+  return ok;
+}
+
+const QString QContactABook::getDisplayName(const QContact& contact) const{
+  //Get Osso ABook ID for the contact (stored as GUID detail)
+  const char* acontactID = NULL;
+  {
+    QContactGuid g = contact.detail(QContactGuid::DefinitionName);
+    acontactID = qPrintable(g.guid());
+  }
+  
+  if (!acontactID){
+    QCM5_DEBUG << "The contact has not been saved yet and it doesn't have any GUID";
+    return QString();
+  }
+    
+  //Get OssoABookContact
+  OssoABookContact *acontact= NULL;
+  {
+    GList* l= NULL;
+    l = osso_abook_aggregator_lookup(m_abookAgregator, acontactID);
+    
+    if (g_list_length(l) == 1) {
+      acontact = A_CONTACT(l->data);
+    }
+    g_list_free(l);
+    
+  }
+  
+  if (!acontact){
+    QCM5_DEBUG << "AContact with ID:" << acontactID << "is null";
+    return QString();
+  }
+  //Get Display name;
+  const char* displayName = osso_abook_contact_get_display_name(acontact);  
+
+  return QString::fromUtf8(displayName);
+}
+
+QContactLocalId QContactABook::selfContactId(QContactManager::Error* errors) const
+{
+  QContactLocalId id;
+  EContact *self = E_CONTACT(osso_abook_self_contact_get_default());
+  if (self) {
+    *errors = QContactManager::NoError;
+    const char* data = CONST_CHAR(e_contact_get_const(self, E_CONTACT_UID));
+    const QByteArray eContactUID(data);
+    QContactLocalId localId = m_localIds[eContactUID];
+    if (localId)
+      id = localId;
+    else {
+      m_localIds << eContactUID; //FIXME MemLeak
+      id = m_localIds[eContactUID];
+      QCM5_DEBUG << "eContactID " << eContactUID << "has been stored in m_localIDs with key" << id;
+    }
+  } else {
+    QCM5_DEBUG << "Cannot find the self contact";
+    *errors = QContactManager::DoesNotExistError;
+    id = 0;
+  }
+  g_object_unref(self);
+  return id;
+}
+
+bool QContactABook::contactActionsMatch(OssoABookContact *contact, QList<QContactActionDescriptor> descriptors) const
+{
+  OssoABookCapsFlags capsFlags = osso_abook_caps_get_capabilities(OSSO_ABOOK_CAPS(contact));
+
+  if(capsFlags & OSSO_ABOOK_CAPS_NONE)
+    return false;
+
+  /* ActionNames could be incorrect */
+  OssoABookCapsFlags actionFlags = OSSO_ABOOK_CAPS_NONE;
+  for(int i = 0; i < descriptors.size(); i++){
+    QString actionName = descriptors.at(i).actionName();
+    QCM5_DEBUG << actionName;
+    if(!actionName.compare("Phone"))
+      actionFlags = (OssoABookCapsFlags)(actionFlags | OSSO_ABOOK_CAPS_PHONE);
+    else if(!actionName.compare("Voice"))
+      actionFlags = (OssoABookCapsFlags)(actionFlags | OSSO_ABOOK_CAPS_VOICE);
+    else if(!actionName.compare("SendEmail"))
+      actionFlags = (OssoABookCapsFlags)(actionFlags | OSSO_ABOOK_CAPS_EMAIL);
+    else if(!actionName.compare("Chat"))
+      actionFlags = (OssoABookCapsFlags)(actionFlags | OSSO_ABOOK_CAPS_CHAT);
+    else if(!actionName.compare("ChatAdditional"))
+      actionFlags = (OssoABookCapsFlags)(actionFlags | OSSO_ABOOK_CAPS_CHAT_ADDITIONAL);
+    else if(!actionName.compare("VoiceAdditional"))
+      actionFlags = (OssoABookCapsFlags)(actionFlags | OSSO_ABOOK_CAPS_VOICE_ADDITIONAL);
+    else if(!actionName.compare("Video"))
+      actionFlags = (OssoABookCapsFlags)(actionFlags | OSSO_ABOOK_CAPS_VIDEO);
+    else if(!actionName.compare("Addressbook"))
+      actionFlags = (OssoABookCapsFlags)(actionFlags | OSSO_ABOOK_CAPS_ADDRESSBOOK);
+  }
+  return ((actionFlags & capsFlags) == actionFlags);
+}
+
+EBookQuery* QContactABook::convert(const QContactFilter& filter) const
+{
+  EBookQuery* query = NULL;
+  
+  switch(filter.type()){
+    case QContactFilter::DefaultFilter:
+    {
+      QCM5_DEBUG << "QContactFilter::DefaultFilter";
+      query = e_book_query_any_field_contains(""); //Match all contacts
+    } break;
+    case QContactFilter::LocalIdFilter:
+    {
+      QCM5_DEBUG << "LocalIdFilter";
+      const QContactLocalIdFilter f(filter);
+      QList<QContactLocalId> ids = f.ids();
+      if (ids.isEmpty())
+        return NULL;
+      
+      query= NULL;
+      foreach(const QContactLocalId id, ids){
+        EBookQuery* q = NULL;
+        
+        // Looking for the eContact local id inside the localId hash
+        const char* eContactId = m_localIds[id];
+        if (!eContactId[0])
+          return NULL;
+        
+        q = e_book_query_field_test(E_CONTACT_UID, E_BOOK_QUERY_IS, eContactId);
+        if (!q)
+          continue;
+        if (query)
+          query = e_book_query_orv(query, q, NULL);
+        else
+          query = q;
+      }
+    } break;
+    case QContactFilter::ContactDetailFilter:
+    {
+      QCM5_DEBUG << "ContactDetailFilter";
+      const QContactDetailFilter f(filter);
+      QString queryStr;
+      if (!f.value().isValid())
+        return NULL;
+      switch (f.matchFlags()){
+        case QContactFilter::MatchContains: queryStr = "contains"; break;
+        case QContactFilter::MatchFixedString:
+        case QContactFilter::MatchCaseSensitive:
+        case QContactFilter::MatchExactly: queryStr = "is"; break;
+        case QContactFilter::MatchStartsWith: queryStr = "beginswith"; break;
+        case QContactFilter::MatchEndsWith: queryStr = "endswith"; break;
+        default:
+          queryStr = "contains";
+      }
+      static QHash<QString,QString> hash;
+      if (hash.isEmpty()){
+        hash[QContactAddress::DefinitionName] = "address";
+        hash[QContactBirthday::DefinitionName] = "birth-date";
+        hash[QContactDisplayLabel::DefinitionName] = "full-name"; //hack
+        hash[QContactEmailAddress::DefinitionName] = "email";
+        hash[QContactName::DefinitionName] = "full-name";
+        hash[QContactNickname::DefinitionName] = "nickname";
+        hash[QContactNote::DefinitionName] = "note";
+        hash[QContactOrganization::DefinitionName] = "org";
+        hash[QContactPhoneNumber::DefinitionName] = "phone";
+        hash[QContactUrl::DefinitionName] = "homepage-url";
+      }
+  
+      QString eDetail = hash[f.detailDefinitionName()];
+      if (eDetail.isEmpty()){
+        return NULL;
+      }
+      queryStr = queryStr + " \"" + eDetail + "\" \"" + f.value().toString() + "\"";
+      query = e_book_query_from_string(qPrintable(queryStr));
+    } break;
+    case QContactFilter::ActionFilter:
+      QCM5_DEBUG << "ActionFilter"; //eQuery doesn't support ActionFilter
+      break;
+    case QContactFilter::IntersectionFilter:
+    {
+      QCM5_DEBUG << "IntersectionFilter";
+      const QContactIntersectionFilter f(filter);
+      const QList<QContactFilter>  fs= f.filters();
+      QContactFilter i;
+      foreach(i, fs){
+        EBookQuery* q = convert(i);
+        if (!q)
+          continue;
+        if (query)
+          query = e_book_query_andv(query, q, NULL);
+        else
+          query = q;
+      } 
+    } break;
+    case QContactFilter::UnionFilter:
+    {
+      QCM5_DEBUG << "UnionFilter";
+      const QContactUnionFilter f(filter);
+      const QList<QContactFilter>  fs= f.filters();
+      QContactFilter i;
+      foreach(i, fs){
+        EBookQuery* q = convert(i);
+        if (!q){
+          continue;
+        }
+        if (query)
+          query = e_book_query_orv(query, q, NULL);
+        else
+          query = q;
+      }
+    } break;
+    case QContactFilter::InvalidFilter:
+    {
+      QCM5_DEBUG << "InvalidFilter";
+      query = e_book_query_from_string("(is \"id\" \"-1\")");
+    } break;
+    default:
+      QCM5_DEBUG << "Filter not supported";
+      query = convert(QContactInvalidFilter());
+  }
+ 
+  //Debugging
+  if (QCM5_DEBUG_ENABLED){
+    const char *queryString = e_book_query_to_string(query);
+    QCM5_DEBUG << "QUERY" << queryString;
+    FREE(queryString);
+  }
+  return query;
+} 
+
+QContact* QContactABook::convert(EContact *eContact) const
+{
+  QContact *contact = new QContact();
+  QList<QContactDetail*> detailList;
+
+  /* Id */
+  contact->setId(getContactId(eContact));
+
+  /* Address */
+  QList<QContactAddress*> addressList = getAddressDetail(eContact);
+  QContactAddress* address;
+  foreach(address, addressList)
+    detailList << address;
+
+  /* Avatar */
+  detailList << getAvatarDetail(eContact); // XXX TODO: FIXME
+  detailList << getThumbnailDetail(eContact);
+
+  /* BirthDay */
+  detailList << getBirthdayDetail(eContact);
+
+  /* Email */
+  QList<QContactEmailAddress*> emailList = getEmailDetail(eContact);
+  QContactEmailAddress* email;
+  foreach(email, emailList)
+    detailList << email;
+
+  /* Gender */
+  detailList << getGenderDetail(eContact);
+
+  /* Global UID*/
+  detailList << getGuidDetail(eContact);
+
+  /* Name & NickName*/
+  detailList << getNameDetail(eContact);
+  detailList << getNicknameDetail(eContact);
+
+  /* Note */
+  detailList << getNoteDetail(eContact);
+
+  /* Online Account & presences*/
+  QList<QContactOnlineAccount*> onlineAccounts;
+  QList<QContactPresence*> presences;
+  getOnlineAccountAndPresenceDetails(eContact, onlineAccounts, presences);
+  
+  QContactOnlineAccount* onlineAccount;
+  foreach(onlineAccount, onlineAccounts)
+    detailList << onlineAccount;
+  
+  QContactPresence* presence;
+  foreach(presence, presences)
+    detailList << presence;
+
+  /* Organization */
+  detailList << getOrganizationDetail(eContact);
+
+  /* Phone*/
+  QList<QContactPhoneNumber*> phoneNumberList = getPhoneDetail(eContact);
+  QContactPhoneNumber* phoneNumber;
+  foreach(phoneNumber, phoneNumberList)
+    detailList << phoneNumber;
+
+  /* TimeStamp */
+  detailList << getTimestampDetail(eContact);
+
+  /* Url */
+  detailList << getUrlDetail(eContact);
+
+  bool ok;
+  QContactDetail* detail;
+ 
+  foreach(detail, detailList){
+    if (detail->isEmpty()){
+      delete detail;
+      continue;
+    }
+
+    ok = contact->saveDetail(detail);
+    if (!ok){
+      delete detail;
+      continue;
+    }
+    delete detail;
+  }
+
+  return contact;
+}
+
+bool QContactABook::setDetailValues(const QVariantMap& data, QContactDetail* detail) const
+{
+  QMapIterator<QString, QVariant> i(data);
+  QVariant value;
+  while (i.hasNext()) {
+     i.next();
+     value = i.value();
+     
+     if (value.isNull())
+       continue;
+     
+     if (value.canConvert<QString>() && value.toString().isEmpty())
+       continue;
+     
+     QCM5_DEBUG << "Set" << i.key() << i.value();
+     detail->setValue(i.key(), i.value());
+
+  }
+  
+  if (detail->isEmpty())
+    return false;
+  return true;
+}
+
+OssoABookContact* QContactABook::getAContact(const QContactLocalId& contactId, QContactManager::Error* error) const
+{
+  OssoABookContact* rtn = NULL;
+
+  QCM5_DEBUG << "Getting aContact with id " << m_localIds[contactId] << "local contactId is" << contactId;
+
+  if(QString::fromAscii(m_localIds[contactId]).compare("osso-abook-self") == 0) {
+    *error = QContactManager::NoError;
+    rtn = A_CONTACT(osso_abook_self_contact_get_default());
+  } else {
+    GList* c = osso_abook_aggregator_lookup(m_abookAgregator, m_localIds[contactId]);
+    if (c)
+      rtn = A_CONTACT(c->data);
+    *error = rtn ? QContactManager::NoError : QContactManager::DoesNotExistError;
+    return rtn;
+  }
+
+  return rtn;
+}
+
+QContactId QContactABook::getContactId(EContact *eContact) const
+{
+  QContactId rtn;
+  /* Set LocalId */
+  {
+    const char* data = CONST_CHAR(e_contact_get_const(eContact, E_CONTACT_UID));
+    const QByteArray eContactUID(data);
+    QContactLocalId localId = m_localIds[eContactUID];
+    
+    if (!localId)
+      rtn.setLocalId(localId);
+  }
+  return rtn;
+}
+
+QList<QContactAddress*> QContactABook::getAddressDetail(EContact *eContact) const
+{
+  QList<QContactAddress*> rtnList;
+
+  //Ordered list of Fields
+  QStringList addressFields;
+  addressFields << QContactAddress::FieldPostOfficeBox
+                << AddressFieldExtension //XXX FIXME I'm not sure we have to use a new field
+                << QContactAddress::FieldStreet
+                << QContactAddress::FieldLocality
+                << QContactAddress::FieldRegion 
+                << QContactAddress::FieldPostcode 
+                << QContactAddress::FieldCountry;
+  
+  GList* attrList = osso_abook_contact_get_attributes(eContact, EVC_ADR);
+  
+  for (GList *node = g_list_last(attrList); node != NULL; node = g_list_previous(node)) {
+    QContactAddress *address = new QContactAddress;
+    QVariantMap map;
+ 
+    EVCardAttribute *attr = static_cast<EVCardAttribute*>(node->data);
+    
+    // Set Address Context using attribute parameter value
+    EVCardAttributeParam *param = NULL;
+    GList* p = e_vcard_attribute_get_params(attr);
+    
+    if (p)
+      param = static_cast<EVCardAttributeParam*>(p->data);
+    
+    if (param){
+      GList *v = e_vcard_attribute_param_get_values(param);
+      QString context = CONST_CHAR(v->data);
+      if (context == "HOME")
+        address->setContexts(QContactDetail::ContextHome);
+      else if (context == "WORK")
+        address->setContexts(QContactDetail::ContextWork);
+    }
+    
+    // Set Address Values
+    GList *v = NULL;
+    v =e_vcard_attribute_get_values(attr);
+    if (!v) {
+      // ADR attribute data is corrupted.  Skipping.
+      g_list_free(attrList);
+      return rtnList;
+    }
+
+    int i = 0;
+    while (v && i < 7) {
+      // we only deal with the first 7 fields: pobox, extension, street, locality, region, postcode, country.
+      map[addressFields[i]] = QString::fromUtf8(CONST_CHAR(v->data));
+      i++;
+      v = v->next;
+    }
+    g_list_free(v);
+    map[QContactDetail::FieldDetailUri] = QString::number(g_list_position(attrList, node));
+    setDetailValues(map, address);
+    
+    rtnList << address;
+  }
+  
+  g_list_free(attrList);
+  return rtnList;
+}
+
+QContactName* QContactABook::getNameDetail(EContact *eContact) const
+{
+  QContactName* rtn = new QContactName;
+  QVariantMap map;
+
+  //Try to get the structure (looks that this is not supported in Maemo5)
+  EContactName* eContactName = static_cast<EContactName*> (e_contact_get(eContact, E_CONTACT_NAME));
+  if (eContactName){
+    map[QContactName::FieldCustomLabel] = eContactName->additional;
+    map[QContactName::FieldFirstName] = eContactName->given;
+    map[QContactName::FieldLastName] = eContactName->family;
+    map[QContactName::FieldPrefix] = eContactName->prefixes;
+    map[QContactName::FieldSuffix] = eContactName->suffixes;
+    e_contact_name_free (eContactName);
+  } else {
+    //Looks that Maemo use just these two fields
+    map[QContactName::FieldFirstName] = QString::fromUtf8(CONST_CHAR(e_contact_get_const(eContact, E_CONTACT_GIVEN_NAME)));
+    map[QContactName::FieldLastName] = QString::fromUtf8(CONST_CHAR(e_contact_get_const(eContact, E_CONTACT_FAMILY_NAME)));
+  }
+  setDetailValues(map, rtn);
+  return rtn;
+}
+
+QContactNickname* QContactABook::getNicknameDetail(EContact *eContact) const
+{
+  QContactNickname* rtn = new QContactNickname;
+  QVariantMap map;
+  map[QContactNickname::FieldNickname] = QString::fromUtf8(CONST_CHAR(e_contact_get_const(eContact, E_CONTACT_NICKNAME)));
+  setDetailValues(map, rtn);
+  return rtn;
+}
+
+QList<QContactEmailAddress*> QContactABook::getEmailDetail(EContact *eContact) const
+{
+  QList<QContactEmailAddress*> rtnList;
+  
+  GList* attrList = osso_abook_contact_get_attributes(eContact, EVC_EMAIL); //FIXME MemLeak
+  
+  for (GList *node = g_list_last(attrList); node != NULL; node = g_list_previous(node)) {
+    QContactEmailAddress *email = new QContactEmailAddress;
+    QVariantMap map;
+  
+    EVCardAttribute *attr = static_cast<EVCardAttribute*>(node->data);
+
+    // Set Address Context using attribute parameter value
+    EVCardAttributeParam *param = NULL;
+    GList* p = e_vcard_attribute_get_params(attr);
+    
+    if (p)
+      param = static_cast<EVCardAttributeParam*>(p->data);
+    
+    if (param){
+      GList *v = e_vcard_attribute_param_get_values(param);
+      QString context = CONST_CHAR(v->data);
+      if (context == "HOME")
+        email->setContexts(QContactDetail::ContextHome);
+      else if (context == "WORK")
+        email->setContexts(QContactDetail::ContextWork);
+    }
+    
+    // Set Address Values
+    GList *v = e_vcard_attribute_get_values(attr);
+    int i = 0;
+    while (v){
+      map[QContactEmailAddress::FieldEmailAddress] = QString::fromUtf8(CONST_CHAR(v->data));
+      i++;
+      v = v->next;
+    }
+    g_list_free(v);
+    
+    map[QContactDetail::FieldDetailUri] = QString::number(g_list_position(attrList, node));
+    setDetailValues(map, email);
+    rtnList << email;
+  }
+  g_list_free(attrList);
+  
+  return rtnList;
+}
+
+QContactAvatar* QContactABook::getAvatarDetail(EContact *eContact) const
+{
+    Q_UNUSED(eContact);
+    // XXX TODO
+    // Should be retrieved from EVC_PHOTO
+
+    QContactAvatar* empty = new QContactAvatar;
+    return empty;
+}
+
+QContactBirthday* QContactABook::getBirthdayDetail(EContact *eContact) const
+{
+  QContactBirthday* rtn = new QContactBirthday;
+  QVariantMap map;
+  EContactDate *date =static_cast<EContactDate*>(e_contact_get(eContact, E_CONTACT_BIRTH_DATE));
+  if (!date)
+    return rtn;
+  QDate birthday(date->year, date->month, date->day);
+  e_contact_date_free(date);
+  map[QContactBirthday::FieldBirthday] = birthday;
+  setDetailValues(map, rtn);
+  return rtn;
+}
+
+QContactGender* QContactABook::getGenderDetail(EContact *eContact) const
+{
+  QContactGender* rtn = new QContactGender;
+  QVariantMap map;
+  const char* g = CONST_CHAR(osso_abook_contact_get_value(eContact, "X-GENDER"));
+  QString gender = g;
+  if (gender == "male")
+    gender = "Male";
+  else if (gender == "female")
+    gender = "Female";
+  else if (gender == "unspecified")
+    gender = "Unspecified";
+  
+  map[QContactGender::FieldGender] = gender;
+  FREE(g);
+  setDetailValues(map, rtn);
+  return rtn;
+}  
+
+//NOTE Using UID as GUID
+QContactGuid* QContactABook::getGuidDetail(EContact *eContact) const
+{
+  QContactGuid* rtn = new QContactGuid;
+  QVariantMap map;
+  const char* uid = CONST_CHAR(e_contact_get(eContact, E_CONTACT_UID));
+  map[QContactGuid::FieldGuid] = uid;
+  FREE(uid);
+  setDetailValues(map, rtn);
+  return rtn;
+}
+
+QContactNote* QContactABook::getNoteDetail(EContact *eContact) const
+{
+  QContactNote* rtn = new QContactNote;
+  QVariantMap map;
+  const char* note = CONST_CHAR(e_contact_get(eContact, E_CONTACT_NOTE));
+  map[QContactNote::FieldNote] = QString::fromUtf8(note);
+  FREE(note);
+  setDetailValues(map, rtn);
+  return rtn;
+}
+
+static const QStringList vcardsManagedByTelepathy(){
+  static QStringList rtn;
+  
+  if (rtn.isEmpty()){
+    OssoABookAccountManager* accountMgr = osso_abook_account_manager_get_default();
+    const GList *vcardFields = osso_abook_account_manager_get_primary_vcard_fields(accountMgr);
+    while (vcardFields){
+      QString field = (const char*)vcardFields->data;
+      if (!rtn.contains(field) && field != "TEL")
+        rtn << field;
+      vcardFields = vcardFields->next;
+    }
+    FREE(vcardFields);
+  }
+  
+  QCM5_DEBUG << "VCards managed by Telepathy:" << rtn;
+  return rtn;
+}
+
+static void populateProfilesCapabilitiesMap(QMap<QString, QStringList>* map){
+  GList* l =  mc_profiles_list(); //Don't free this!!
+  GList* node;
+  
+  map->clear();
+
+  for (node = l; node != NULL; node = g_list_next(node)) {
+    McProfile* p = (McProfile*) node->data; //Don't free this!!
+    
+    //Get serviceProvider name from the profile
+    QString serviceProvider = mc_profile_get_unique_name(p);
+    if (serviceProvider.isEmpty())
+      continue;
+    
+    // Get capabilities
+    McProfileCapabilityFlags caps = mc_profile_get_capabilities(p);
+    QStringList capList;
+    
+    // MC_PROFILE_CAPABILITY_NONE skipped
+    if (caps & MC_PROFILE_CAPABILITY_CHAT_P2P)
+      capList << "CHAT_P2P";
+    if (caps & MC_PROFILE_CAPABILITY_CHAT_ROOM)
+      capList << "CHAT_ROOM";
+    if (caps & MC_PROFILE_CAPABILITY_CHAT_ROOM_LIST)
+      capList << "CHAT_ROOM_LIST";
+    if (caps & MC_PROFILE_CAPABILITY_VOICE_P2P)
+      capList << "VOICE_P2P";
+    if (caps & MC_PROFILE_CAPABILITY_CONTACT_SEARCH)
+      capList << "CONTACT_SEARCH";
+    if (caps & MC_PROFILE_CAPABILITY_SPLIT_ACCOUNT)
+      capList << "SPLIT_ACCOUNT";
+    if (caps & MC_PROFILE_CAPABILITY_REGISTRATION_UI)
+      capList << "REGISTRATION_UI";
+    if (caps & MC_PROFILE_CAPABILITY_SUPPORTS_AVATARS)
+      capList << "SUPPORTS_AVATARS";
+    if (caps & MC_PROFILE_CAPABILITY_SUPPORTS_ALIAS)
+      capList << "SUPPORTS_ALIAS";
+    if (caps & MC_PROFILE_CAPABILITY_SUPPORTS_ROSTER)
+      capList << "SUPPORTS_ROSTER";
+    if (caps & MC_PROFILE_CAPABILITY_VIDEO_P2P)
+      capList << "VIDEO_P2P";
+    if (caps & MC_PROFILE_CAPABILITY_CHAT_UPGRADE)
+      capList << "CHAT_UPGRADE";
+    
+    // Store into the map
+    map->insert(serviceProvider, capList); 
+  }  
+  
+}
+
+static const QStringList serviceProviderCapabilities(const QString& serviceProvider){
+  static QMap<QString, QStringList> map;
+  
+  if (map.isEmpty())
+    populateProfilesCapabilitiesMap(&map);
+  
+  if (serviceProvider.isEmpty())
+    return QStringList();
+  
+  QStringList rtn = map.value(serviceProvider);
+  if (!rtn.isEmpty())
+    return rtn;
+  else
+    return QStringList();
+}
+
+#if 0
+// BUG? This code has been commented out because osso_abook_presence_get_presence_status returns empty strings
+static QContactPresence::PresenceState telepathyStatusToPresenceState(const QString& status){
+  if (status == "offline")
+    return QContactPresence::PresenceOffline;
+  else if (status == "unknown" || status == "error")
+    return QContactPresence::PresenceUnknown;
+  else if (status == "available")
+    return QContactPresence::PresenceAvailable;
+  else if (status == "away" || status == "brb" ) //Be Right Back (a more specific form of Away)
+    return QContactPresence::PresenceAway;
+  else if (status == "busy" || status == "dnd") //Do Not Disturb (a more specific form of Busy)
+    return QContactPresence::PresenceBusy;
+  else if (status == "xa") //Extended Away
+    return QContactPresence::PresenceExtendedAway;
+  else if (status == "hidden") // "Invisible" or "Appear Offline"
+    return QContactPresence::PresenceHidden;
+}
+#endif
+
+void QContactABook::getOnlineAccountAndPresenceDetails(EContact *eContact, 
+                                                      QList<QContactOnlineAccount*>& onlineAccounts,
+                                                      QList<QContactPresence*>& presences) const
+{
+  const QStringList telepathyVCards = vcardsManagedByTelepathy();
+  
+  // Parsing Attributes associated to the Telepathy VCards
+  GList *attributeList = e_vcard_get_attributes((EVCard*)eContact);
+  GList *node;
+
+  if (!attributeList)
+    return;
+  
+  for (node = attributeList; node != NULL; node = g_list_next (node)) {
+    QContactOnlineAccount* onlineAccount = new QContactOnlineAccount;
+    QContactPresence* contactPresence = new QContactPresence;
+    
+    const char* accountUri = NULL;
+    const char* serviceProvider = NULL;
+    const char* accountPath = NULL; // Outgoing account path eg: SERVICE_NAME/PROTOCOL_NAME/USER_NAME
+    EVCardAttribute* attr = NULL;
+    QVariantMap map;
+    QString presenceMsg, presenceIcon, presenceDisplayState, presenceNickname;
+    QStringList caps;
+    
+    attr = (EVCardAttribute*)node->data;
+    if (!attr)
+      continue;
+    
+    // Continue if the attribute doesn't contain Online Account info
+    QString attributeName = e_vcard_attribute_get_name(attr);
+    if (!telepathyVCards.contains(attributeName))
+      continue;
+    
+    // Get the account URI
+    accountUri = e_vcard_attribute_get_value(attr);
+
+    // Get AccountPath and service provider for the roster contact associated to the attribute
+    // Note: there should be just one roster contact associated to the attribute
+    GList* rContacts = osso_abook_contact_find_roster_contacts_for_attribute(A_CONTACT(eContact), attr);
+    for (GList * node = rContacts; node != NULL; node = g_list_next(node)){
+      OssoABookContact* c = NULL;
+      c = A_CONTACT(node->data);
+      if (c) {
+       McAccount* a = NULL;
+       OssoABookPresence *p = NULL;
+       
+       a = osso_abook_contact_get_account(c);
+       if (a){
+         accountPath = a->name;
+         serviceProvider = mc_account_compat_get_profile(a);
+       }
+       
+       p = OSSO_ABOOK_PRESENCE(c);
+       presenceMsg = QString::fromUtf8(osso_abook_presence_get_presence_status_message(p));
+       presenceIcon = QString::fromUtf8(osso_abook_presence_get_icon_name(p));
+       // presenceState = QString::fromUtf8(osso_abook_presence_get_presence_status(p)); //BUG in osso_abook_presence_get_presence_status??
+       presenceDisplayState = QString::fromUtf8(osso_abook_presence_get_display_status(p));
+       presenceNickname = QString::fromUtf8(osso_abook_contact_get_display_name(c));
+       // Set OnlineAccount details
+       map.clear();
+       map[QContactOnlineAccount::FieldAccountUri] = accountUri;
+       map[QContactOnlineAccount::FieldCapabilities] = serviceProviderCapabilities(serviceProvider);
+       map[QContactOnlineAccount::FieldServiceProvider] = serviceProvider; // eg: facebook-chat,
+       map["AccountPath"] = accountPath;
+       setDetailValues(map, onlineAccount);
+       onlineAccounts << onlineAccount;
+    
+       // Set OnlinePresence details
+       map.clear();
+       map[QContactDetail::FieldLinkedDetailUris] = accountUri;
+       map[QContactPresence::FieldCustomMessage] = presenceMsg;
+       map[QContactPresence::FieldPresenceStateImageUrl] = presenceIcon;
+       map[QContactPresence::FieldPresenceStateText] = presenceDisplayState;
+       map[QContactPresence::FieldNickname] = presenceNickname;
+       //map[QContactPresence::FieldPresenceState] = telepathyStatusToPresenceState(presenceState);
+       //map[QContactPresence::FieldTimestamp] = ;
+       setDetailValues(map, contactPresence);
+       presences << contactPresence;
+      }
+    }
+  }
+}
+
+QContactOrganization* QContactABook::getOrganizationDetail(EContact *eContact) const
+{
+  QContactOrganization* rtn = new QContactOrganization;
+  QVariantMap map;
+  const char* org = CONST_CHAR(e_contact_get(eContact, E_CONTACT_ORG));
+  map[QContactOrganization::FieldName] = QString::fromUtf8(org);
+  FREE(org);
+  setDetailValues(map, rtn);
+  return rtn;
+}
+
+QList<QContactPhoneNumber*> QContactABook::getPhoneDetail(EContact *eContact) const
+{
+  QList<QContactPhoneNumber*> rtnList;
+  
+  GList *l = osso_abook_contact_get_attributes(eContact, EVC_TEL);
+  
+  for (GList *node = g_list_last(l); node != NULL; node = g_list_previous(node)) {
+    QContactPhoneNumber* phoneNumber = new QContactPhoneNumber;
+    QVariantMap map;
+    
+    EVCardAttribute *attr = static_cast<EVCardAttribute*>(node->data);
+    GList* p = e_vcard_attribute_get_param(attr, EVC_TYPE);
+    
+    //Set Contexts and SubTypes
+    while (p) {
+      QString value = CONST_CHAR(p->data);
+      
+      if (value == "HOME")
+        phoneNumber->setContexts(QContactDetail::ContextHome);
+      else if (value == "WORK")
+        phoneNumber->setContexts(QContactDetail::ContextWork);
+      else
+      if (value == "CELL")
+        phoneNumber->setSubTypes(QContactPhoneNumber::SubTypeMobile);
+      else if (value == "VOICE")
+        phoneNumber->setSubTypes(QContactPhoneNumber::SubTypeVoice);
+      
+      p = p->next;
+    }
+    g_list_free(p);
+    
+    //Set Phone Number
+    GList* phoneNumbers = e_vcard_attribute_get_values(attr);
+    const char* normalized = e_normalize_phone_number(CONST_CHAR(phoneNumbers->data)); //FIXME Valgrind complains about this
+    QString phoneNumberStr(normalized);
+    FREE(normalized);
+    map[QContactPhoneNumber::FieldNumber] = phoneNumberStr;
+    map[QContactDetail::FieldDetailUri] = QString::number(g_list_position(l, node));
+    setDetailValues(map, phoneNumber);
+    
+    rtnList << phoneNumber;
+  }
+  g_list_free(l);
+  
+  return rtnList;
+}
+
+QContactTimestamp* QContactABook::getTimestampDetail(EContact *eContact) const
+{
+   QContactTimestamp* rtn = new QContactTimestamp;
+   QVariantMap map;
+   const char* rev = CONST_CHAR(e_contact_get(eContact, E_CONTACT_REV));
+   map[QContactTimestamp::FieldModificationTimestamp] = QDateTime::fromString(rev, Qt::ISODate);
+   FREE(rev);
+   setDetailValues(map, rtn);
+   return rtn;
+}
+
+QContactThumbnail* QContactABook::getThumbnailDetail(EContact *eContact) const
+{
+  QContactThumbnail* rtn = new QContactThumbnail;
+  QVariantMap map;
+
+  OssoABookContact *aContact = A_CONTACT(eContact);
+  if (!aContact)
+    return rtn;
+
+  GdkPixbuf* pixbuf = osso_abook_avatar_get_image_rounded(OSSO_ABOOK_AVATAR(aContact), -1, -1, false, 0, NULL);
+  if (!GDK_IS_PIXBUF(pixbuf)){
+    FREE(pixbuf);
+    return rtn;
+  }
+
+  const uchar* bdata = (const uchar*)gdk_pixbuf_get_pixels(pixbuf);
+  QSize bsize(gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf));
+
+  //Convert GdkPixbuf to QPixmap
+  QImage::Format format = gdk_pixbuf_get_has_alpha(pixbuf) ? QImage::Format_ARGB32 : QImage::Format_RGB32;
+  int stride = gdk_pixbuf_get_rowstride(pixbuf);
+  QImage converted(bdata, bsize.width(), bsize.height(), stride, format);
+  converted = converted.rgbSwapped();
+  map[QContactThumbnail::FieldThumbnail] = converted;
+  g_object_unref(pixbuf);
+  setDetailValues(map, rtn);
+
+  return rtn;
+}
+
+QContactUrl* QContactABook::getUrlDetail(EContact *eContact) const
+{
+   QContactUrl* rtn = new QContactUrl;
+   QVariantMap map;
+   const char* url = CONST_CHAR(e_contact_get(eContact, E_CONTACT_HOMEPAGE_URL));
+   map[QContactUrl::FieldUrl] = url;
+   FREE(url);
+   setDetailValues(map, rtn);
+   return rtn;
+}
+
+static void addAttributeToAContact(const OssoABookContact* contact,
+                                   const QString& attrName, const QStringList& attrValues,
+                                   const QString& paramName = QString(), const QStringList& paramValues = QStringList())
+{
+  if (!contact)
+    return;
+  
+  EVCard *vcard = E_VCARD (contact);
+  EVCardAttribute *attr = NULL;
+  EVCardAttributeParam* param = NULL;
+  
+  QCM5_DEBUG << "Adding attribute" << attrName << "AttrValues:" << attrValues
+             << "ParamName:" << paramName << "ParamValues:" << paramValues;
+  
+  // Check if attrValues contains something
+  bool noValues = true;
+  foreach(QString s, attrValues){
+    if (!s.isEmpty()){
+      noValues = false;
+      break;
+    }
+  }
+  
+  if (attr) {
+    if (noValues){
+      e_vcard_remove_attribute(vcard, attr);
+      return;
+    } else {
+      e_vcard_attribute_remove_values(attr);
+    }
+  } else {
+    if (noValues)
+      return;
+    
+    // Create Attribute with right parameters
+    attr = e_vcard_attribute_new(NULL, qPrintable(attrName));
+    if (!paramName.isEmpty()){
+      param = e_vcard_attribute_param_new(qPrintable(paramName));
+      
+      foreach(QString paramV, paramValues)
+        e_vcard_attribute_param_add_value(param, qPrintable(paramV));
+
+      e_vcard_attribute_add_param(attr, param);
+    }
+    // Save the attribute to the VCard
+    e_vcard_add_attribute(vcard, attr);
+  }
+  
+  // Add values to the attribute
+  foreach(QString attrV, attrValues) {
+    e_vcard_attribute_add_value(attr, qPrintable(attrV));
+  }
+  
+  // Debugging
+  {
+    const char* dbgStr = e_vcard_to_string(vcard, EVC_FORMAT_VCARD_30);
+    QCM5_DEBUG << "Modified VCard" << dbgStr;
+    FREE(dbgStr);
+  }
+}
+
+OssoABookContact* QContactABook::convert(const QContact *contact, QContactManager::Error* error) const
+{
+  Q_CHECK_PTR(contact);
+
+  // first, check for uniqueness constraints.
+  // currently, it is only addresses, email addresses, phone numbers
+  // and online accounts which are NOT unique.
+  if (contact->details<QContactAvatar>().count() > 1) {
+      *error = QContactManager::LimitReachedError;
+      return 0;
+  }
+  if (contact->details<QContactBirthday>().count() > 1) {
+      *error = QContactManager::LimitReachedError;
+      return 0;
+  }
+  if (contact->details<QContactGender>().count() > 1) {
+      *error = QContactManager::LimitReachedError;
+      return 0;
+  }
+  if (contact->details<QContactName>().count() > 1) {
+      *error = QContactManager::LimitReachedError;
+      return 0;
+  }
+  if (contact->details<QContactNickname>().count() > 1) {
+      *error = QContactManager::LimitReachedError;
+      return 0;
+  }
+  if (contact->details<QContactNote>().count() > 1) {
+      *error = QContactManager::LimitReachedError;
+      return 0;
+  }
+  if (contact->details<QContactOrganization>().count() > 1) {
+      *error = QContactManager::LimitReachedError;
+      return 0;
+  }
+  if (contact->details<QContactThumbnail>().count() > 1) {
+      *error = QContactManager::LimitReachedError;
+      return 0;
+  }
+  if (contact->details<QContactUrl>().count() > 1) {
+      *error = QContactManager::LimitReachedError;
+      return 0;
+  }
+
+  OssoABookContact* rtn;
+  
+  // Get aContact if it exists or create a new one if it doesn't
+  QContactLocalId id = contact->localId();
+  QCM5_DEBUG << "Converting QContact id:" << id << " to aContact";
+  if (id) {
+    rtn = getAContact(id, error);
+    EVCardAttribute *uidAttr = e_vcard_get_attribute(E_VCARD(rtn), e_contact_vcard_attribute(E_CONTACT_UID));
+
+    // remove all current attributes, since we rewrite them all.
+    EVCardAttribute *attr;
+    GList *attr_list = e_vcard_get_attributes (E_VCARD (rtn));
+    while (attr_list) {
+        attr = static_cast<EVCardAttribute*>(attr_list->data);
+        attr_list = attr_list->next;
+        if (!osso_abook_contact_attribute_is_readonly (attr)) {
+            if (attr != uidAttr) {
+                // we don't remove the uid, since we are updating the contact.
+                e_vcard_remove_attribute (E_VCARD (rtn), attr);
+            }
+        }
+    }
+  } else {
+    rtn = osso_abook_contact_new();
+  }
+  
+  QList<QContactDetail> allDetails = contact->details();
+
+  foreach(const QContactDetail &detail, allDetails){
+    QString definitionName = detail.definitionName();
+    
+    QCM5_DEBUG << "Saving" << definitionName;
+    
+    //QContactDisplayLabel::DefinitionName
+    if (definitionName == QContactAddress::DefinitionName){
+      setAddressDetail(rtn, detail);
+    } else
+    if (definitionName == QContactAvatar::DefinitionName){
+      setAvatarDetail(rtn, detail);
+    } else
+    if (definitionName == QContactBirthday::DefinitionName){
+      setBirthdayDetail(rtn, detail);
+    } else
+    if (definitionName == QContactEmailAddress::DefinitionName){
+      setEmailDetail(rtn, detail);
+    } else
+    if (definitionName == QContactGender::DefinitionName){
+      setGenderDetail(rtn, detail);
+    } else
+    if (definitionName == QContactName::DefinitionName){
+      setNameDetail(rtn, detail);
+    } else
+    if (definitionName == QContactNickname::DefinitionName){
+      setNicknameDetail(rtn, detail);
+    } else
+    if (definitionName == QContactNote::DefinitionName){
+      setNoteDetail(rtn, detail);
+    } else
+    if (definitionName == QContactOnlineAccount::DefinitionName){
+      setOnlineAccountDetail(rtn, detail);
+    } else
+    if (definitionName == QContactOrganization::DefinitionName){
+      setOrganizationDetail(rtn, detail);
+    } else
+    if (definitionName == QContactPhoneNumber::DefinitionName){
+      setPhoneDetail(rtn, detail);
+    } else
+    if (definitionName == QContactThumbnail::DefinitionName){
+      setThumbnailDetail(rtn, detail);
+    } else
+    if (definitionName == QContactUrl::DefinitionName){
+      setUrlDetail(rtn, detail);
+    }
+  }  
+  
+  return rtn;
+}
+
+void QContactABook::setAddressDetail(const OssoABookContact* aContact, const QContactAddress& detail) const
+{
+  if (!aContact) return;
+  
+  uint detailUri;
+  const uint nAddressElems = 7;
+  QStringList adrAttrValues, 
+              lblAttrValues,
+              paramValues;
+  
+  // Get parameters
+  foreach(QString c, detail.contexts())
+    paramValues << c.toUpper();
+  
+  // Initialize adrAttrValues;
+  for (uint i = 0; i < nAddressElems; ++i)
+    adrAttrValues << "";
+
+  // Fill adrAttrValues
+  QVariantMap vm = detail.variantValues();
+  QMapIterator<QString, QVariant> i(vm);
+  
+  while (i.hasNext()) {
+    i.next();
+    int index = -1;
+    QString key = i.key();
+      
+    if (key == QContactAddress::FieldPostOfficeBox) index = 0;
+    else if (key == AddressFieldExtension) index = 1;
+    else if (key == QContactAddress::FieldStreet) index = 2;
+    else if (key == QContactAddress::FieldLocality) index = 3;
+    else if (key == QContactAddress::FieldRegion) index = 4;
+    else if (key == QContactAddress::FieldPostcode) index = 5;
+    else if (key == QContactAddress::FieldCountry) index = 6;  
+    else if (key == QContactDetail::FieldContext) continue;
+    else if (key == QContactDetail::FieldDetailUri) detailUri = i.value().toInt();
+    else {
+      //qWarning() << "Address contains an invalid field:" << key;
+      return;
+    }
+    
+    if (index != -1)
+      adrAttrValues[index] = i.value().toString();
+  }
+
+  // Fill lblAttrValues
+  QStringList labelValues;
+  labelValues << adrAttrValues[1] 
+              << adrAttrValues[2]
+              << adrAttrValues[0]
+              << adrAttrValues[3]
+              << adrAttrValues[4]
+              << adrAttrValues[5]
+              << adrAttrValues[6];
+  lblAttrValues << labelValues.join(", ");
+  
+  // Skip if adrAttrValues contains only empty strings
+  bool noValues = true;
+  foreach(QString s, adrAttrValues){
+    if (!s.isEmpty()){
+      noValues = false;
+      break;
+    }
+  } 
+  if (noValues)
+    return;
+  
+  // Saving LABEL and ADR attributes into the VCard
+  addAttributeToAContact(aContact, EVC_ADR, adrAttrValues, EVC_TYPE, paramValues);
+  
+  //BUG Label attribute contains a bug
+  //It contains TYPE(TYPE) if ADDRESS doesn't contain any parameter value.
+  if (paramValues.isEmpty())
+    paramValues << EVC_TYPE;
+  
+  addAttributeToAContact(aContact, EVC_LABEL, lblAttrValues, EVC_TYPE, paramValues);
+}
+
+void QContactABook::setThumbnailDetail(const OssoABookContact* aContact, const QContactThumbnail& detail) const
+{
+    if (!aContact) return;
+
+    EBook *book;
+    {
+      OssoABookRoster* roster = A_ROSTER(m_abookAgregator);
+      book = osso_abook_roster_get_book(roster);
+    }
+
+    QImage image = detail.thumbnail();
+    
+    if (image.isNull())
+      return;
+
+    if (image.hasAlphaChannel()) {
+        image = image.convertToFormat(QImage::Format_ARGB32);
+        image = image.rgbSwapped();
+    } else {
+        image = image.convertToFormat(QImage::Format_RGB888);
+    }
+
+    GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data(image.bits(), GDK_COLORSPACE_RGB,
+                                                 image.hasAlphaChannel(), 8,
+                                                 image.width(), image.height(),
+                                                 image.bytesPerLine(), 0, 0);
+
+    osso_abook_contact_set_pixbuf((OssoABookContact*)aContact, pixbuf, 0, 0);
+    g_object_unref(pixbuf);
+}
+
+void QContactABook::setAvatarDetail(const OssoABookContact* aContact, const QContactAvatar& detail) const
+{
+  Q_UNUSED(aContact)
+  Q_UNUSED(detail);
+  // XXX TODO: FIXME
+  // We should set the path of the avatar in EVC_PHOTO
+}
+
+void QContactABook::setBirthdayDetail(const OssoABookContact* aContact, const QContactBirthday& detail) const
+{
+  if (!aContact) return;
+  
+  QStringList attrValues;
+  attrValues << detail.value(QContactBirthday::FieldBirthday);
+  
+  addAttributeToAContact(aContact, EVC_BDAY, attrValues);
+}
+
+void QContactABook::setEmailDetail(const OssoABookContact* aContact, const QContactEmailAddress& detail) const
+{
+  if (!aContact) return;
+  QStringList attrValues,
+              paramValues;
+
+  QVariantMap vm = detail.variantValues();
+  QMapIterator<QString, QVariant> i(vm);
+  while (i.hasNext()) {
+    i.next();
+    QString key = i.key();
+    
+    // We don't want to save the Detail URI
+    if (key == QContactDetail::FieldDetailUri)
+      continue;
+    
+    if (key == QContactDetail::FieldContext)
+      paramValues << i.value().toString().toUpper();
+    else
+      attrValues << i.value().toString();
+  }
+  
+  addAttributeToAContact(aContact, EVC_EMAIL, attrValues, EVC_TYPE, paramValues);
+}
+
+void QContactABook::setGenderDetail(const OssoABookContact* aContact, const QContactGender& detail) const
+{
+  if (!aContact) return;
+  
+  QStringList attrValues;
+  attrValues << detail.value(QContactGender::FieldGender).toLower();
+  
+  addAttributeToAContact(aContact, "X-GENDER", attrValues);
+}
+
+void QContactABook::setNameDetail(const OssoABookContact* aContact, const QContactName& detail) const
+{
+  if (!aContact) return;
+  
+  QStringList attrValues;
+  // Save First and Last name in the N vcard attribute
+  {  
+    QStringList supportedDetailValues;
+    supportedDetailValues << QContactName::FieldLastName << QContactName::FieldFirstName;
+  
+    foreach(QString key, supportedDetailValues){
+      attrValues << detail.value(key);
+    }
+  
+    //REMOVE ME - We don't want to support custom label
+    if (attrValues[1].isEmpty()){
+      //qWarning() << "QContactName::FieldFirstName is empty";
+      attrValues[1] = detail.customLabel();
+    }
+  
+    addAttributeToAContact(aContact, EVC_N, attrValues);
+  }
+  
+  // Save Fist + Last name in the FN card attribute
+  {
+    attrValues << attrValues.join(" ");
+    addAttributeToAContact(aContact, EVC_FN, attrValues);
+  }
+}
+
+void QContactABook::setNicknameDetail(const OssoABookContact* aContact, const QContactNickname& detail) const
+{
+  if (!aContact) return;
+  
+  QStringList attrValues;
+  attrValues << detail.value(QContactNickname::FieldNickname);
+  
+  addAttributeToAContact(aContact, EVC_NICKNAME, attrValues);
+}
+
+void QContactABook::setNoteDetail(const OssoABookContact* aContact, const QContactNote& detail) const
+{
+  if (!aContact) return;
+  
+  QStringList attrValues;
+  attrValues << detail.value(QContactNote::FieldNote);
+  
+  addAttributeToAContact(aContact, EVC_NOTE, attrValues);
+}
+
+/*NOTE: Online details comes from Telepathy or can be added manually by the user.
+ *      OnlineDetals coming from Telepathy/Roster contacts can't be saved.
+ */
+void QContactABook::setOnlineAccountDetail(const OssoABookContact* aContact, const QContactOnlineAccount& detail) const
+{
+   if (!aContact)
+     return;
+   
+   Q_UNUSED(detail);
+}
+
+void QContactABook::setOrganizationDetail(const OssoABookContact* aContact, const QContactOrganization& detail) const
+{
+  if (!aContact) return;
+  
+  QStringList attrValues;
+  attrValues << detail.value(QContactOrganization::FieldName);
+  
+  addAttributeToAContact(aContact, EVC_ORG, attrValues);
+}
+
+void QContactABook::setPhoneDetail(const OssoABookContact* aContact, const QContactPhoneNumber& detail) const
+{
+  if (!aContact) return;
+  QStringList attrValues,
+              paramValues;
+
+  QVariantMap vm = detail.variantValues();
+  QMapIterator<QString, QVariant> i(vm);
+  while (i.hasNext()) {
+    i.next();
+    const QString key = i.key();
+    
+    // We don't want to save the Detail URI
+    if (key == QContactDetail::FieldDetailUri)
+      continue;
+    
+    if (key == QContactDetail::FieldContext ||
+        key == QContactPhoneNumber::FieldSubTypes){
+      QString value = i.value().toString();
+      if (value == QContactPhoneNumber::SubTypeMobile)
+        value = "CELL";
+      else if (value == QContactPhoneNumber::SubTypeVoice)
+        value = "VOICE";
+      paramValues << value.toUpper();
+    } else
+      attrValues << i.value().toString();
+  }
+  
+  // Avoid unsupported type
+  if (paramValues.isEmpty())
+    paramValues << "VOICE";
+
+  // new phone number detail.
+  addAttributeToAContact(aContact, EVC_TEL, attrValues, EVC_TYPE, paramValues);
+}
+
+void QContactABook::setUrlDetail(const OssoABookContact* aContact, const QContactUrl& detail) const
+{
+  if (!aContact) return;
+  
+  QStringList attrValues;
+  attrValues << detail.value(QContactUrl::FieldUrl);
+  
+  addAttributeToAContact(aContact, EVC_URL, attrValues);
+}