qtmobility/plugins/contacts/maemo5/qcontactabook.cpp
changeset 4 90517678cc4f
child 5 453da2cfceef
equal deleted inserted replaced
1:2b40d63a9c3d 4:90517678cc4f
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Mobility Components.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qcontactmaemo5backend_p.h"
       
    43 #include "qcontactabook_p.h"
       
    44 
       
    45 #include <QEventLoop>
       
    46 #include <libebook/e-book-util.h>
       
    47 
       
    48 
       
    49 /* Error handling Macros */
       
    50 #define FATAL_IF_ERROR(x) if(x) { \
       
    51                             QString message(x->message); \
       
    52                             g_error_free(x); \
       
    53                             qFatal(qPrintable(message)); \
       
    54                           }
       
    55 #define WARNING_IF_ERROR(x) if(x) { \
       
    56                             QString message(x->message); \
       
    57                             g_error_free(x); \
       
    58                             qWarning(qPrintable(message)); \
       
    59                           }
       
    60 
       
    61 /* Casting Macros */
       
    62 #define A_CONTACT(x) reinterpret_cast<OssoABookContact*>(x)
       
    63 #define A_ROSTER(x) reinterpret_cast<OssoABookRoster*>(x)
       
    64 #define CONST_CHAR(x) static_cast<const char*>(x)
       
    65 #define FREE(x) free((void*)x)
       
    66 
       
    67 struct cbSharedData{
       
    68   QContactIDsHash* hash;
       
    69   QContactABook *that;
       
    70 };
       
    71 
       
    72 struct jobSharedData{
       
    73    QContactABook* that;
       
    74    bool *result;
       
    75    char *uid;
       
    76 };
       
    77 
       
    78 /* QContactABook */
       
    79 QContactABook::QContactABook(QObject* parent) :QObject(parent), m_cbSD(0), m_deleteJobSD(0), m_saveJobSD(0)
       
    80 {
       
    81   //Initialize QContactDetail context list
       
    82   initAddressBook();
       
    83 }
       
    84 
       
    85 QContactABook::~QContactABook()
       
    86 {
       
    87   OssoABookAggregator *roster = reinterpret_cast<OssoABookAggregator*>(m_abookAgregator);
       
    88   if (g_signal_handler_is_connected(roster, m_contactAddedHandlerId))
       
    89       g_signal_handler_disconnect(roster, m_contactAddedHandlerId);
       
    90   if (g_signal_handler_is_connected(roster, m_contactChangedHandlerId))
       
    91       g_signal_handler_disconnect(roster, m_contactChangedHandlerId);
       
    92   if (g_signal_handler_is_connected(roster, m_contactRemovedHandlerId))
       
    93       g_signal_handler_disconnect(roster, m_contactRemovedHandlerId);
       
    94 
       
    95   // XXX FIXME: memory leak?
       
    96   //g_object_unref(m_abookAgregator);
       
    97   delete m_cbSD;
       
    98   m_cbSD = 0;
       
    99   delete m_deleteJobSD;
       
   100   m_deleteJobSD = 0;
       
   101   delete m_saveJobSD;
       
   102   m_saveJobSD = 0;
       
   103 }
       
   104 
       
   105 static void contactsAddedCB(OssoABookRoster *roster, OssoABookContact **contacts, gpointer data)
       
   106 {
       
   107   QCM5_DEBUG << "CONTACT ADDED";
       
   108   Q_UNUSED(roster)
       
   109   
       
   110   cbSharedData* d = static_cast<cbSharedData*>(data);
       
   111   if (!d){
       
   112     qWarning() << "d has been deleted";
       
   113     return;
       
   114   }
       
   115   
       
   116   OssoABookContact **p;
       
   117   QList<QContactLocalId> contactIds;
       
   118   
       
   119   for (p = contacts; *p; ++p) {
       
   120     if (osso_abook_contact_is_roster_contact(*p))
       
   121       continue;
       
   122     
       
   123     // Add a new localID to the local ID hash
       
   124     const char* uid = CONST_CHAR(e_contact_get_const(E_CONTACT(*p), E_CONTACT_UID));
       
   125     QContactLocalId id = d->hash->append(uid);
       
   126     
       
   127     if (id)
       
   128       contactIds << id;
       
   129   }
       
   130   d->that->_contactsAdded(contactIds);
       
   131 }
       
   132 
       
   133 static void contactsChangedCB(OssoABookRoster *roster, OssoABookContact **contacts, gpointer data)
       
   134 {
       
   135   QCM5_DEBUG << "CONTACT CHANGED";
       
   136   Q_UNUSED(roster)
       
   137   
       
   138   cbSharedData* d = static_cast<cbSharedData*>(data);
       
   139   if (!d){
       
   140     qWarning() << "d has been deleted";
       
   141     return;
       
   142   }
       
   143   
       
   144   OssoABookContact **p;
       
   145   QList<QContactLocalId> contactIds;
       
   146   
       
   147   for (p = contacts; *p; ++p) {
       
   148     if (osso_abook_contact_is_roster_contact(*p))
       
   149       continue;
       
   150     
       
   151     const char* uid = CONST_CHAR(e_contact_get_const(E_CONTACT(*p), E_CONTACT_UID));
       
   152     QContactLocalId id = d->hash->find(uid);
       
   153     //FREE(uid);
       
   154     if (id)
       
   155       contactIds << id;
       
   156   }
       
   157   d->that->_contactsChanged(contactIds);
       
   158 }
       
   159 
       
   160 static void contactsRemovedCB(OssoABookRoster *roster, const char **ids, gpointer data)
       
   161 {
       
   162   QCM5_DEBUG << "CONTACT REMOVED";
       
   163   Q_UNUSED(roster)
       
   164   
       
   165   cbSharedData* d = static_cast<cbSharedData*>(data);
       
   166   if (!d){
       
   167     qWarning() << "d has been deleted";
       
   168     return;
       
   169   }
       
   170   
       
   171   const char **p;
       
   172   QList<QContactLocalId> contactIds;
       
   173   
       
   174   for (p = ids; *p; ++p) {
       
   175     QContactLocalId id = d->hash->take(*p);
       
   176     QCM5_DEBUG << "Contact" << id << "has been removed";
       
   177     if (id)
       
   178       contactIds << id;
       
   179   }
       
   180   
       
   181   d->that->_contactsRemoved(contactIds);
       
   182 }
       
   183 
       
   184 void QContactABook::initAddressBook(){
       
   185   /* Open AddressBook */
       
   186   GError *gError = NULL;
       
   187   OssoABookRoster* roster = NULL;
       
   188   
       
   189   roster = osso_abook_aggregator_get_default(&gError);
       
   190   FATAL_IF_ERROR(gError)
       
   191   
       
   192   osso_abook_waitable_run((OssoABookWaitable *) roster, g_main_context_default(), &gError);
       
   193   FATAL_IF_ERROR(gError)
       
   194   
       
   195   if (!osso_abook_waitable_is_ready ((OssoABookWaitable *) roster, &gError))
       
   196     FATAL_IF_ERROR(gError)
       
   197   
       
   198   m_abookAgregator = reinterpret_cast<OssoABookAggregator*>(roster);
       
   199   
       
   200   /* Initialize local Id Hash */
       
   201   initLocalIdHash();
       
   202   
       
   203   /* Initialize callbacks shared data */
       
   204   m_cbSD = new cbSharedData;
       
   205   m_cbSD->hash = &m_localIds;
       
   206   m_cbSD->that = this;
       
   207    
       
   208   /* Setup signals */
       
   209   m_contactAddedHandlerId = g_signal_connect(roster, "contacts-added",
       
   210                    G_CALLBACK (contactsAddedCB), m_cbSD);
       
   211   m_contactChangedHandlerId = g_signal_connect(roster, "contacts-changed",
       
   212                    G_CALLBACK (contactsChangedCB), m_cbSD);
       
   213   m_contactRemovedHandlerId = g_signal_connect(roster, "contacts-removed",
       
   214                    G_CALLBACK (contactsRemovedCB), m_cbSD);
       
   215   
       
   216 #if 0
       
   217   //TEST List supported fields
       
   218   EBook *book = NULL;
       
   219   GList *l;
       
   220   book = osso_abook_roster_get_book(roster);
       
   221   e_book_get_supported_fields (book, &l, NULL);
       
   222   while (l) {
       
   223     qDebug() << "SUPPORTED FIELD" << (const char*)l->data;
       
   224     l = l->next;  
       
   225   }
       
   226   g_list_free(l);
       
   227 #endif
       
   228 }
       
   229 
       
   230 /*! Fill LocalId Hash associating an internal QContactLocalId to any
       
   231  *  master contact ID.
       
   232  *  NOTE: master contact IDs are string like "1" or "osso-abook-tmc1".
       
   233  */
       
   234 void QContactABook::initLocalIdHash()
       
   235 {  
       
   236    GList *contactList = NULL;
       
   237    GList *node;
       
   238    
       
   239    contactList = osso_abook_aggregator_list_master_contacts(m_abookAgregator);
       
   240 
       
   241    if (!contactList) {
       
   242      qWarning() << "There are no Master contacts. LocalId hash is empty.";
       
   243      return;
       
   244    }
       
   245    
       
   246    for (node = contactList; node != NULL; node = g_list_next (node)) {
       
   247      EContact *contact = E_CONTACT(node->data);
       
   248      const char* data = CONST_CHAR(e_contact_get_const(contact, E_CONTACT_UID));
       
   249      QByteArray eContactUID(data);
       
   250      //FREE(data);
       
   251      m_localIds << eContactUID; //FIXME MemLeak
       
   252      QCM5_DEBUG << "eContactID " << eContactUID << "has been stored in m_localIDs with key" << m_localIds[eContactUID];
       
   253      
       
   254      // Useful for debugging.
       
   255      if (QCM5_DEBUG_ENABLED) e_vcard_dump_structure((EVCard*)contact);
       
   256    }
       
   257    
       
   258    g_list_free(contactList);
       
   259 }
       
   260 
       
   261 QList<QContactLocalId> QContactABook::contactIds(const QContactFilter& filter, const QList<QContactSortOrder>& sortOrders, QContactManager::Error* error) const
       
   262 {
       
   263   QList<QContactLocalId> rtn;
       
   264   
       
   265   /* Sorting */
       
   266   //NOTE Native sorting is possible thanks to g_list_sort.
       
   267   //     It's limited just to one filter.
       
   268   //     Multi filters support need non native sorting.
       
   269   //     Native filtering needs a lot of coding since we need
       
   270   //     detailDefinitionName * detailFieldName functions
       
   271   //     to compare couple of contacts
       
   272   if (sortOrders.count()){
       
   273     QCM5_DEBUG << "Sorting...";
       
   274     // We don't need 
       
   275     // Fill Ids
       
   276     QList<QContactLocalId> Ids;
       
   277     {
       
   278       QList<QContactSortOrder> so;
       
   279       QContactManager::Error e;
       
   280       Ids = contactIds(filter, so, &e);
       
   281     }
       
   282       
       
   283     // Fill Contact List
       
   284     QList<QContact> contacts;
       
   285     foreach(QContactLocalId id, Ids){
       
   286       QContact *c;
       
   287       QContactManager::Error e;
       
   288       c = getQContact(id, &e);
       
   289       if (e == QContactManager::NoError)
       
   290 	contacts << *c;
       
   291       else
       
   292         *error = e;
       
   293     }
       
   294     
       
   295     // Non native sorting
       
   296     return QContactManagerEngine::sortContacts(contacts, sortOrders);
       
   297   }
       
   298   
       
   299   /* Matching action filter */
       
   300   //NOTE The code was not really tested */
       
   301   if(filter.type() == QContactFilter::ActionFilter){
       
   302     QContactActionFilter af(filter);
       
   303     /* This looks a bit strange for me */
       
   304     QList<QContactActionDescriptor> descriptors = QContactAction::actionDescriptors(af.actionName(), af.vendorName(), af.implementationVersion());
       
   305 
       
   306     GList *masterContacts = osso_abook_aggregator_list_master_contacts(m_abookAgregator);
       
   307     for(; masterContacts; ){
       
   308       OssoABookContact *masterContact = A_CONTACT(masterContacts->data);
       
   309       bool match = contactActionsMatch(masterContact, descriptors);
       
   310       if(!match) {
       
   311         GList *rosterContacts = osso_abook_contact_get_roster_contacts(masterContact);
       
   312         for(; rosterContacts && !match; ){
       
   313           OssoABookContact *rosterContact = A_CONTACT(rosterContacts->data);
       
   314           match = contactActionsMatch(rosterContact, descriptors);
       
   315           rosterContacts = g_list_delete_link(rosterContacts, rosterContacts);
       
   316         }
       
   317         g_list_free(rosterContacts);
       
   318       }
       
   319       if(match){
       
   320         EContact *contact = E_CONTACT(masterContact);
       
   321         const char* data = CONST_CHAR(e_contact_get_const(contact, E_CONTACT_UID));
       
   322         QByteArray localId(data);
       
   323         m_localIds << localId;
       
   324         rtn.append(m_localIds[localId]);
       
   325         QCM5_DEBUG << "eContactID " << localId << "has been stored in m_localIDs with key" << m_localIds[localId];
       
   326       }
       
   327       masterContacts = g_list_delete_link(masterContacts, masterContacts);
       
   328     }
       
   329     *error = QContactManager::NoError;
       
   330     return  rtn;
       
   331   }
       
   332   
       
   333   EBookQuery* query = convert(filter);
       
   334   
       
   335   GList* l = osso_abook_aggregator_find_contacts(m_abookAgregator, query);
       
   336   if (query)
       
   337       e_book_query_unref(query);
       
   338   
       
   339   while (l){
       
   340     EContact *contact = E_CONTACT(l->data);
       
   341     const char* data = CONST_CHAR(e_contact_get_const(contact, E_CONTACT_UID));
       
   342     QByteArray localId(data);
       
   343     m_localIds << localId;
       
   344     rtn.append(m_localIds[localId]);
       
   345     QCM5_DEBUG << "eContactID " << localId << "has been stored in m_localIDs with key" << m_localIds[localId];
       
   346     l = g_list_delete_link(l, l);
       
   347   }
       
   348   
       
   349   *error = QContactManager::NoError;
       
   350   return rtn;
       
   351 }
       
   352 
       
   353 QContact* QContactABook::getQContact(const QContactLocalId& contactId, QContactManager::Error* error) const
       
   354 {
       
   355   QContact *rtn;
       
   356   OssoABookContact* aContact = getAContact(contactId);
       
   357   if (!aContact) {
       
   358     qWarning() << "Unable to get a valid AContact";
       
   359     *error = QContactManager::DoesNotExistError;
       
   360     return new QContact;
       
   361   }
       
   362   
       
   363   //Convert aContact => qContact
       
   364   rtn = convert(E_CONTACT(aContact));
       
   365   return rtn;
       
   366 }
       
   367 
       
   368 static void delContactCB(EBook *book, EBookStatus status, gpointer closure)
       
   369 {
       
   370   Q_UNUSED(book);
       
   371   QCM5_DEBUG << "Contact Removed";
       
   372   
       
   373   jobSharedData *sd = static_cast<jobSharedData*>(closure);
       
   374   if (!sd)
       
   375     return;
       
   376   
       
   377   *sd->result = (status != E_BOOK_ERROR_OK &&
       
   378                  status != E_BOOK_ERROR_CONTACT_NOT_FOUND) ? false : true;  
       
   379   sd->that->_jobRemovingCompleted();
       
   380 }
       
   381 
       
   382 //### FIXME error is not managed
       
   383 bool QContactABook::removeContact(const QContactLocalId& contactId, QContactManager::Error* error)
       
   384 {
       
   385   Q_UNUSED(error);
       
   386   QMutexLocker locker(&m_delContactMutex);
       
   387 
       
   388   bool ok = false;
       
   389   
       
   390   OssoABookRoster *roster = A_ROSTER(m_abookAgregator);
       
   391   EBook *book = osso_abook_roster_get_book(roster);
       
   392   OssoABookContact *aContact = getAContact(contactId);
       
   393   if (!OSSO_ABOOK_IS_CONTACT(aContact)){
       
   394     qWarning() << "aCtontact is not a valid ABook contact"; 
       
   395     return false;
       
   396   }
       
   397   
       
   398   // ASync => Sync
       
   399   QEventLoop loop;                           
       
   400   connect(this, SIGNAL(jobRemovingCompleted()), &loop, SLOT(quit()));
       
   401   
       
   402   // Prepare shared data
       
   403   if (m_deleteJobSD){
       
   404     delete m_deleteJobSD;
       
   405     m_deleteJobSD = 0;
       
   406   }
       
   407   m_deleteJobSD = new jobSharedData;
       
   408   m_deleteJobSD->that = this;
       
   409   m_deleteJobSD->result = &ok;
       
   410   
       
   411   //Remove photos
       
   412   EContactPhoto *photo = NULL;
       
   413   GFile *file = NULL;
       
   414   photo = (EContactPhoto*) e_contact_get(E_CONTACT (aContact), E_CONTACT_PHOTO);
       
   415   if (photo) {
       
   416     if (photo->type == E_CONTACT_PHOTO_TYPE_URI && photo->data.uri) {
       
   417       file = g_file_new_for_uri(photo->data.uri);
       
   418       g_file_delete(file, NULL, NULL);
       
   419       g_object_unref (file);
       
   420     }
       
   421     e_contact_photo_free (photo);
       
   422   }
       
   423   
       
   424   //Remove all roster contacts from their roster
       
   425   GList* rosterContacts = NULL;
       
   426   rosterContacts = osso_abook_contact_get_roster_contacts(aContact);
       
   427   const char *masterUid = CONST_CHAR(e_contact_get_const(E_CONTACT(aContact), E_CONTACT_UID));
       
   428   while(rosterContacts){
       
   429     OssoABookContact *rosterContact = A_CONTACT(rosterContacts->data);
       
   430     osso_abook_contact_reject_for_uid(rosterContact, masterUid, NULL);
       
   431     rosterContacts = rosterContacts->next;
       
   432   }  
       
   433   
       
   434   // Remove contact
       
   435   e_book_async_remove_contact(book, E_CONTACT(aContact),
       
   436                               delContactCB, m_deleteJobSD);
       
   437   
       
   438   loop.exec(QEventLoop::AllEvents|QEventLoop::WaitForMoreEvents);
       
   439   
       
   440   return ok;
       
   441 }
       
   442 
       
   443 static void commitContactCB(EBook* book, EBookStatus  status, gpointer user_data)
       
   444 {
       
   445   Q_UNUSED(book)
       
   446   jobSharedData *sd = static_cast<jobSharedData*>(user_data);
       
   447   
       
   448   *sd->result = (status == E_BOOK_ERROR_OK) ? true : false;  
       
   449   sd->that->_jobSavingCompleted();
       
   450 }
       
   451 
       
   452 static void addContactCB(EBook* book, EBookStatus  status, const char  *uid, gpointer user_data)
       
   453 {
       
   454   jobSharedData *sd = static_cast<jobSharedData*>(user_data);
       
   455   if (uid)
       
   456     sd->uid = strdup(uid);
       
   457   
       
   458   //osso_abook_contact_set_roster(OssoABookContact *contact, OssoABookRoster *roster)
       
   459   commitContactCB(book, status, user_data);
       
   460 }
       
   461 
       
   462 bool QContactABook::saveContact(QContact* contact, QContactManager::Error* error)
       
   463 {
       
   464   QMutexLocker locker(&m_saveContactMutex);
       
   465   
       
   466   if (!contact) {
       
   467     *error = QContactManager::BadArgumentError;
       
   468     return false;
       
   469   }
       
   470 
       
   471   bool ok = false;
       
   472   
       
   473   OssoABookContact *aContact = NULL;
       
   474   const char *uid;
       
   475   EBook *book;
       
   476   {
       
   477     OssoABookRoster* roster = reinterpret_cast<OssoABookRoster*>(m_abookAgregator);
       
   478     book = osso_abook_roster_get_book(roster);
       
   479   }
       
   480   
       
   481   // Conver QContact to AContact
       
   482   aContact = convert(contact);
       
   483   if (!aContact){
       
   484     *error = QContactManager::UnspecifiedError;
       
   485     return false;
       
   486   }  
       
   487 
       
   488   // ASync => Sync
       
   489   QEventLoop loop;
       
   490   connect(this, SIGNAL(jobSavingCompleted()), &loop, SLOT(quit()));
       
   491 
       
   492   // Prepare shared data
       
   493   if (m_saveJobSD){
       
   494     delete m_saveJobSD;
       
   495     m_saveJobSD = 0;
       
   496   }
       
   497   m_saveJobSD = new jobSharedData;
       
   498   m_saveJobSD->that = this;
       
   499   m_saveJobSD->result = &ok;
       
   500   
       
   501   // Add/Commit the contact
       
   502   uid = CONST_CHAR(e_contact_get_const(E_CONTACT (aContact), E_CONTACT_UID)); 
       
   503   if (uid) {
       
   504     osso_abook_contact_async_commit(aContact, book, commitContactCB, m_saveJobSD);
       
   505   } else {
       
   506     osso_abook_contact_async_add(aContact, book, addContactCB, m_saveJobSD);
       
   507   }
       
   508   
       
   509   loop.exec(QEventLoop::AllEvents|QEventLoop::WaitForMoreEvents);
       
   510 
       
   511   // set the id of the contact.
       
   512   QContactId cId;
       
   513   cId.setLocalId(m_localIds[m_saveJobSD->uid]);
       
   514   contact->setId(cId);
       
   515   //free(m_saveJobSD->uid);
       
   516   
       
   517   return ok;
       
   518 }
       
   519 
       
   520 QContactLocalId QContactABook::selfContactId(QContactManager::Error* errors) const
       
   521 {
       
   522   QContactLocalId id;
       
   523   EContact *self = E_CONTACT(osso_abook_self_contact_get_default());
       
   524   if (self) {
       
   525     *errors = QContactManager::NoError;
       
   526     const char* data = CONST_CHAR(e_contact_get_const(self, E_CONTACT_UID));
       
   527     const QByteArray eContactUID(data);
       
   528     QContactLocalId localId = m_localIds[eContactUID];
       
   529     if (localId)
       
   530       id = localId;
       
   531     else {
       
   532       m_localIds << eContactUID; //FIXME MemLeak
       
   533       id = m_localIds[eContactUID];
       
   534       QCM5_DEBUG << "eContactID " << eContactUID << "has been stored in m_localIDs with key" << id;
       
   535     }
       
   536   } else {
       
   537     qWarning() << "Cannot find self contact";
       
   538     *errors = QContactManager::DoesNotExistError;
       
   539     id = 0;
       
   540   }
       
   541   g_object_unref(self);
       
   542   return id;
       
   543 }
       
   544 
       
   545 bool QContactABook::contactActionsMatch(OssoABookContact *contact, QList<QContactActionDescriptor> descriptors) const
       
   546 {
       
   547   OssoABookCapsFlags capsFlags = osso_abook_caps_get_capabilities(OSSO_ABOOK_CAPS(contact));
       
   548 
       
   549   if(capsFlags & OSSO_ABOOK_CAPS_NONE)
       
   550     return false;
       
   551 
       
   552   /* ActionNames could be incorrect */
       
   553   OssoABookCapsFlags actionFlags = OSSO_ABOOK_CAPS_NONE;
       
   554   for(int i = 0; i < descriptors.size(); i++){
       
   555     QString actionName = descriptors.at(i).actionName();
       
   556     QCM5_DEBUG << actionName;
       
   557     if(!actionName.compare("Phone"))
       
   558       actionFlags = (OssoABookCapsFlags)(actionFlags | OSSO_ABOOK_CAPS_PHONE);
       
   559     else if(!actionName.compare("Voice"))
       
   560       actionFlags = (OssoABookCapsFlags)(actionFlags | OSSO_ABOOK_CAPS_VOICE);
       
   561     else if(!actionName.compare("SendEmail"))
       
   562       actionFlags = (OssoABookCapsFlags)(actionFlags | OSSO_ABOOK_CAPS_EMAIL);
       
   563     else if(!actionName.compare("Chat"))
       
   564       actionFlags = (OssoABookCapsFlags)(actionFlags | OSSO_ABOOK_CAPS_CHAT);
       
   565     else if(!actionName.compare("ChatAdditional"))
       
   566       actionFlags = (OssoABookCapsFlags)(actionFlags | OSSO_ABOOK_CAPS_CHAT_ADDITIONAL);
       
   567     else if(!actionName.compare("VoiceAdditional"))
       
   568       actionFlags = (OssoABookCapsFlags)(actionFlags | OSSO_ABOOK_CAPS_VOICE_ADDITIONAL);
       
   569     else if(!actionName.compare("Video"))
       
   570       actionFlags = (OssoABookCapsFlags)(actionFlags | OSSO_ABOOK_CAPS_VIDEO);
       
   571     else if(!actionName.compare("Addressbook"))
       
   572       actionFlags = (OssoABookCapsFlags)(actionFlags | OSSO_ABOOK_CAPS_ADDRESSBOOK);
       
   573   }
       
   574   return ((actionFlags & capsFlags) == actionFlags);
       
   575 }
       
   576 
       
   577 EBookQuery* QContactABook::convert(const QContactFilter& filter) const
       
   578 {
       
   579   EBookQuery* query = NULL;
       
   580   
       
   581   switch(filter.type()){
       
   582     case QContactFilter::DefaultFilter:
       
   583     {
       
   584       QCM5_DEBUG << "QContactFilter::DefaultFilter";
       
   585       query = e_book_query_any_field_contains(""); //Match all contacts
       
   586     } break;
       
   587     case QContactFilter::LocalIdFilter:
       
   588     {
       
   589       QCM5_DEBUG << "LocalIdFilter";
       
   590       const QContactLocalIdFilter f(filter);
       
   591       QList<QContactLocalId> ids = f.ids();
       
   592       if (ids.isEmpty())
       
   593         return NULL;
       
   594       
       
   595       query= NULL;
       
   596       foreach(const QContactLocalId id, ids){
       
   597         EBookQuery* q = NULL;
       
   598         
       
   599         // Looking for the eContact local id inside the localId hash
       
   600         const char* eContactId = m_localIds[id];
       
   601         if (!eContactId[0])
       
   602           return NULL;
       
   603         
       
   604         q = e_book_query_field_test(E_CONTACT_UID, E_BOOK_QUERY_IS, eContactId);
       
   605         if (!q)
       
   606           continue;
       
   607         if (query)
       
   608           query = e_book_query_orv(query, q, NULL);
       
   609         else
       
   610           query = q;
       
   611       }
       
   612     } break;
       
   613     case QContactFilter::ContactDetailFilter:
       
   614     {
       
   615       QCM5_DEBUG << "ContactDetailFilter";
       
   616       const QContactDetailFilter f(filter);
       
   617       QString queryStr;
       
   618       if (!f.value().isValid())
       
   619         return NULL;
       
   620       switch (f.matchFlags()){
       
   621         case QContactFilter::MatchContains: queryStr = "contains"; break;
       
   622         case QContactFilter::MatchFixedString:
       
   623         case QContactFilter::MatchCaseSensitive:
       
   624         case QContactFilter::MatchExactly: queryStr = "is"; break;
       
   625         case QContactFilter::MatchStartsWith: queryStr = "beginswith"; break;
       
   626         case QContactFilter::MatchEndsWith: queryStr = "endswith"; break;
       
   627         default:
       
   628           queryStr = "contains";
       
   629           qWarning() << "Match flag not supported";
       
   630       }
       
   631       static QHash<QString,QString> hash;
       
   632       if (hash.isEmpty()){
       
   633         hash[QContactAddress::DefinitionName] = "address";
       
   634         hash[QContactBirthday::DefinitionName] = "birth-date";
       
   635         hash[QContactDisplayLabel::DefinitionName] = "full-name"; //hack
       
   636         hash[QContactEmailAddress::DefinitionName] = "email";
       
   637         hash[QContactName::DefinitionName] = "full-name";
       
   638         hash[QContactNickname::DefinitionName] = "nickname";
       
   639         hash[QContactNote::DefinitionName] = "note";
       
   640         hash[QContactOrganization::DefinitionName] = "title";
       
   641         hash[QContactPhoneNumber::DefinitionName] = "phone";
       
   642         hash[QContactUrl::DefinitionName] = "homepage-url";
       
   643       }
       
   644   
       
   645       QString eDetail = hash[f.detailDefinitionName()];
       
   646       if (eDetail.isEmpty()){
       
   647         qWarning() << "Unable to found an ebook detail for " << f.detailDefinitionName();
       
   648         return NULL;
       
   649       }
       
   650       queryStr = queryStr + " \"" + eDetail + "\" \"" + f.value().toString() + "\"";
       
   651       query = e_book_query_from_string(qPrintable(queryStr));
       
   652     } break;
       
   653     case QContactFilter::ActionFilter:
       
   654       QCM5_DEBUG << "ActionFilter"; //eQuery doesn't support ActionFilter
       
   655       break;
       
   656     case QContactFilter::IntersectionFilter:
       
   657     {
       
   658       QCM5_DEBUG << "IntersectionFilter";
       
   659       const QContactIntersectionFilter f(filter);
       
   660       const QList<QContactFilter>  fs= f.filters();
       
   661       QContactFilter i;
       
   662       foreach(i, fs){
       
   663         EBookQuery* q = convert(i);
       
   664         if (!q){
       
   665           qWarning() << "Query is null";
       
   666           continue;
       
   667         }
       
   668         if (query)
       
   669           query = e_book_query_andv(query, q, NULL);
       
   670         else
       
   671           query = q;
       
   672       } 
       
   673     } break;
       
   674     case QContactFilter::UnionFilter:
       
   675     {
       
   676       QCM5_DEBUG << "UnionFilter";
       
   677       const QContactUnionFilter f(filter);
       
   678       const QList<QContactFilter>  fs= f.filters();
       
   679       QContactFilter i;
       
   680       foreach(i, fs){
       
   681         EBookQuery* q = convert(i);
       
   682         if (!q){
       
   683           qWarning() << "Query is null";
       
   684           continue;
       
   685         }
       
   686         if (query)
       
   687           query = e_book_query_orv(query, q, NULL);
       
   688         else
       
   689           query = q;
       
   690       }
       
   691     } break;
       
   692     case QContactFilter::InvalidFilter:
       
   693     {
       
   694       QCM5_DEBUG << "InvalidFilter";
       
   695       query = e_book_query_from_string("(is \"id\" \"-1\")");
       
   696     } break;
       
   697     default:
       
   698       QCM5_DEBUG << "Filter not supported";
       
   699   }
       
   700  
       
   701   //Debugging
       
   702   const char *queryString = e_book_query_to_string(query);
       
   703   QCM5_DEBUG << "QUERY" << queryString;
       
   704   FREE(queryString);
       
   705   
       
   706   return query;
       
   707 } 
       
   708 
       
   709 QContact* QContactABook::convert(EContact *eContact) const
       
   710 {
       
   711   QContact *contact = new QContact();
       
   712   QList<QContactDetail*> detailList;
       
   713 
       
   714   /* Id */
       
   715   contact->setId(getContactId(eContact));
       
   716 
       
   717   /* Address */
       
   718   QList<QContactAddress*> addressList = getAddressDetail(eContact);
       
   719   QContactAddress* address;
       
   720   foreach(address, addressList)
       
   721     detailList << address;
       
   722 
       
   723   /* Avatar */
       
   724   detailList << getAvatarDetail(eContact); // XXX TODO: FIXME
       
   725   detailList << getThumbnailDetail(eContact);
       
   726 
       
   727   /* BirthDay */
       
   728   detailList << getBirthdayDetail(eContact);
       
   729 
       
   730   /* Email */
       
   731   QList<QContactEmailAddress*> emailList = getEmailDetail(eContact);
       
   732   QContactEmailAddress* email;
       
   733   foreach(email, emailList)
       
   734     detailList << email;
       
   735 
       
   736   /* Gender */
       
   737   detailList << getGenderDetail(eContact);
       
   738 
       
   739   /* Global UID*/
       
   740   detailList << getGuidDetail(eContact);
       
   741 
       
   742   /* Name & NickName*/
       
   743   detailList << getNameDetail(eContact);
       
   744   detailList << getNicknameDetail(eContact);
       
   745 
       
   746   /* Note */
       
   747   detailList << getNoteDetail(eContact);
       
   748 
       
   749   /* Online Account */
       
   750   QList<QContactOnlineAccount*> onlineAccountList = getOnlineAccountDetail(eContact);
       
   751   QContactOnlineAccount* onlineAccount;
       
   752   foreach(onlineAccount, onlineAccountList)
       
   753     detailList << onlineAccount;
       
   754 
       
   755   /* Organization */
       
   756   detailList << getOrganizationDetail(eContact);
       
   757 
       
   758   /* Phone*/
       
   759   QList<QContactPhoneNumber*> phoneNumberList = getPhoneDetail(eContact);
       
   760   QContactPhoneNumber* phoneNumber;
       
   761   foreach(phoneNumber, phoneNumberList)
       
   762     detailList << phoneNumber;
       
   763 
       
   764   /* TimeStamp */
       
   765   detailList << getTimestampDetail(eContact);
       
   766 
       
   767   /* Url */
       
   768   detailList << getUrlDetail(eContact);
       
   769 
       
   770   bool ok;
       
   771   QContactDetail* detail;
       
   772  
       
   773   foreach(detail, detailList){
       
   774     if (detail->isEmpty()){
       
   775       delete detail;
       
   776       continue;
       
   777     }
       
   778 
       
   779     ok = contact->saveDetail(detail);
       
   780     if (!ok){
       
   781       qWarning() << "Detail can't be saved into QContact";
       
   782       delete detail;
       
   783       continue;
       
   784     }
       
   785     delete detail;
       
   786   }
       
   787 
       
   788   return contact;
       
   789 }
       
   790 
       
   791 bool QContactABook::setDetailValues(const QVariantMap& data, QContactDetail* detail) const
       
   792 {
       
   793   QMapIterator<QString, QVariant> i(data);
       
   794   QVariant value;
       
   795   while (i.hasNext()) {
       
   796      i.next();
       
   797      value = i.value();
       
   798      
       
   799      if (value.isNull())
       
   800        continue;
       
   801      
       
   802      if (value.canConvert<QString>() && value.toString().isEmpty())
       
   803        continue;
       
   804      
       
   805      QCM5_DEBUG << "Set" << i.key() << i.value();
       
   806      detail->setValue(i.key(), i.value());
       
   807 
       
   808   }
       
   809   
       
   810   if (detail->isEmpty())
       
   811     return false;
       
   812   return true;
       
   813 }
       
   814 
       
   815 OssoABookContact* QContactABook::getAContact(const QContactLocalId& contactId) const
       
   816 {
       
   817   OssoABookContact* rtn = NULL;
       
   818 
       
   819   QCM5_DEBUG << "Getting aContact with id " << m_localIds[contactId] << "local contactId is" << contactId;
       
   820 
       
   821   if(QString(m_localIds[contactId]).compare("osso-abook-self") == 0) {
       
   822     rtn = A_CONTACT(osso_abook_self_contact_get_default());
       
   823   } else {
       
   824     EBookQuery* query;
       
   825     GList* contacts;
       
   826 
       
   827     query = e_book_query_field_test(E_CONTACT_UID, E_BOOK_QUERY_IS, m_localIds[contactId]);
       
   828     contacts = osso_abook_aggregator_find_contacts(m_abookAgregator, query);
       
   829     if (query)
       
   830         e_book_query_unref(query);
       
   831 
       
   832     if (g_list_length(contacts) == 1) {
       
   833       rtn = A_CONTACT(contacts->data);
       
   834     } else {
       
   835       qWarning("List is empty or several contacts have the same UID or contactId belongs to a roster contact.");
       
   836     }
       
   837     if (contacts)
       
   838       g_list_free(contacts);
       
   839   }
       
   840 
       
   841   return rtn;
       
   842 }
       
   843 
       
   844 QContactId QContactABook::getContactId(EContact *eContact) const
       
   845 {
       
   846   QContactId rtn;
       
   847   /* Set LocalId */
       
   848   {
       
   849     const char* data = CONST_CHAR(e_contact_get_const(eContact, E_CONTACT_UID));
       
   850     const QByteArray eContactUID(data);
       
   851     QContactLocalId localId = m_localIds[eContactUID];
       
   852     if (!localId)
       
   853       qWarning("Unable to get valid localId for the specified eContaact UID");
       
   854     rtn.setLocalId(localId);
       
   855   }
       
   856   return rtn;
       
   857 }
       
   858 
       
   859 QList<QContactAddress*> QContactABook::getAddressDetail(EContact *eContact) const
       
   860 {
       
   861   QList<QContactAddress*> rtnList;
       
   862 
       
   863   //Ordered list of Fields
       
   864   QStringList addressFields;
       
   865   addressFields << QContactAddress::FieldPostOfficeBox
       
   866                 << "Estension" //FIXME I'm not sure we have to use a new field 
       
   867                 << QContactAddress::FieldStreet
       
   868                 << QContactAddress::FieldLocality
       
   869                 << QContactAddress::FieldRegion 
       
   870                 << QContactAddress::FieldPostcode 
       
   871                 << QContactAddress::FieldCountry;
       
   872   
       
   873   GList* attrList = osso_abook_contact_get_attributes(eContact, EVC_ADR);
       
   874   
       
   875   for (GList *node = g_list_last(attrList); node != NULL; node = g_list_previous(node)) {
       
   876     QContactAddress *address = new QContactAddress;
       
   877     QVariantMap map;
       
   878  
       
   879     EVCardAttribute *attr = static_cast<EVCardAttribute*>(node->data);
       
   880     
       
   881     
       
   882     // Set Address Context using attribute parameter value
       
   883     EVCardAttributeParam *param = NULL;
       
   884     GList* p = e_vcard_attribute_get_params(attr);
       
   885     
       
   886     if (p)
       
   887       param = static_cast<EVCardAttributeParam*>(p->data);
       
   888     
       
   889     if (param){
       
   890       GList *v = e_vcard_attribute_param_get_values(param);
       
   891       QString context = CONST_CHAR(v->data);
       
   892       if (context == "HOME")
       
   893         address->setContexts(QContactDetail::ContextHome);
       
   894       else if (context == "WORK")
       
   895         address->setContexts(QContactDetail::ContextWork);
       
   896     }
       
   897     
       
   898     // Set Address Values
       
   899     GList *v = NULL;
       
   900     v =e_vcard_attribute_get_values(attr);
       
   901     if (!v)
       
   902       qFatal("ADR attribute data is corrupted"); 
       
   903     if (g_list_length(v) != 7){
       
   904       g_list_free(v);
       
   905       qCritical() << "ADR attribute data is corrupted"; 
       
   906     }
       
   907     int i = 0;
       
   908     while (v){
       
   909       map[addressFields[i]] = QString::fromLatin1(CONST_CHAR(v->data));
       
   910       i++;
       
   911       v = v->next;
       
   912     }
       
   913     g_list_free(v);
       
   914     map[QContactDetail::FieldDetailUri] = QString::number(g_list_position(attrList, node));
       
   915     setDetailValues(map, address);
       
   916     
       
   917     rtnList << address;
       
   918   }
       
   919   
       
   920   g_list_free(attrList);
       
   921   
       
   922   return rtnList;
       
   923 }
       
   924 
       
   925 QContactName* QContactABook::getNameDetail(EContact *eContact) const
       
   926 {
       
   927   QContactName* rtn = new QContactName;
       
   928   QVariantMap map;
       
   929 
       
   930   //Try to get the structure (looks that this is not supported in Maemo5)
       
   931   EContactName* eContactName = static_cast<EContactName*> (e_contact_get(eContact, E_CONTACT_NAME));
       
   932   if (eContactName){
       
   933     map[QContactName::FieldCustomLabel] = eContactName->additional;
       
   934     map[QContactName::FieldFirstName] = eContactName->given;
       
   935     map[QContactName::FieldLastName] = eContactName->family;
       
   936     //map[QContactName::FieldMiddleName] = eContactName->
       
   937     map[QContactName::FieldPrefix] = eContactName->prefixes;
       
   938     map[QContactName::FieldSuffix] = eContactName->suffixes;
       
   939     e_contact_name_free (eContactName);
       
   940   } else {
       
   941     //Looks that Maemo use just these two fields
       
   942     map[QContactName::FieldFirstName] = CONST_CHAR(e_contact_get_const(eContact, E_CONTACT_GIVEN_NAME));
       
   943     map[QContactName::FieldLastName] = CONST_CHAR(e_contact_get_const(eContact, E_CONTACT_FAMILY_NAME));
       
   944   }
       
   945   setDetailValues(map, rtn);
       
   946   return rtn;
       
   947 }
       
   948 
       
   949 QContactNickname* QContactABook::getNicknameDetail(EContact *eContact) const
       
   950 {
       
   951   QContactNickname* rtn = new QContactNickname;
       
   952   QVariantMap map;
       
   953   map[QContactNickname::FieldNickname] = CONST_CHAR (e_contact_get_const(eContact, E_CONTACT_NICKNAME));
       
   954   setDetailValues(map, rtn);
       
   955   return rtn;
       
   956 }
       
   957 
       
   958 QList<QContactEmailAddress*> QContactABook::getEmailDetail(EContact *eContact) const
       
   959 {
       
   960   QList<QContactEmailAddress*> rtnList;
       
   961   
       
   962   GList* attrList = osso_abook_contact_get_attributes(eContact, EVC_EMAIL); //FIXME MemLeak
       
   963   
       
   964   for (GList *node = g_list_last(attrList); node != NULL; node = g_list_previous(node)) {
       
   965     QContactEmailAddress *email = new QContactEmailAddress;
       
   966     QVariantMap map;
       
   967   
       
   968     EVCardAttribute *attr = static_cast<EVCardAttribute*>(node->data);
       
   969 
       
   970     // Set Address Context using attribute parameter value
       
   971     EVCardAttributeParam *param = NULL;
       
   972     GList* p = e_vcard_attribute_get_params(attr);
       
   973     
       
   974     if (p)
       
   975       param = static_cast<EVCardAttributeParam*>(p->data);
       
   976     
       
   977     if (param){
       
   978       GList *v = e_vcard_attribute_param_get_values(param);
       
   979       QString context = CONST_CHAR(v->data);
       
   980       if (context == "HOME")
       
   981         email->setContexts(QContactDetail::ContextHome);
       
   982       else if (context == "WORK")
       
   983         email->setContexts(QContactDetail::ContextWork);
       
   984     }
       
   985     
       
   986     // Set Address Values
       
   987     GList *v = e_vcard_attribute_get_values(attr);
       
   988     int i = 0;
       
   989     while (v){
       
   990       map[QContactEmailAddress::FieldEmailAddress] = QString::fromLatin1(CONST_CHAR(v->data));
       
   991       i++;
       
   992       v = v->next;
       
   993     }
       
   994     g_list_free(v);
       
   995     
       
   996     map[QContactDetail::FieldDetailUri] = QString::number(g_list_position(attrList, node));
       
   997     setDetailValues(map, email);
       
   998     rtnList << email;
       
   999   }
       
  1000   g_list_free(attrList);
       
  1001   
       
  1002   return rtnList;
       
  1003 }
       
  1004 
       
  1005 QContactAvatar* QContactABook::getAvatarDetail(EContact *eContact) const
       
  1006 {
       
  1007     Q_UNUSED(eContact);
       
  1008 // XXX TODO: FIXME
       
  1009 //  QContactAvatar* rtn = new QContactAvatar;
       
  1010 //  QVariantMap map;
       
  1011 //
       
  1012 //  OssoABookContact *aContact = A_CONTACT(eContact);
       
  1013 //  if (!aContact)
       
  1014 //    return rtn;
       
  1015 //
       
  1016 //  //GdkPixbuf* pixbuf = osso_abook_contact_get_avatar_pixbuf(aContact, NULL, NULL);
       
  1017 //  GdkPixbuf* pixbuf = osso_abook_avatar_get_image_rounded(OSSO_ABOOK_AVATAR(aContact), 64, 64, true, 4, NULL);
       
  1018 //  if (!GDK_IS_PIXBUF(pixbuf)){
       
  1019 //    FREE(pixbuf);
       
  1020 //    return rtn;
       
  1021 //  }
       
  1022 //
       
  1023 //  const uchar* bdata = (const uchar*)gdk_pixbuf_get_pixels(pixbuf);
       
  1024 //  QSize bsize(gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf));
       
  1025 //
       
  1026 //  //Convert GdkPixbuf to QPixmap
       
  1027 //  QImage converted(bdata, bsize.width(), bsize.height(), QImage::Format_ARGB32_Premultiplied);
       
  1028 //  map[QContactAvatar::FieldPixmap] = QPixmap::fromImage(converted);
       
  1029 //  g_object_unref(pixbuf);
       
  1030 //  setDetailValues(map, rtn);
       
  1031 //
       
  1032 //  return rtn;
       
  1033 
       
  1034     QContactAvatar* empty = new QContactAvatar;
       
  1035     return empty;
       
  1036 }
       
  1037 
       
  1038 QContactBirthday* QContactABook::getBirthdayDetail(EContact *eContact) const
       
  1039 {
       
  1040   QContactBirthday* rtn = new QContactBirthday;
       
  1041   QVariantMap map;
       
  1042   EContactDate *date =static_cast<EContactDate*>(e_contact_get(eContact, E_CONTACT_BIRTH_DATE));
       
  1043   if (!date)
       
  1044     return rtn;
       
  1045   QDate birthday(date->year, date->month, date->day);
       
  1046   e_contact_date_free(date);
       
  1047   map[QContactBirthday::FieldBirthday] = birthday;
       
  1048   setDetailValues(map, rtn);
       
  1049   return rtn;
       
  1050 }
       
  1051 
       
  1052 QContactGender* QContactABook::getGenderDetail(EContact *eContact) const
       
  1053 {
       
  1054   QContactGender* rtn = new QContactGender;
       
  1055   QVariantMap map;
       
  1056   const char* g = CONST_CHAR(osso_abook_contact_get_value(eContact, "X-GENDER"));
       
  1057   QString gender = QString::fromLatin1(g);
       
  1058   if (gender == "male")
       
  1059     gender = "Male";
       
  1060   else if (gender == "female")
       
  1061     gender = "Female";
       
  1062   else if (gender == "unspecified")
       
  1063     gender = "Unspecified";
       
  1064   
       
  1065   map[QContactGender::FieldGender] = gender;
       
  1066   FREE(g);
       
  1067   setDetailValues(map, rtn);
       
  1068   return rtn;
       
  1069 }  
       
  1070 
       
  1071 //NOTE Using UID as GUID
       
  1072 QContactGuid* QContactABook::getGuidDetail(EContact *eContact) const
       
  1073 {
       
  1074   QContactGuid* rtn = new QContactGuid;
       
  1075   QVariantMap map;
       
  1076   const char* uid = CONST_CHAR(e_contact_get(eContact, E_CONTACT_UID));
       
  1077   map[QContactGuid::FieldGuid] = uid;
       
  1078   FREE(uid);
       
  1079   setDetailValues(map, rtn);
       
  1080   return rtn;
       
  1081 }
       
  1082 
       
  1083 QContactNote* QContactABook::getNoteDetail(EContact *eContact) const
       
  1084 {
       
  1085   QContactNote* rtn = new QContactNote;
       
  1086   QVariantMap map;
       
  1087   const char* note = CONST_CHAR(e_contact_get(eContact, E_CONTACT_NOTE));
       
  1088   map[QContactNote::FieldNote] = QString::fromLatin1(note);
       
  1089   FREE(note);
       
  1090   setDetailValues(map, rtn);
       
  1091   return rtn;
       
  1092 }
       
  1093 
       
  1094 static const QStringList vcardsManagedByTelepathy(){
       
  1095   QStringList rtn;
       
  1096   OssoABookAccountManager* accountMgr = osso_abook_account_manager_get_default();
       
  1097   const GList *vcardFields = osso_abook_account_manager_get_primary_vcard_fields(accountMgr);
       
  1098   while (vcardFields){
       
  1099     QString field = (const char*)vcardFields->data;
       
  1100     if (!rtn.contains(field))
       
  1101       rtn << field;
       
  1102     vcardFields = vcardFields->next;
       
  1103   }
       
  1104   
       
  1105   return rtn;
       
  1106 }
       
  1107 
       
  1108 QList<QContactOnlineAccount*> QContactABook::getOnlineAccountDetail(EContact *eContact) const
       
  1109 {
       
  1110   QList<QContactOnlineAccount*> rtnList;
       
  1111   
       
  1112   QStringList evcardToSkip = vcardsManagedByTelepathy();
       
  1113   
       
  1114   // Gets info of online accounts from roster contacts associated to the master one  
       
  1115   if (!osso_abook_contact_is_roster_contact (A_CONTACT(eContact))) {
       
  1116     QContactOnlineAccount* rtn = new QContactOnlineAccount;
       
  1117     
       
  1118     GList *contacts = osso_abook_contact_get_roster_contacts(A_CONTACT(eContact));
       
  1119     GList *node;
       
  1120     for (node = contacts; node != NULL; node = g_list_next(node)){
       
  1121       OssoABookContact *rosterContact = A_CONTACT(node->data);
       
  1122      
       
  1123       McProfile* id = osso_abook_contact_get_profile(rosterContact);
       
  1124       McAccount* account = osso_abook_contact_get_account(rosterContact);
       
  1125       
       
  1126       // Avoid to look for Roster contacts into the VCard
       
  1127       QString accountVCard = QString::fromLatin1(mc_profile_get_vcard_field(id));
       
  1128       evcardToSkip.removeOne(accountVCard);
       
  1129       
       
  1130       // Presence
       
  1131       OssoABookPresence *presence = OSSO_ABOOK_PRESENCE (rosterContact);
       
  1132       TpConnectionPresenceType presenceType = osso_abook_presence_get_presence_type (presence);
       
  1133       QString presenceTypeString;
       
  1134       QContactPresence::PresenceState presenceTypeEnum;
       
  1135       switch (presenceType) {
       
  1136         case TP_CONNECTION_PRESENCE_TYPE_UNSET: presenceTypeString = "Unset"; presenceTypeEnum = QContactPresence::PresenceUnknown; break;
       
  1137         case TP_CONNECTION_PRESENCE_TYPE_OFFLINE: presenceTypeString = "Offline"; presenceTypeEnum = QContactPresence::PresenceOffline; break;
       
  1138         case TP_CONNECTION_PRESENCE_TYPE_AVAILABLE: presenceTypeString = "Available"; presenceTypeEnum = QContactPresence::PresenceAvailable; break;
       
  1139         case TP_CONNECTION_PRESENCE_TYPE_AWAY: presenceTypeString = "Away"; presenceTypeEnum = QContactPresence::PresenceAway; break;
       
  1140         case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY: presenceTypeString = "Extended Away"; presenceTypeEnum = QContactPresence::PresenceExtendedAway; break;
       
  1141         case TP_CONNECTION_PRESENCE_TYPE_HIDDEN: presenceTypeString = "Hidden"; presenceTypeEnum = QContactPresence::PresenceHidden; break;
       
  1142         case TP_CONNECTION_PRESENCE_TYPE_BUSY: presenceTypeString = "Busy"; presenceTypeEnum = QContactPresence::PresenceBusy; break;
       
  1143         case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN: presenceTypeString = "Unknown"; presenceTypeEnum = QContactPresence::PresenceUnknown; break;
       
  1144         case TP_CONNECTION_PRESENCE_TYPE_ERROR: presenceTypeString = "Error"; presenceTypeEnum = QContactPresence::PresenceUnknown; break;
       
  1145         default:
       
  1146           qCritical() << "Presence type is not vaild" << presenceType;
       
  1147       }
       
  1148       
       
  1149       QVariantMap map;
       
  1150       map[QContactOnlineAccount::FieldServiceProvider] = mc_profile_get_unique_name(id);
       
  1151       map[QContactOnlineAccount::FieldDetailUri] = mc_profile_get_unique_name(id); // use this as detail URI so we can link to presence.
       
  1152       map["AccountPath"] = account->name; //MCAccount name: variable part of the D-Bus object path.
       
  1153       
       
  1154       setDetailValues(map, rtn);
       
  1155     }
       
  1156     rtnList << rtn;
       
  1157     g_list_free (contacts);
       
  1158   }
       
  1159   
       
  1160   /* Users can add Online account details manually. Eg: IRC username.
       
  1161    * evcardToSkip stringlist contains evCard attributes that have been already processed.
       
  1162    */
       
  1163   GList *attributeList = e_vcard_get_attributes((EVCard*)eContact);
       
  1164   GList *node;
       
  1165 
       
  1166   if (attributeList) {
       
  1167     for (node = attributeList; node != NULL; node = g_list_next (node)) {
       
  1168       EVCardAttribute* attr = (EVCardAttribute*)node->data;
       
  1169       if (!attr)
       
  1170         continue;
       
  1171       QString attributeName = QString::fromLatin1(e_vcard_attribute_get_name(attr));
       
  1172       
       
  1173       // Skip attributes processed scanning roster contacts.
       
  1174       if (!evcardToSkip.contains(attributeName))
       
  1175         continue;
       
  1176       
       
  1177       GList *params = e_vcard_attribute_get_params(attr);
       
  1178       GList *nodeP;
       
  1179       QString type;
       
  1180       // If the parameter list lenght is 1, X-OSSO-VALID is not specified
       
  1181       bool ossoValidIsOk = (g_list_length(params) == 1) ? true : false;
       
  1182 
       
  1183       for (nodeP = params; nodeP != NULL; nodeP = g_list_next (nodeP)) {
       
  1184         EVCardAttributeParam* p = (EVCardAttributeParam*) nodeP->data;
       
  1185         QString paramName = QString::fromLatin1(e_vcard_attribute_param_get_name(p));
       
  1186         bool attrIsType = false;
       
  1187         bool attrIsOssoValid = false;
       
  1188         
       
  1189         //If type is empty check if the attribute is "TYPE"
       
  1190         if (type.isEmpty())
       
  1191           attrIsType = paramName.contains(EVC_TYPE);
       
  1192         
       
  1193         if(!ossoValidIsOk)
       
  1194           attrIsOssoValid = paramName.contains("X-OSSO-VALID");
       
  1195         
       
  1196         if (!attrIsType && !attrIsOssoValid) {
       
  1197           qWarning () << "Skipping attribute parameter checking for" << paramName;
       
  1198           continue;
       
  1199         }
       
  1200         
       
  1201         GList *values = e_vcard_attribute_param_get_values(p);
       
  1202         GList *node;
       
  1203         for (node = values; node != NULL; node = g_list_next (node)) {
       
  1204           QString attributeParameterValue = QString::fromLatin1(CONST_CHAR(node->data));
       
  1205           if (attrIsOssoValid) {
       
  1206             ossoValidIsOk = (attributeParameterValue == "yes")? true : false;
       
  1207             if (!ossoValidIsOk) {
       
  1208               qWarning() << "X-OSSO-VALID is false.";
       
  1209               break;
       
  1210             }
       
  1211           } else if (type.isEmpty()) {
       
  1212             type = attributeParameterValue;
       
  1213             if (type.isEmpty())
       
  1214               qCritical() << "TYPE is empty"; 
       
  1215           }
       
  1216         }
       
  1217         
       
  1218         if (ossoValidIsOk && !type.isEmpty()) {
       
  1219           QContactOnlineAccount* rtn = new QContactOnlineAccount;
       
  1220           QVariantMap map;
       
  1221           map[QContactOnlineAccount::FieldServiceProvider] = type;
       
  1222           setDetailValues(map, rtn);
       
  1223           rtnList << rtn;
       
  1224         }
       
  1225       }
       
  1226     }
       
  1227   }
       
  1228 
       
  1229   return rtnList;
       
  1230 }
       
  1231 
       
  1232 QContactOrganization* QContactABook::getOrganizationDetail(EContact *eContact) const
       
  1233 {
       
  1234   QContactOrganization* rtn = new QContactOrganization;
       
  1235   QVariantMap map;
       
  1236   const char* title = CONST_CHAR(e_contact_get(eContact, E_CONTACT_TITLE));
       
  1237   map[QContactOrganization::FieldTitle] = title;
       
  1238   FREE(title);
       
  1239   setDetailValues(map, rtn);
       
  1240   return rtn;
       
  1241 }
       
  1242 
       
  1243 QList<QContactPhoneNumber*> QContactABook::getPhoneDetail(EContact *eContact) const
       
  1244 {
       
  1245   QList<QContactPhoneNumber*> rtnList;
       
  1246   
       
  1247   GList *l = osso_abook_contact_get_attributes(eContact, EVC_TEL);
       
  1248   
       
  1249   for (GList *node = g_list_last(l); node != NULL; node = g_list_previous(node)) {
       
  1250     QContactPhoneNumber* phoneNumber = new QContactPhoneNumber;
       
  1251     QVariantMap map;
       
  1252     
       
  1253     EVCardAttribute *attr = static_cast<EVCardAttribute*>(node->data);
       
  1254     GList* p = e_vcard_attribute_get_param(attr, EVC_TYPE);
       
  1255     
       
  1256     //Set Contexts and SubTypes
       
  1257     while (p) {
       
  1258       QString value = QString::fromLatin1(CONST_CHAR(p->data));
       
  1259       
       
  1260       if (value == "HOME")
       
  1261         phoneNumber->setContexts(QContactDetail::ContextHome);
       
  1262       else if (value == "WORK")
       
  1263         phoneNumber->setContexts(QContactDetail::ContextWork);
       
  1264       else
       
  1265       if (value == "CELL")
       
  1266         phoneNumber->setSubTypes(QContactPhoneNumber::SubTypeMobile);
       
  1267       else if (value == "VOICE")
       
  1268         phoneNumber->setSubTypes(QContactPhoneNumber::SubTypeVoice);
       
  1269       
       
  1270       p = p->next;
       
  1271     }
       
  1272     g_list_free(p);
       
  1273     
       
  1274     //Set Phone Number
       
  1275     GList* phoneNumbers = e_vcard_attribute_get_values(attr);
       
  1276     const char* normalized = e_normalize_phone_number(CONST_CHAR(phoneNumbers->data)); //FIXME Valgrind complains about this
       
  1277     QString phoneNumberStr(normalized);
       
  1278     FREE(normalized);
       
  1279     map[QContactPhoneNumber::FieldNumber] = phoneNumberStr;
       
  1280     map[QContactDetail::FieldDetailUri] = QString::number(g_list_position(l, node));
       
  1281     setDetailValues(map, phoneNumber);
       
  1282     
       
  1283     rtnList << phoneNumber;
       
  1284   }
       
  1285   g_list_free(l);
       
  1286   
       
  1287   return rtnList;
       
  1288 }
       
  1289 
       
  1290 
       
  1291 QList<QContactPresence*> QContactABook::getPresenceDetail(EContact *eContact) const
       
  1292 {
       
  1293   QList<QContactPresence*> rtnList;
       
  1294 
       
  1295   QStringList evcardToSkip = vcardsManagedByTelepathy();
       
  1296 
       
  1297   // Gets info of online accounts from roster contacts associated to the master one
       
  1298   if (!osso_abook_contact_is_roster_contact (A_CONTACT(eContact))) {
       
  1299     QContactPresence* rtn = new QContactPresence;
       
  1300 
       
  1301     GList *contacts = osso_abook_contact_get_roster_contacts(A_CONTACT(eContact));
       
  1302     GList *node;
       
  1303     for (node = contacts; node != NULL; node = g_list_next(node)){
       
  1304       OssoABookContact *rosterContact = A_CONTACT(node->data);
       
  1305 
       
  1306       McProfile* id = osso_abook_contact_get_profile(rosterContact);
       
  1307       McAccount* account = osso_abook_contact_get_account(rosterContact);
       
  1308 
       
  1309       // Avoid to look for Roster contacts into the VCard
       
  1310       QString accountVCard = QString::fromLatin1(mc_profile_get_vcard_field(id));
       
  1311       evcardToSkip.removeOne(accountVCard);
       
  1312 
       
  1313       // Presence
       
  1314       OssoABookPresence *presence = OSSO_ABOOK_PRESENCE (rosterContact);
       
  1315       TpConnectionPresenceType presenceType = osso_abook_presence_get_presence_type (presence);
       
  1316       QString presenceTypeString;
       
  1317       QContactPresence::PresenceState presenceTypeEnum;
       
  1318       switch (presenceType) {
       
  1319         case TP_CONNECTION_PRESENCE_TYPE_UNSET: presenceTypeString = "Unset"; presenceTypeEnum = QContactPresence::PresenceUnknown; break;
       
  1320         case TP_CONNECTION_PRESENCE_TYPE_OFFLINE: presenceTypeString = "Offline"; presenceTypeEnum = QContactPresence::PresenceOffline; break;
       
  1321         case TP_CONNECTION_PRESENCE_TYPE_AVAILABLE: presenceTypeString = "Available"; presenceTypeEnum = QContactPresence::PresenceAvailable; break;
       
  1322         case TP_CONNECTION_PRESENCE_TYPE_AWAY: presenceTypeString = "Away"; presenceTypeEnum = QContactPresence::PresenceAway; break;
       
  1323         case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY: presenceTypeString = "Extended Away"; presenceTypeEnum = QContactPresence::PresenceExtendedAway; break;
       
  1324         case TP_CONNECTION_PRESENCE_TYPE_HIDDEN: presenceTypeString = "Hidden"; presenceTypeEnum = QContactPresence::PresenceHidden; break;
       
  1325         case TP_CONNECTION_PRESENCE_TYPE_BUSY: presenceTypeString = "Busy"; presenceTypeEnum = QContactPresence::PresenceBusy; break;
       
  1326         case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN: presenceTypeString = "Unknown"; presenceTypeEnum = QContactPresence::PresenceUnknown; break;
       
  1327         case TP_CONNECTION_PRESENCE_TYPE_ERROR: presenceTypeString = "Error"; presenceTypeEnum = QContactPresence::PresenceUnknown; break;
       
  1328         default:
       
  1329           qCritical() << "Presence type is not valid" << presenceType;
       
  1330       }
       
  1331 
       
  1332       QVariantMap map; // XXX FIXME
       
  1333       map[QContactPresence::FieldNickname] = osso_abook_contact_get_display_name(rosterContact);
       
  1334       map[QContactPresence::FieldPresenceState] = presenceTypeEnum;
       
  1335       map[QContactPresence::FieldPresenceStateText] = QString::fromLatin1(osso_abook_presence_get_presence_status_message(presence));
       
  1336       map[QContactPresence::FieldLinkedDetailUris] = mc_profile_get_unique_name(id); //use the unique name as a detail uri of the online account.
       
  1337       map["AccountPath"] = account->name; //MCAccount name: variable part of the D-Bus object path.
       
  1338 
       
  1339       setDetailValues(map, rtn);
       
  1340     }
       
  1341     rtnList << rtn;
       
  1342     g_list_free (contacts);
       
  1343   }
       
  1344 
       
  1345   /* Users can add Online account details manually. Eg: IRC username.
       
  1346    * evcardToSkip stringlist contains evCard attributes that have been already processed.
       
  1347    */
       
  1348   GList *attributeList = e_vcard_get_attributes((EVCard*)eContact);
       
  1349   GList *node;
       
  1350 
       
  1351   if (attributeList) {
       
  1352     for (node = attributeList; node != NULL; node = g_list_next (node)) {
       
  1353       EVCardAttribute* attr = (EVCardAttribute*)node->data;
       
  1354       if (!attr)
       
  1355         continue;
       
  1356       QString attributeName = QString::fromLatin1(e_vcard_attribute_get_name(attr));
       
  1357 
       
  1358       // Skip attributes processed scanning roster contacts.
       
  1359       if (!evcardToSkip.contains(attributeName))
       
  1360         continue;
       
  1361 
       
  1362       GList *params = e_vcard_attribute_get_params(attr);
       
  1363       GList *nodeP;
       
  1364       QString type;
       
  1365       // If the parameter list lenght is 1, X-OSSO-VALID is not specified
       
  1366       bool ossoValidIsOk = (g_list_length(params) == 1) ? true : false;
       
  1367 
       
  1368       for (nodeP = params; nodeP != NULL; nodeP = g_list_next (nodeP)) {
       
  1369         EVCardAttributeParam* p = (EVCardAttributeParam*) nodeP->data;
       
  1370         QString paramName = QString::fromLatin1(e_vcard_attribute_param_get_name(p));
       
  1371         bool attrIsType = false;
       
  1372         bool attrIsOssoValid = false;
       
  1373 
       
  1374         //If type is empty check if the attribute is "TYPE"
       
  1375         if (type.isEmpty())
       
  1376           attrIsType = paramName.contains(EVC_TYPE);
       
  1377 
       
  1378         if(!ossoValidIsOk)
       
  1379           attrIsOssoValid = paramName.contains("X-OSSO-VALID");
       
  1380 
       
  1381         if (!attrIsType && !attrIsOssoValid) {
       
  1382           qWarning () << "Skipping attribute parameter checking for" << paramName;
       
  1383           continue;
       
  1384         }
       
  1385 
       
  1386         GList *values = e_vcard_attribute_param_get_values(p);
       
  1387         GList *node;
       
  1388         for (node = values; node != NULL; node = g_list_next (node)) {
       
  1389           QString attributeParameterValue = QString::fromLatin1(CONST_CHAR(node->data));
       
  1390           if (attrIsOssoValid) {
       
  1391             ossoValidIsOk = (attributeParameterValue == "yes")? true : false;
       
  1392             if (!ossoValidIsOk) {
       
  1393               qWarning() << "X-OSSO-VALID is false.";
       
  1394               break;
       
  1395             }
       
  1396           } else if (type.isEmpty()) {
       
  1397             type = attributeParameterValue;
       
  1398             if (type.isEmpty())
       
  1399               qCritical() << "TYPE is empty";
       
  1400           }
       
  1401         }
       
  1402 
       
  1403         if (ossoValidIsOk && !type.isEmpty()) {
       
  1404           QContactPresence* rtn = new QContactPresence;
       
  1405           QVariantMap map;
       
  1406           map[QContactPresence::FieldNickname] = QString::fromLatin1(e_vcard_attribute_get_value(attr));
       
  1407           map[QContactPresence::FieldLinkedDetailUris] = type; // XXX FIXME
       
  1408           setDetailValues(map, rtn);
       
  1409           rtnList << rtn;
       
  1410         }
       
  1411       }
       
  1412     }
       
  1413   }
       
  1414 
       
  1415   return rtnList;
       
  1416 }
       
  1417 
       
  1418 QContactTimestamp* QContactABook::getTimestampDetail(EContact *eContact) const
       
  1419 {
       
  1420    QContactTimestamp* rtn = new QContactTimestamp;
       
  1421    QVariantMap map;
       
  1422    const char* rev = CONST_CHAR(e_contact_get(eContact, E_CONTACT_REV));
       
  1423    map[QContactTimestamp::FieldModificationTimestamp] = QDateTime::fromString(rev, Qt::ISODate);
       
  1424    FREE(rev);
       
  1425    setDetailValues(map, rtn);
       
  1426    return rtn;
       
  1427 }
       
  1428 
       
  1429 QContactThumbnail* QContactABook::getThumbnailDetail(EContact *eContact) const
       
  1430 {
       
  1431   QContactThumbnail* rtn = new QContactThumbnail;
       
  1432   QVariantMap map;
       
  1433 
       
  1434   OssoABookContact *aContact = A_CONTACT(eContact);
       
  1435   if (!aContact)
       
  1436     return rtn;
       
  1437 
       
  1438   //GdkPixbuf* pixbuf = osso_abook_contact_get_avatar_pixbuf(aContact, NULL, NULL);
       
  1439   GdkPixbuf* pixbuf = osso_abook_avatar_get_image_rounded(OSSO_ABOOK_AVATAR(aContact), 64, 64, true, 4, NULL);
       
  1440   if (!GDK_IS_PIXBUF(pixbuf)){
       
  1441     FREE(pixbuf);
       
  1442     return rtn;
       
  1443   }
       
  1444 
       
  1445   const uchar* bdata = (const uchar*)gdk_pixbuf_get_pixels(pixbuf);
       
  1446   QSize bsize(gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf));
       
  1447 
       
  1448   //Convert GdkPixbuf to QPixmap
       
  1449   QImage converted(bdata, bsize.width(), bsize.height(), QImage::Format_ARGB32_Premultiplied);
       
  1450   map[QContactThumbnail::FieldThumbnail] = converted;
       
  1451   g_object_unref(pixbuf);
       
  1452   setDetailValues(map, rtn);
       
  1453 
       
  1454   return rtn;
       
  1455 }
       
  1456 
       
  1457 QContactUrl* QContactABook::getUrlDetail(EContact *eContact) const
       
  1458 {
       
  1459    QContactUrl* rtn = new QContactUrl;
       
  1460    QVariantMap map;
       
  1461    const char* url = CONST_CHAR(e_contact_get(eContact, E_CONTACT_HOMEPAGE_URL));
       
  1462    map[QContactUrl::FieldUrl] = url;
       
  1463    FREE(url);
       
  1464    setDetailValues(map, rtn);
       
  1465    return rtn;
       
  1466 }
       
  1467 
       
  1468 static void addAttributeToAContact(const OssoABookContact* contact,
       
  1469                                    const QString& attrName, const QStringList& attrValues,
       
  1470                                    const QString& paramName = QString(), const QStringList& paramValues = QStringList(),
       
  1471                                    bool overwrite = true,
       
  1472                                    const int index = 0)
       
  1473 {
       
  1474   if (!contact)
       
  1475     return;
       
  1476   
       
  1477   EVCard *vcard = E_VCARD (contact);
       
  1478   EVCardAttribute *attr = NULL;
       
  1479   EVCardAttributeParam* param = NULL;
       
  1480   
       
  1481   QCM5_DEBUG << "Adding attribute" << attrName << "AttrValues:" << attrValues
       
  1482              << "ParamName:" << paramName << "ParamValues:" << paramValues
       
  1483              << "overwrite" << overwrite << "Index" << index;
       
  1484   
       
  1485   if (overwrite)
       
  1486   {
       
  1487     GList *attributeList = osso_abook_contact_get_attributes(E_CONTACT(contact), qPrintable(attrName));
       
  1488     
       
  1489     for (GList *node = g_list_last(attributeList); node != NULL; node = g_list_previous(node)) {
       
  1490       EVCardAttribute* eAttr = (EVCardAttribute*)node->data;
       
  1491       int pos =  g_list_position(attributeList, node);
       
  1492       
       
  1493       if (index > pos){
       
  1494         qWarning() << "Attribute doesn't found at position" << index;
       
  1495         return;
       
  1496       }
       
  1497       
       
  1498       if (index != pos)
       
  1499         continue;
       
  1500       
       
  1501       // Select the current EVCard Attribute if it contains the same parameters of
       
  1502       // attribute we want to modify/add
       
  1503       int matchedParams = 0;
       
  1504       GList* p = NULL;
       
  1505       p = e_vcard_attribute_get_param(eAttr, qPrintable(paramName));
       
  1506 
       
  1507       while (p){
       
  1508           foreach(QString paramV, paramValues){
       
  1509           QString value = CONST_CHAR(p->data);
       
  1510           if (paramV == value)
       
  1511             ++matchedParams;
       
  1512         }
       
  1513         p = p->next;
       
  1514       }
       
  1515       g_list_free(p);
       
  1516 
       
  1517       if (matchedParams == paramValues.count()) {
       
  1518         attr = eAttr;
       
  1519         break;
       
  1520       }    
       
  1521     }
       
  1522   }
       
  1523   
       
  1524   // Check if attrValues contains something
       
  1525   bool noValues = true;
       
  1526   foreach(QString s, attrValues){
       
  1527     if (!s.isEmpty()){
       
  1528       noValues = false;
       
  1529       break;
       
  1530     }
       
  1531   }
       
  1532   
       
  1533   if (attr) {
       
  1534     if (noValues){
       
  1535       e_vcard_remove_attribute(vcard, attr);
       
  1536       return;
       
  1537     } else {
       
  1538       e_vcard_attribute_remove_values(attr);
       
  1539     }
       
  1540   } else {
       
  1541     if (noValues)
       
  1542       return;
       
  1543     
       
  1544     // Create Attribute with right parameters
       
  1545     attr = e_vcard_attribute_new(NULL, qPrintable(attrName));
       
  1546     if (!paramName.isEmpty()){
       
  1547       param = e_vcard_attribute_param_new(qPrintable(paramName));
       
  1548       
       
  1549       foreach(QString paramV, paramValues)
       
  1550         e_vcard_attribute_param_add_value(param, qPrintable(paramV));
       
  1551 
       
  1552       e_vcard_attribute_add_param(attr, param);
       
  1553     }
       
  1554     // Save the attribute to the VCard
       
  1555     e_vcard_add_attribute(vcard, attr);
       
  1556   }
       
  1557   
       
  1558   // Add values to the attribute
       
  1559   foreach(QString attrV, attrValues) {
       
  1560     e_vcard_attribute_add_value(attr, qPrintable(attrV));
       
  1561   }
       
  1562   
       
  1563   // Debugging
       
  1564   {
       
  1565     const char* dbgStr = e_vcard_to_string(vcard, EVC_FORMAT_VCARD_30);
       
  1566     QCM5_DEBUG << "Modified VCard" << dbgStr;
       
  1567     FREE(dbgStr);
       
  1568   }
       
  1569 }
       
  1570 
       
  1571 OssoABookContact* QContactABook::convert(const QContact *contact) const
       
  1572 {
       
  1573   Q_CHECK_PTR(contact);
       
  1574   
       
  1575   OssoABookContact* rtn;
       
  1576   
       
  1577   // Get aContact if it exists or create a new one if it doesn't
       
  1578   QContactLocalId id = contact->localId();
       
  1579   QCM5_DEBUG << "Converting QContact id:" << id << " to aContact";
       
  1580   if (id){
       
  1581     rtn = getAContact(id);
       
  1582     // It's not safe to commit changes to a contact that has been modified.
       
  1583     // This problem affects attributes with the same name and parameters such as
       
  1584     // EMail, Address...
       
  1585     QContactTimestamp* ts = getTimestampDetail(E_CONTACT(rtn));
       
  1586     if (*ts != contact->detail<QContactTimestamp>()){
       
  1587       delete ts;
       
  1588       return NULL;
       
  1589     }
       
  1590     delete ts;
       
  1591   } else {
       
  1592     rtn = osso_abook_contact_new();
       
  1593   }
       
  1594   
       
  1595   QList<QContactDetail> allDetails = contact->details();
       
  1596   foreach(const QContactDetail &detail, allDetails){
       
  1597     QString definitionName = detail.definitionName();
       
  1598     
       
  1599     QCM5_DEBUG << "Saving" << definitionName;
       
  1600     
       
  1601     //QContactDisplayLabel::DefinitionName
       
  1602     if (definitionName == QContactAddress::DefinitionName){
       
  1603       setAddressDetail(rtn, detail);
       
  1604     } else
       
  1605     if (definitionName == QContactAvatar::DefinitionName){
       
  1606       setAvatarDetail(rtn, detail);
       
  1607     } else
       
  1608     if (definitionName == QContactBirthday::DefinitionName){
       
  1609       setBirthdayDetail(rtn, detail);
       
  1610     } else
       
  1611     if (definitionName == QContactEmailAddress::DefinitionName){
       
  1612       setEmailDetail(rtn, detail);
       
  1613     } else
       
  1614     if (definitionName == QContactGender::DefinitionName){
       
  1615       setGenderDetail(rtn, detail);
       
  1616     } else
       
  1617     if (definitionName == QContactName::DefinitionName){
       
  1618       setNameDetail(rtn, detail);
       
  1619     } else
       
  1620     if (definitionName == QContactNickname::DefinitionName){
       
  1621       setNicknameDetail(rtn, detail);
       
  1622     } else
       
  1623     if (definitionName == QContactNote::DefinitionName){
       
  1624       setNoteDetail(rtn, detail);
       
  1625     } else
       
  1626     if (definitionName == QContactOnlineAccount::DefinitionName){
       
  1627       setOnlineAccountDetail(rtn, detail);
       
  1628     } else
       
  1629     if (definitionName == QContactOrganization::DefinitionName){
       
  1630       setOrganizationDetail(rtn, detail);
       
  1631     } else
       
  1632     if (definitionName == QContactPhoneNumber::DefinitionName){
       
  1633       setPhoneDetail(rtn, detail);
       
  1634     } else
       
  1635     if (definitionName == QContactThumbnail::DefinitionName){
       
  1636       setThumbnailDetail(rtn, detail);
       
  1637     } else
       
  1638     if (definitionName == QContactUrl::DefinitionName){
       
  1639       setUrlDetail(rtn, detail);
       
  1640     }
       
  1641   }  
       
  1642   
       
  1643   return rtn;
       
  1644 }
       
  1645 
       
  1646 void QContactABook::setAddressDetail(const OssoABookContact* aContact, const QContactAddress& detail) const
       
  1647 {
       
  1648   if (!aContact) return;
       
  1649   
       
  1650   uint detailUri;
       
  1651   const uint nAddressElems = 7;
       
  1652   QStringList adrAttrValues, 
       
  1653               lblAttrValues,
       
  1654               paramValues;
       
  1655   
       
  1656   // Get parameters
       
  1657   foreach(QString c, detail.contexts())
       
  1658     paramValues << c.toUpper();
       
  1659   
       
  1660   // Initialize adrAttrValues;
       
  1661   for (uint i = 0; i < nAddressElems; ++i)
       
  1662     adrAttrValues << "";
       
  1663 
       
  1664   // Fill adrAttrValues
       
  1665   QVariantMap vm = detail.variantValues();
       
  1666   QMapIterator<QString, QVariant> i(vm);
       
  1667   
       
  1668   while (i.hasNext()) {
       
  1669     i.next();
       
  1670     int index = -1;
       
  1671     QString key = i.key();
       
  1672       
       
  1673     if (key == QContactAddress::FieldPostOfficeBox) index = 0;
       
  1674     else if (key == "Estension") index = 1;
       
  1675     else if (key == QContactAddress::FieldStreet) index = 2;
       
  1676     else if (key == QContactAddress::FieldLocality) index = 3;
       
  1677     else if (key == QContactAddress::FieldRegion) index = 4;
       
  1678     else if (key == QContactAddress::FieldPostcode) index = 5;
       
  1679     else if (key == QContactAddress::FieldCountry) index = 6;  
       
  1680     else if (key == QContactDetail::FieldContext) continue;
       
  1681     else if (key == QContactDetail::FieldDetailUri) detailUri = i.value().toInt();
       
  1682     else {
       
  1683       qWarning() << "Address contains an invalid field:" << key;
       
  1684       return;
       
  1685     }
       
  1686     
       
  1687     if (index != -1)
       
  1688       adrAttrValues[index] = i.value().toString();
       
  1689   }
       
  1690 
       
  1691   // Fill lblAttrValues
       
  1692   QStringList labelValues;
       
  1693   labelValues << adrAttrValues[1] 
       
  1694               << adrAttrValues[2]
       
  1695               << adrAttrValues[0]
       
  1696               << adrAttrValues[3]
       
  1697               << adrAttrValues[4]
       
  1698               << adrAttrValues[5]
       
  1699               << adrAttrValues[6];
       
  1700   lblAttrValues << labelValues.join(", ");
       
  1701   
       
  1702   // Skip if adrAttrValues contains only empty strings
       
  1703   bool noValues = true;
       
  1704   foreach(QString s, adrAttrValues){
       
  1705     if (!s.isEmpty()){
       
  1706       noValues = false;
       
  1707       break;
       
  1708     }
       
  1709   } 
       
  1710   if (noValues)
       
  1711     return;
       
  1712   
       
  1713   // Saving LABEL and ADR attributes into the VCard
       
  1714   addAttributeToAContact(aContact, EVC_ADR, adrAttrValues, EVC_TYPE, paramValues, true, detailUri);
       
  1715   
       
  1716   //BUG Label attribute contains a bug
       
  1717   //It contains TYPE(TYPE) if ADDRESS doesn't contain any parameter value.
       
  1718   if (paramValues.isEmpty())
       
  1719     paramValues << EVC_TYPE;
       
  1720   
       
  1721   addAttributeToAContact(aContact, EVC_LABEL, lblAttrValues, EVC_TYPE, paramValues, true, detailUri);
       
  1722 }
       
  1723 
       
  1724 void QContactABook::setThumbnailDetail(const OssoABookContact* aContact, const QContactThumbnail& detail) const
       
  1725 {
       
  1726     if (!aContact) return;
       
  1727 
       
  1728     EBook *book;
       
  1729     {
       
  1730       OssoABookRoster* roster = A_ROSTER(m_abookAgregator);
       
  1731       book = osso_abook_roster_get_book(roster);
       
  1732     }
       
  1733 
       
  1734     QImage image = detail.thumbnail();
       
  1735     
       
  1736     if (image.isNull())
       
  1737       return;
       
  1738     
       
  1739     if (image.format() != QImage::Format_ARGB32_Premultiplied)
       
  1740         image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
       
  1741     GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data(image.bits(), GDK_COLORSPACE_RGB,
       
  1742                                                  image.hasAlphaChannel(), 8,
       
  1743                                                  image.width(), image.height(),
       
  1744                                                  image.bytesPerLine(), 0, 0);
       
  1745     osso_abook_contact_set_pixbuf((OssoABookContact*)aContact, pixbuf, book, 0);
       
  1746     g_object_unref(pixbuf);
       
  1747 }
       
  1748 
       
  1749 void QContactABook::setAvatarDetail(const OssoABookContact* aContact, const QContactAvatar& detail) const
       
  1750 {
       
  1751   Q_UNUSED(aContact)
       
  1752   Q_UNUSED(detail);
       
  1753 // XXX TODO: FIXME
       
  1754 //  if (!aContact) return;
       
  1755 //
       
  1756 //  EBook *book;
       
  1757 //  {
       
  1758 //    OssoABookRoster* roster = A_ROSTER(m_abookAgregator);
       
  1759 //    book = osso_abook_roster_get_book(roster);
       
  1760 //  }
       
  1761 //
       
  1762 //  QImage image = detail.pixmap().toImage();
       
  1763 //  if (image.format() != QImage::Format_ARGB32_Premultiplied)
       
  1764 //      image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
       
  1765 //  GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data(image.bits(), GDK_COLORSPACE_RGB,
       
  1766 //                                               image.hasAlphaChannel(), 8,
       
  1767 //                                               image.width(), image.height(),
       
  1768 //                                               image.bytesPerLine(), 0, 0);
       
  1769 //  osso_abook_contact_set_pixbuf((OssoABookContact*)aContact, pixbuf, book, 0);
       
  1770 //  g_object_unref(pixbuf);
       
  1771 }
       
  1772 
       
  1773 void QContactABook::setBirthdayDetail(const OssoABookContact* aContact, const QContactBirthday& detail) const
       
  1774 {
       
  1775   if (!aContact) return;
       
  1776   
       
  1777   QStringList attrValues;
       
  1778   attrValues << detail.value(QContactBirthday::FieldBirthday);
       
  1779   
       
  1780   addAttributeToAContact(aContact, EVC_BDAY, attrValues);
       
  1781 }
       
  1782 
       
  1783 void QContactABook::setEmailDetail(const OssoABookContact* aContact, const QContactEmailAddress& detail) const
       
  1784 {
       
  1785   if (!aContact) return;
       
  1786   QStringList attrValues,
       
  1787               paramValues;
       
  1788 
       
  1789   QVariantMap vm = detail.variantValues();
       
  1790   QMapIterator<QString, QVariant> i(vm);
       
  1791   while (i.hasNext()) {
       
  1792     i.next();
       
  1793     QString key = i.key();
       
  1794     
       
  1795     // We don't want to save the Detail URI
       
  1796     if (key == QContactDetail::FieldDetailUri)
       
  1797       continue;
       
  1798     
       
  1799     if (key == QContactDetail::FieldContext)
       
  1800       paramValues << i.value().toString().toUpper();
       
  1801     else
       
  1802       attrValues << i.value().toString();
       
  1803   }
       
  1804   
       
  1805   addAttributeToAContact(aContact, EVC_EMAIL, attrValues, EVC_TYPE, paramValues, true, detail.detailUri().toInt());
       
  1806 }
       
  1807 
       
  1808 void QContactABook::setGenderDetail(const OssoABookContact* aContact, const QContactGender& detail) const
       
  1809 {
       
  1810   if (!aContact) return;
       
  1811   
       
  1812   QStringList attrValues;
       
  1813   attrValues << detail.value(QContactGender::FieldGender).toLower();
       
  1814   
       
  1815   addAttributeToAContact(aContact, "X-GENDER", attrValues);
       
  1816 }
       
  1817 
       
  1818 void QContactABook::setNameDetail(const OssoABookContact* aContact, const QContactName& detail) const
       
  1819 {
       
  1820   if (!aContact) return;
       
  1821   
       
  1822   QStringList attrValues;
       
  1823   // Save First and Last name in the N vcard attribute
       
  1824   {  
       
  1825     QStringList supportedDetailValues;
       
  1826     supportedDetailValues << QContactName::FieldFirstName << QContactName::FieldLastName;
       
  1827   
       
  1828     foreach(QString key, supportedDetailValues){
       
  1829       attrValues << detail.value(key);
       
  1830     }
       
  1831   
       
  1832     //REMOVE ME - We don't want to support custom label
       
  1833     if (attrValues[0].isEmpty()){
       
  1834       qWarning() << "QContactName::FieldFirstName is empty";
       
  1835       attrValues[0] = detail.customLabel();
       
  1836     }
       
  1837   
       
  1838     addAttributeToAContact(aContact, EVC_N, attrValues);
       
  1839   }
       
  1840   
       
  1841   // Save Fist + Last name in the FN card attribute
       
  1842   {
       
  1843     attrValues << attrValues.join(" ");
       
  1844     addAttributeToAContact(aContact, EVC_FN, attrValues);
       
  1845   }
       
  1846 }
       
  1847 
       
  1848 void QContactABook::setNicknameDetail(const OssoABookContact* aContact, const QContactNickname& detail) const
       
  1849 {
       
  1850   if (!aContact) return;
       
  1851   
       
  1852   QStringList attrValues;
       
  1853   attrValues << detail.value(QContactNickname::FieldNickname);
       
  1854   
       
  1855   addAttributeToAContact(aContact, EVC_NICKNAME, attrValues);
       
  1856 }
       
  1857 
       
  1858 void QContactABook::setNoteDetail(const OssoABookContact* aContact, const QContactNote& detail) const
       
  1859 {
       
  1860   if (!aContact) return;
       
  1861   
       
  1862   QStringList attrValues;
       
  1863   attrValues << detail.value(QContactNote::FieldNote);
       
  1864   
       
  1865   addAttributeToAContact(aContact, EVC_NOTE, attrValues);
       
  1866 }
       
  1867 
       
  1868 /*NOTE: Online details comes from Telepathy or can be added manually by the user.
       
  1869  *      OnlineDetals coming from Telepathy/Roster contacts can't be saved.
       
  1870  */
       
  1871 void QContactABook::setOnlineAccountDetail(const OssoABookContact* aContact, const QContactOnlineAccount& detail) const
       
  1872 {
       
  1873    if (!aContact)
       
  1874      return;
       
  1875    
       
  1876    Q_UNUSED(detail);
       
  1877 }
       
  1878 
       
  1879 void QContactABook::setOrganizationDetail(const OssoABookContact* aContact, const QContactOrganization& detail) const
       
  1880 {
       
  1881   if (!aContact) return;
       
  1882   
       
  1883   QStringList attrValues;
       
  1884   attrValues << detail.value(QContactOrganization::FieldTitle);
       
  1885   
       
  1886   addAttributeToAContact(aContact, EVC_ORG, attrValues);
       
  1887 }
       
  1888 
       
  1889 void QContactABook::setPhoneDetail(const OssoABookContact* aContact, const QContactPhoneNumber& detail) const
       
  1890 {
       
  1891   if (!aContact) return;
       
  1892   QStringList attrValues,
       
  1893               paramValues;
       
  1894 
       
  1895   QVariantMap vm = detail.variantValues();
       
  1896   QMapIterator<QString, QVariant> i(vm);
       
  1897   while (i.hasNext()) {
       
  1898     i.next();
       
  1899     const QString key = i.key();
       
  1900     
       
  1901     // We don't want to save the Detail URI
       
  1902     if (key == QContactDetail::FieldDetailUri)
       
  1903       continue;
       
  1904     
       
  1905     if (key == QContactDetail::FieldContext ||
       
  1906         key == QContactPhoneNumber::FieldSubTypes){
       
  1907       QString value = i.value().toString();
       
  1908       if (value == QContactPhoneNumber::SubTypeMobile)
       
  1909         value = "CELL";
       
  1910       paramValues << value.toUpper();
       
  1911     } else
       
  1912       attrValues << i.value().toString();
       
  1913   }
       
  1914   
       
  1915   // Avoid unsupported type
       
  1916   if (paramValues.isEmpty())
       
  1917     paramValues << "VOICE";
       
  1918   
       
  1919   addAttributeToAContact(aContact, EVC_TEL, attrValues, EVC_TYPE, paramValues, true, detail.detailUri().toInt());
       
  1920 }
       
  1921 
       
  1922 void QContactABook::setUrlDetail(const OssoABookContact* aContact, const QContactUrl& detail) const
       
  1923 {
       
  1924   if (!aContact) return;
       
  1925   
       
  1926   QStringList attrValues;
       
  1927   attrValues << detail.value(QContactUrl::FieldUrl);
       
  1928   
       
  1929   addAttributeToAContact(aContact, EVC_URL, attrValues);
       
  1930 }