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