qtmobility/plugins/contacts/maemo5/qcontactabook.cpp
changeset 4 90517678cc4f
child 5 453da2cfceef
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qtmobility/plugins/contacts/maemo5/qcontactabook.cpp	Mon May 03 13:18:40 2010 +0300
@@ -0,0 +1,1930 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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>
+
+
+/* Error handling Macros */
+#define FATAL_IF_ERROR(x) if(x) { \
+                            QString message(x->message); \
+                            g_error_free(x); \
+                            qFatal(qPrintable(message)); \
+                          }
+#define WARNING_IF_ERROR(x) if(x) { \
+                            QString message(x->message); \
+                            g_error_free(x); \
+                            qWarning(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;
+};
+
+/* 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){
+    qWarning() << "d has been deleted";
+    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;
+  }
+  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){
+    qWarning() << "d has been deleted";
+    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;
+  }
+  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){
+    qWarning() << "d has been deleted";
+    return;
+  }
+  
+  const char **p;
+  QList<QContactLocalId> contactIds;
+  
+  for (p = ids; *p; ++p) {
+    QContactLocalId id = d->hash->take(*p);
+    QCM5_DEBUG << "Contact" << id << "has been removed";
+    if (id)
+      contactIds << id;
+  }
+  
+  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);
+  
+#if 0
+  //TEST List supported fields
+  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);
+#endif
+}
+
+/*! 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) {
+     qWarning() << "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);
+}
+
+QList<QContactLocalId> QContactABook::contactIds(const QContactFilter& filter, const QList<QContactSortOrder>& sortOrders, QContactManager::Error* error) const
+{
+  QList<QContactLocalId> rtn;
+  
+  /* 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);
+  }
+  
+  /* Matching action filter */
+  //NOTE The code was not really tested */
+  if(filter.type() == QContactFilter::ActionFilter){
+    QContactActionFilter af(filter);
+    /* This looks a bit strange for me */
+    QList<QContactActionDescriptor> descriptors = QContactAction::actionDescriptors(af.actionName(), af.vendorName(), af.implementationVersion());
+
+    GList *masterContacts = osso_abook_aggregator_list_master_contacts(m_abookAgregator);
+    for(; masterContacts; ){
+      OssoABookContact *masterContact = A_CONTACT(masterContacts->data);
+      bool match = contactActionsMatch(masterContact, descriptors);
+      if(!match) {
+        GList *rosterContacts = osso_abook_contact_get_roster_contacts(masterContact);
+        for(; rosterContacts && !match; ){
+          OssoABookContact *rosterContact = A_CONTACT(rosterContacts->data);
+          match = contactActionsMatch(rosterContact, descriptors);
+          rosterContacts = g_list_delete_link(rosterContacts, rosterContacts);
+        }
+        g_list_free(rosterContacts);
+      }
+      if(match){
+        EContact *contact = E_CONTACT(masterContact);
+        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];
+      }
+      masterContacts = g_list_delete_link(masterContacts, masterContacts);
+    }
+    *error = QContactManager::NoError;
+    return  rtn;
+  }
+  
+  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);
+  if (!aContact) {
+    qWarning() << "Unable to get a valid AContact";
+    *error = QContactManager::DoesNotExistError;
+    return new QContact;
+  }
+  
+  //Convert aContact => qContact
+  rtn = convert(E_CONTACT(aContact));
+  return rtn;
+}
+
+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->that->_jobRemovingCompleted();
+}
+
+//### FIXME error is not managed
+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);
+  if (!OSSO_ABOOK_IS_CONTACT(aContact)){
+    qWarning() << "aCtontact 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;
+  
+  //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));
+  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);
+  
+  return ok;
+}
+
+static void commitContactCB(EBook* book, EBookStatus  status, gpointer user_data)
+{
+  Q_UNUSED(book)
+  jobSharedData *sd = static_cast<jobSharedData*>(user_data);
+  
+  *sd->result = (status == E_BOOK_ERROR_OK) ? true : false;  
+  sd->that->_jobSavingCompleted();
+}
+
+static void addContactCB(EBook* book, EBookStatus  status, const char  *uid, gpointer user_data)
+{
+  jobSharedData *sd = static_cast<jobSharedData*>(user_data);
+  if (uid)
+    sd->uid = strdup(uid);
+  
+  //osso_abook_contact_set_roster(OssoABookContact *contact, OssoABookRoster *roster)
+  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);
+  }
+  
+  // Conver QContact to AContact
+  aContact = convert(contact);
+  if (!aContact){
+    *error = QContactManager::UnspecifiedError;
+    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;
+  
+  // Add/Commit the contact
+  uid = CONST_CHAR(e_contact_get_const(E_CONTACT (aContact), E_CONTACT_UID)); 
+  if (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);
+
+  // set the id of the contact.
+  QContactId cId;
+  cId.setLocalId(m_localIds[m_saveJobSD->uid]);
+  contact->setId(cId);
+  //free(m_saveJobSD->uid);
+  
+  return ok;
+}
+
+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 {
+    qWarning() << "Cannot find 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";
+          qWarning() << "Match flag not supported";
+      }
+      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] = "title";
+        hash[QContactPhoneNumber::DefinitionName] = "phone";
+        hash[QContactUrl::DefinitionName] = "homepage-url";
+      }
+  
+      QString eDetail = hash[f.detailDefinitionName()];
+      if (eDetail.isEmpty()){
+        qWarning() << "Unable to found an ebook detail for " << f.detailDefinitionName();
+        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){
+          qWarning() << "Query is null";
+          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){
+          qWarning() << "Query is null";
+          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";
+  }
+ 
+  //Debugging
+  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 */
+  QList<QContactOnlineAccount*> onlineAccountList = getOnlineAccountDetail(eContact);
+  QContactOnlineAccount* onlineAccount;
+  foreach(onlineAccount, onlineAccountList)
+    detailList << onlineAccount;
+
+  /* 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){
+      qWarning() << "Detail can't be saved into QContact";
+      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) const
+{
+  OssoABookContact* rtn = NULL;
+
+  QCM5_DEBUG << "Getting aContact with id " << m_localIds[contactId] << "local contactId is" << contactId;
+
+  if(QString(m_localIds[contactId]).compare("osso-abook-self") == 0) {
+    rtn = A_CONTACT(osso_abook_self_contact_get_default());
+  } else {
+    EBookQuery* query;
+    GList* contacts;
+
+    query = e_book_query_field_test(E_CONTACT_UID, E_BOOK_QUERY_IS, m_localIds[contactId]);
+    contacts = osso_abook_aggregator_find_contacts(m_abookAgregator, query);
+    if (query)
+        e_book_query_unref(query);
+
+    if (g_list_length(contacts) == 1) {
+      rtn = A_CONTACT(contacts->data);
+    } else {
+      qWarning("List is empty or several contacts have the same UID or contactId belongs to a roster contact.");
+    }
+    if (contacts)
+      g_list_free(contacts);
+  }
+
+  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)
+      qWarning("Unable to get valid localId for the specified eContaact UID");
+    rtn.setLocalId(localId);
+  }
+  return rtn;
+}
+
+QList<QContactAddress*> QContactABook::getAddressDetail(EContact *eContact) const
+{
+  QList<QContactAddress*> rtnList;
+
+  //Ordered list of Fields
+  QStringList addressFields;
+  addressFields << QContactAddress::FieldPostOfficeBox
+                << "Estension" //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)
+      qFatal("ADR attribute data is corrupted"); 
+    if (g_list_length(v) != 7){
+      g_list_free(v);
+      qCritical() << "ADR attribute data is corrupted"; 
+    }
+    int i = 0;
+    while (v){
+      map[addressFields[i]] = QString::fromLatin1(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::FieldMiddleName] = eContactName->
+    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] = CONST_CHAR(e_contact_get_const(eContact, E_CONTACT_GIVEN_NAME));
+    map[QContactName::FieldLastName] = 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] = 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::fromLatin1(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: FIXME
+//  QContactAvatar* rtn = new QContactAvatar;
+//  QVariantMap map;
+//
+//  OssoABookContact *aContact = A_CONTACT(eContact);
+//  if (!aContact)
+//    return rtn;
+//
+//  //GdkPixbuf* pixbuf = osso_abook_contact_get_avatar_pixbuf(aContact, NULL, NULL);
+//  GdkPixbuf* pixbuf = osso_abook_avatar_get_image_rounded(OSSO_ABOOK_AVATAR(aContact), 64, 64, true, 4, 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 converted(bdata, bsize.width(), bsize.height(), QImage::Format_ARGB32_Premultiplied);
+//  map[QContactAvatar::FieldPixmap] = QPixmap::fromImage(converted);
+//  g_object_unref(pixbuf);
+//  setDetailValues(map, rtn);
+//
+//  return rtn;
+
+    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 = QString::fromLatin1(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::fromLatin1(note);
+  FREE(note);
+  setDetailValues(map, rtn);
+  return rtn;
+}
+
+static const QStringList vcardsManagedByTelepathy(){
+  QStringList rtn;
+  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))
+      rtn << field;
+    vcardFields = vcardFields->next;
+  }
+  
+  return rtn;
+}
+
+QList<QContactOnlineAccount*> QContactABook::getOnlineAccountDetail(EContact *eContact) const
+{
+  QList<QContactOnlineAccount*> rtnList;
+  
+  QStringList evcardToSkip = vcardsManagedByTelepathy();
+  
+  // Gets info of online accounts from roster contacts associated to the master one  
+  if (!osso_abook_contact_is_roster_contact (A_CONTACT(eContact))) {
+    QContactOnlineAccount* rtn = new QContactOnlineAccount;
+    
+    GList *contacts = osso_abook_contact_get_roster_contacts(A_CONTACT(eContact));
+    GList *node;
+    for (node = contacts; node != NULL; node = g_list_next(node)){
+      OssoABookContact *rosterContact = A_CONTACT(node->data);
+     
+      McProfile* id = osso_abook_contact_get_profile(rosterContact);
+      McAccount* account = osso_abook_contact_get_account(rosterContact);
+      
+      // Avoid to look for Roster contacts into the VCard
+      QString accountVCard = QString::fromLatin1(mc_profile_get_vcard_field(id));
+      evcardToSkip.removeOne(accountVCard);
+      
+      // Presence
+      OssoABookPresence *presence = OSSO_ABOOK_PRESENCE (rosterContact);
+      TpConnectionPresenceType presenceType = osso_abook_presence_get_presence_type (presence);
+      QString presenceTypeString;
+      QContactPresence::PresenceState presenceTypeEnum;
+      switch (presenceType) {
+        case TP_CONNECTION_PRESENCE_TYPE_UNSET: presenceTypeString = "Unset"; presenceTypeEnum = QContactPresence::PresenceUnknown; break;
+        case TP_CONNECTION_PRESENCE_TYPE_OFFLINE: presenceTypeString = "Offline"; presenceTypeEnum = QContactPresence::PresenceOffline; break;
+        case TP_CONNECTION_PRESENCE_TYPE_AVAILABLE: presenceTypeString = "Available"; presenceTypeEnum = QContactPresence::PresenceAvailable; break;
+        case TP_CONNECTION_PRESENCE_TYPE_AWAY: presenceTypeString = "Away"; presenceTypeEnum = QContactPresence::PresenceAway; break;
+        case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY: presenceTypeString = "Extended Away"; presenceTypeEnum = QContactPresence::PresenceExtendedAway; break;
+        case TP_CONNECTION_PRESENCE_TYPE_HIDDEN: presenceTypeString = "Hidden"; presenceTypeEnum = QContactPresence::PresenceHidden; break;
+        case TP_CONNECTION_PRESENCE_TYPE_BUSY: presenceTypeString = "Busy"; presenceTypeEnum = QContactPresence::PresenceBusy; break;
+        case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN: presenceTypeString = "Unknown"; presenceTypeEnum = QContactPresence::PresenceUnknown; break;
+        case TP_CONNECTION_PRESENCE_TYPE_ERROR: presenceTypeString = "Error"; presenceTypeEnum = QContactPresence::PresenceUnknown; break;
+        default:
+          qCritical() << "Presence type is not vaild" << presenceType;
+      }
+      
+      QVariantMap map;
+      map[QContactOnlineAccount::FieldServiceProvider] = mc_profile_get_unique_name(id);
+      map[QContactOnlineAccount::FieldDetailUri] = mc_profile_get_unique_name(id); // use this as detail URI so we can link to presence.
+      map["AccountPath"] = account->name; //MCAccount name: variable part of the D-Bus object path.
+      
+      setDetailValues(map, rtn);
+    }
+    rtnList << rtn;
+    g_list_free (contacts);
+  }
+  
+  /* Users can add Online account details manually. Eg: IRC username.
+   * evcardToSkip stringlist contains evCard attributes that have been already processed.
+   */
+  GList *attributeList = e_vcard_get_attributes((EVCard*)eContact);
+  GList *node;
+
+  if (attributeList) {
+    for (node = attributeList; node != NULL; node = g_list_next (node)) {
+      EVCardAttribute* attr = (EVCardAttribute*)node->data;
+      if (!attr)
+        continue;
+      QString attributeName = QString::fromLatin1(e_vcard_attribute_get_name(attr));
+      
+      // Skip attributes processed scanning roster contacts.
+      if (!evcardToSkip.contains(attributeName))
+        continue;
+      
+      GList *params = e_vcard_attribute_get_params(attr);
+      GList *nodeP;
+      QString type;
+      // If the parameter list lenght is 1, X-OSSO-VALID is not specified
+      bool ossoValidIsOk = (g_list_length(params) == 1) ? true : false;
+
+      for (nodeP = params; nodeP != NULL; nodeP = g_list_next (nodeP)) {
+        EVCardAttributeParam* p = (EVCardAttributeParam*) nodeP->data;
+        QString paramName = QString::fromLatin1(e_vcard_attribute_param_get_name(p));
+        bool attrIsType = false;
+        bool attrIsOssoValid = false;
+        
+        //If type is empty check if the attribute is "TYPE"
+        if (type.isEmpty())
+          attrIsType = paramName.contains(EVC_TYPE);
+        
+        if(!ossoValidIsOk)
+          attrIsOssoValid = paramName.contains("X-OSSO-VALID");
+        
+        if (!attrIsType && !attrIsOssoValid) {
+          qWarning () << "Skipping attribute parameter checking for" << paramName;
+          continue;
+        }
+        
+        GList *values = e_vcard_attribute_param_get_values(p);
+        GList *node;
+        for (node = values; node != NULL; node = g_list_next (node)) {
+          QString attributeParameterValue = QString::fromLatin1(CONST_CHAR(node->data));
+          if (attrIsOssoValid) {
+            ossoValidIsOk = (attributeParameterValue == "yes")? true : false;
+            if (!ossoValidIsOk) {
+              qWarning() << "X-OSSO-VALID is false.";
+              break;
+            }
+          } else if (type.isEmpty()) {
+            type = attributeParameterValue;
+            if (type.isEmpty())
+              qCritical() << "TYPE is empty"; 
+          }
+        }
+        
+        if (ossoValidIsOk && !type.isEmpty()) {
+          QContactOnlineAccount* rtn = new QContactOnlineAccount;
+          QVariantMap map;
+          map[QContactOnlineAccount::FieldServiceProvider] = type;
+          setDetailValues(map, rtn);
+          rtnList << rtn;
+        }
+      }
+    }
+  }
+
+  return rtnList;
+}
+
+QContactOrganization* QContactABook::getOrganizationDetail(EContact *eContact) const
+{
+  QContactOrganization* rtn = new QContactOrganization;
+  QVariantMap map;
+  const char* title = CONST_CHAR(e_contact_get(eContact, E_CONTACT_TITLE));
+  map[QContactOrganization::FieldTitle] = title;
+  FREE(title);
+  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 = QString::fromLatin1(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;
+}
+
+
+QList<QContactPresence*> QContactABook::getPresenceDetail(EContact *eContact) const
+{
+  QList<QContactPresence*> rtnList;
+
+  QStringList evcardToSkip = vcardsManagedByTelepathy();
+
+  // Gets info of online accounts from roster contacts associated to the master one
+  if (!osso_abook_contact_is_roster_contact (A_CONTACT(eContact))) {
+    QContactPresence* rtn = new QContactPresence;
+
+    GList *contacts = osso_abook_contact_get_roster_contacts(A_CONTACT(eContact));
+    GList *node;
+    for (node = contacts; node != NULL; node = g_list_next(node)){
+      OssoABookContact *rosterContact = A_CONTACT(node->data);
+
+      McProfile* id = osso_abook_contact_get_profile(rosterContact);
+      McAccount* account = osso_abook_contact_get_account(rosterContact);
+
+      // Avoid to look for Roster contacts into the VCard
+      QString accountVCard = QString::fromLatin1(mc_profile_get_vcard_field(id));
+      evcardToSkip.removeOne(accountVCard);
+
+      // Presence
+      OssoABookPresence *presence = OSSO_ABOOK_PRESENCE (rosterContact);
+      TpConnectionPresenceType presenceType = osso_abook_presence_get_presence_type (presence);
+      QString presenceTypeString;
+      QContactPresence::PresenceState presenceTypeEnum;
+      switch (presenceType) {
+        case TP_CONNECTION_PRESENCE_TYPE_UNSET: presenceTypeString = "Unset"; presenceTypeEnum = QContactPresence::PresenceUnknown; break;
+        case TP_CONNECTION_PRESENCE_TYPE_OFFLINE: presenceTypeString = "Offline"; presenceTypeEnum = QContactPresence::PresenceOffline; break;
+        case TP_CONNECTION_PRESENCE_TYPE_AVAILABLE: presenceTypeString = "Available"; presenceTypeEnum = QContactPresence::PresenceAvailable; break;
+        case TP_CONNECTION_PRESENCE_TYPE_AWAY: presenceTypeString = "Away"; presenceTypeEnum = QContactPresence::PresenceAway; break;
+        case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY: presenceTypeString = "Extended Away"; presenceTypeEnum = QContactPresence::PresenceExtendedAway; break;
+        case TP_CONNECTION_PRESENCE_TYPE_HIDDEN: presenceTypeString = "Hidden"; presenceTypeEnum = QContactPresence::PresenceHidden; break;
+        case TP_CONNECTION_PRESENCE_TYPE_BUSY: presenceTypeString = "Busy"; presenceTypeEnum = QContactPresence::PresenceBusy; break;
+        case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN: presenceTypeString = "Unknown"; presenceTypeEnum = QContactPresence::PresenceUnknown; break;
+        case TP_CONNECTION_PRESENCE_TYPE_ERROR: presenceTypeString = "Error"; presenceTypeEnum = QContactPresence::PresenceUnknown; break;
+        default:
+          qCritical() << "Presence type is not valid" << presenceType;
+      }
+
+      QVariantMap map; // XXX FIXME
+      map[QContactPresence::FieldNickname] = osso_abook_contact_get_display_name(rosterContact);
+      map[QContactPresence::FieldPresenceState] = presenceTypeEnum;
+      map[QContactPresence::FieldPresenceStateText] = QString::fromLatin1(osso_abook_presence_get_presence_status_message(presence));
+      map[QContactPresence::FieldLinkedDetailUris] = mc_profile_get_unique_name(id); //use the unique name as a detail uri of the online account.
+      map["AccountPath"] = account->name; //MCAccount name: variable part of the D-Bus object path.
+
+      setDetailValues(map, rtn);
+    }
+    rtnList << rtn;
+    g_list_free (contacts);
+  }
+
+  /* Users can add Online account details manually. Eg: IRC username.
+   * evcardToSkip stringlist contains evCard attributes that have been already processed.
+   */
+  GList *attributeList = e_vcard_get_attributes((EVCard*)eContact);
+  GList *node;
+
+  if (attributeList) {
+    for (node = attributeList; node != NULL; node = g_list_next (node)) {
+      EVCardAttribute* attr = (EVCardAttribute*)node->data;
+      if (!attr)
+        continue;
+      QString attributeName = QString::fromLatin1(e_vcard_attribute_get_name(attr));
+
+      // Skip attributes processed scanning roster contacts.
+      if (!evcardToSkip.contains(attributeName))
+        continue;
+
+      GList *params = e_vcard_attribute_get_params(attr);
+      GList *nodeP;
+      QString type;
+      // If the parameter list lenght is 1, X-OSSO-VALID is not specified
+      bool ossoValidIsOk = (g_list_length(params) == 1) ? true : false;
+
+      for (nodeP = params; nodeP != NULL; nodeP = g_list_next (nodeP)) {
+        EVCardAttributeParam* p = (EVCardAttributeParam*) nodeP->data;
+        QString paramName = QString::fromLatin1(e_vcard_attribute_param_get_name(p));
+        bool attrIsType = false;
+        bool attrIsOssoValid = false;
+
+        //If type is empty check if the attribute is "TYPE"
+        if (type.isEmpty())
+          attrIsType = paramName.contains(EVC_TYPE);
+
+        if(!ossoValidIsOk)
+          attrIsOssoValid = paramName.contains("X-OSSO-VALID");
+
+        if (!attrIsType && !attrIsOssoValid) {
+          qWarning () << "Skipping attribute parameter checking for" << paramName;
+          continue;
+        }
+
+        GList *values = e_vcard_attribute_param_get_values(p);
+        GList *node;
+        for (node = values; node != NULL; node = g_list_next (node)) {
+          QString attributeParameterValue = QString::fromLatin1(CONST_CHAR(node->data));
+          if (attrIsOssoValid) {
+            ossoValidIsOk = (attributeParameterValue == "yes")? true : false;
+            if (!ossoValidIsOk) {
+              qWarning() << "X-OSSO-VALID is false.";
+              break;
+            }
+          } else if (type.isEmpty()) {
+            type = attributeParameterValue;
+            if (type.isEmpty())
+              qCritical() << "TYPE is empty";
+          }
+        }
+
+        if (ossoValidIsOk && !type.isEmpty()) {
+          QContactPresence* rtn = new QContactPresence;
+          QVariantMap map;
+          map[QContactPresence::FieldNickname] = QString::fromLatin1(e_vcard_attribute_get_value(attr));
+          map[QContactPresence::FieldLinkedDetailUris] = type; // XXX FIXME
+          setDetailValues(map, rtn);
+          rtnList << rtn;
+        }
+      }
+    }
+  }
+
+  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_contact_get_avatar_pixbuf(aContact, NULL, NULL);
+  GdkPixbuf* pixbuf = osso_abook_avatar_get_image_rounded(OSSO_ABOOK_AVATAR(aContact), 64, 64, true, 4, 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 converted(bdata, bsize.width(), bsize.height(), QImage::Format_ARGB32_Premultiplied);
+  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(),
+                                   bool overwrite = true,
+                                   const int index = 0)
+{
+  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
+             << "overwrite" << overwrite << "Index" << index;
+  
+  if (overwrite)
+  {
+    GList *attributeList = osso_abook_contact_get_attributes(E_CONTACT(contact), qPrintable(attrName));
+    
+    for (GList *node = g_list_last(attributeList); node != NULL; node = g_list_previous(node)) {
+      EVCardAttribute* eAttr = (EVCardAttribute*)node->data;
+      int pos =  g_list_position(attributeList, node);
+      
+      if (index > pos){
+        qWarning() << "Attribute doesn't found at position" << index;
+        return;
+      }
+      
+      if (index != pos)
+        continue;
+      
+      // Select the current EVCard Attribute if it contains the same parameters of
+      // attribute we want to modify/add
+      int matchedParams = 0;
+      GList* p = NULL;
+      p = e_vcard_attribute_get_param(eAttr, qPrintable(paramName));
+
+      while (p){
+          foreach(QString paramV, paramValues){
+          QString value = CONST_CHAR(p->data);
+          if (paramV == value)
+            ++matchedParams;
+        }
+        p = p->next;
+      }
+      g_list_free(p);
+
+      if (matchedParams == paramValues.count()) {
+        attr = eAttr;
+        break;
+      }    
+    }
+  }
+  
+  // 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) const
+{
+  Q_CHECK_PTR(contact);
+  
+  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);
+    // It's not safe to commit changes to a contact that has been modified.
+    // This problem affects attributes with the same name and parameters such as
+    // EMail, Address...
+    QContactTimestamp* ts = getTimestampDetail(E_CONTACT(rtn));
+    if (*ts != contact->detail<QContactTimestamp>()){
+      delete ts;
+      return NULL;
+    }
+    delete ts;
+  } 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 == "Estension") 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, true, detailUri);
+  
+  //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, true, detailUri);
+}
+
+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.format() != QImage::Format_ARGB32_Premultiplied)
+        image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+    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, book, 0);
+    g_object_unref(pixbuf);
+}
+
+void QContactABook::setAvatarDetail(const OssoABookContact* aContact, const QContactAvatar& detail) const
+{
+  Q_UNUSED(aContact)
+  Q_UNUSED(detail);
+// XXX TODO: FIXME
+//  if (!aContact) return;
+//
+//  EBook *book;
+//  {
+//    OssoABookRoster* roster = A_ROSTER(m_abookAgregator);
+//    book = osso_abook_roster_get_book(roster);
+//  }
+//
+//  QImage image = detail.pixmap().toImage();
+//  if (image.format() != QImage::Format_ARGB32_Premultiplied)
+//      image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
+//  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, book, 0);
+//  g_object_unref(pixbuf);
+}
+
+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, true, detail.detailUri().toInt());
+}
+
+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::FieldFirstName << QContactName::FieldLastName;
+  
+    foreach(QString key, supportedDetailValues){
+      attrValues << detail.value(key);
+    }
+  
+    //REMOVE ME - We don't want to support custom label
+    if (attrValues[0].isEmpty()){
+      qWarning() << "QContactName::FieldFirstName is empty";
+      attrValues[0] = 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::FieldTitle);
+  
+  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";
+      paramValues << value.toUpper();
+    } else
+      attrValues << i.value().toString();
+  }
+  
+  // Avoid unsupported type
+  if (paramValues.isEmpty())
+    paramValues << "VOICE";
+  
+  addAttributeToAContact(aContact, EVC_TEL, attrValues, EVC_TYPE, paramValues, true, detail.detailUri().toInt());
+}
+
+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);
+}