/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qcontactmaemo5backend_p.h"
#include "qcontactabook_p.h"
#include <QEventLoop>
#include <libebook/e-book-util.h>
/* Error handling Macros */
#define FATAL_IF_ERROR(x) if(x) { \
QString message(x->message); \
g_error_free(x); \
qFatal(qPrintable(message)); \
}
#define WARNING_IF_ERROR(x) if(x) { \
QString message(x->message); \
g_error_free(x); \
qWarning(qPrintable(message)); \
}
/* Casting Macros */
#define A_CONTACT(x) reinterpret_cast<OssoABookContact*>(x)
#define A_ROSTER(x) reinterpret_cast<OssoABookRoster*>(x)
#define CONST_CHAR(x) static_cast<const char*>(x)
#define FREE(x) free((void*)x)
struct cbSharedData{
QContactIDsHash* hash;
QContactABook *that;
};
struct jobSharedData{
QContactABook* that;
bool *result;
char *uid;
};
/* QContactABook */
QContactABook::QContactABook(QObject* parent) :QObject(parent), m_cbSD(0), m_deleteJobSD(0), m_saveJobSD(0)
{
//Initialize QContactDetail context list
initAddressBook();
}
QContactABook::~QContactABook()
{
OssoABookAggregator *roster = reinterpret_cast<OssoABookAggregator*>(m_abookAgregator);
if (g_signal_handler_is_connected(roster, m_contactAddedHandlerId))
g_signal_handler_disconnect(roster, m_contactAddedHandlerId);
if (g_signal_handler_is_connected(roster, m_contactChangedHandlerId))
g_signal_handler_disconnect(roster, m_contactChangedHandlerId);
if (g_signal_handler_is_connected(roster, m_contactRemovedHandlerId))
g_signal_handler_disconnect(roster, m_contactRemovedHandlerId);
// XXX FIXME: memory leak?
//g_object_unref(m_abookAgregator);
delete m_cbSD;
m_cbSD = 0;
delete m_deleteJobSD;
m_deleteJobSD = 0;
delete m_saveJobSD;
m_saveJobSD = 0;
}
static void contactsAddedCB(OssoABookRoster *roster, OssoABookContact **contacts, gpointer data)
{
QCM5_DEBUG << "CONTACT ADDED";
Q_UNUSED(roster)
cbSharedData* d = static_cast<cbSharedData*>(data);
if (!d){
qWarning() << "d has been deleted";
return;
}
OssoABookContact **p;
QList<QContactLocalId> contactIds;
for (p = contacts; *p; ++p) {
if (osso_abook_contact_is_roster_contact(*p))
continue;
// Add a new localID to the local ID hash
const char* uid = CONST_CHAR(e_contact_get_const(E_CONTACT(*p), E_CONTACT_UID));
QContactLocalId id = d->hash->append(uid);
if (id)
contactIds << id;
}
d->that->_contactsAdded(contactIds);
}
static void contactsChangedCB(OssoABookRoster *roster, OssoABookContact **contacts, gpointer data)
{
QCM5_DEBUG << "CONTACT CHANGED";
Q_UNUSED(roster)
cbSharedData* d = static_cast<cbSharedData*>(data);
if (!d){
qWarning() << "d has been deleted";
return;
}
OssoABookContact **p;
QList<QContactLocalId> contactIds;
for (p = contacts; *p; ++p) {
if (osso_abook_contact_is_roster_contact(*p))
continue;
const char* uid = CONST_CHAR(e_contact_get_const(E_CONTACT(*p), E_CONTACT_UID));
QContactLocalId id = d->hash->find(uid);
//FREE(uid);
if (id)
contactIds << id;
}
d->that->_contactsChanged(contactIds);
}
static void contactsRemovedCB(OssoABookRoster *roster, const char **ids, gpointer data)
{
QCM5_DEBUG << "CONTACT REMOVED";
Q_UNUSED(roster)
cbSharedData* d = static_cast<cbSharedData*>(data);
if (!d){
qWarning() << "d has been deleted";
return;
}
const char **p;
QList<QContactLocalId> contactIds;
for (p = ids; *p; ++p) {
QContactLocalId id = d->hash->take(*p);
QCM5_DEBUG << "Contact" << id << "has been removed";
if (id)
contactIds << id;
}
d->that->_contactsRemoved(contactIds);
}
void QContactABook::initAddressBook(){
/* Open AddressBook */
GError *gError = NULL;
OssoABookRoster* roster = NULL;
roster = osso_abook_aggregator_get_default(&gError);
FATAL_IF_ERROR(gError)
osso_abook_waitable_run((OssoABookWaitable *) roster, g_main_context_default(), &gError);
FATAL_IF_ERROR(gError)
if (!osso_abook_waitable_is_ready ((OssoABookWaitable *) roster, &gError))
FATAL_IF_ERROR(gError)
m_abookAgregator = reinterpret_cast<OssoABookAggregator*>(roster);
/* Initialize local Id Hash */
initLocalIdHash();
/* Initialize callbacks shared data */
m_cbSD = new cbSharedData;
m_cbSD->hash = &m_localIds;
m_cbSD->that = this;
/* Setup signals */
m_contactAddedHandlerId = g_signal_connect(roster, "contacts-added",
G_CALLBACK (contactsAddedCB), m_cbSD);
m_contactChangedHandlerId = g_signal_connect(roster, "contacts-changed",
G_CALLBACK (contactsChangedCB), m_cbSD);
m_contactRemovedHandlerId = g_signal_connect(roster, "contacts-removed",
G_CALLBACK (contactsRemovedCB), m_cbSD);
#if 0
//TEST List supported fields
EBook *book = NULL;
GList *l;
book = osso_abook_roster_get_book(roster);
e_book_get_supported_fields (book, &l, NULL);
while (l) {
qDebug() << "SUPPORTED FIELD" << (const char*)l->data;
l = l->next;
}
g_list_free(l);
#endif
}
/*! Fill LocalId Hash associating an internal QContactLocalId to any
* master contact ID.
* NOTE: master contact IDs are string like "1" or "osso-abook-tmc1".
*/
void QContactABook::initLocalIdHash()
{
GList *contactList = NULL;
GList *node;
contactList = osso_abook_aggregator_list_master_contacts(m_abookAgregator);
if (!contactList) {
qWarning() << "There are no Master contacts. LocalId hash is empty.";
return;
}
for (node = contactList; node != NULL; node = g_list_next (node)) {
EContact *contact = E_CONTACT(node->data);
const char* data = CONST_CHAR(e_contact_get_const(contact, E_CONTACT_UID));
QByteArray eContactUID(data);
//FREE(data);
m_localIds << eContactUID; //FIXME MemLeak
QCM5_DEBUG << "eContactID " << eContactUID << "has been stored in m_localIDs with key" << m_localIds[eContactUID];
// Useful for debugging.
if (QCM5_DEBUG_ENABLED) e_vcard_dump_structure((EVCard*)contact);
}
g_list_free(contactList);
}
QList<QContactLocalId> QContactABook::contactIds(const QContactFilter& filter, const QList<QContactSortOrder>& sortOrders, QContactManager::Error* error) const
{
QList<QContactLocalId> rtn;
/* Sorting */
//NOTE Native sorting is possible thanks to g_list_sort.
// It's limited just to one filter.
// Multi filters support need non native sorting.
// Native filtering needs a lot of coding since we need
// detailDefinitionName * detailFieldName functions
// to compare couple of contacts
if (sortOrders.count()){
QCM5_DEBUG << "Sorting...";
// We don't need
// Fill Ids
QList<QContactLocalId> Ids;
{
QList<QContactSortOrder> so;
QContactManager::Error e;
Ids = contactIds(filter, so, &e);
}
// Fill Contact List
QList<QContact> contacts;
foreach(QContactLocalId id, Ids){
QContact *c;
QContactManager::Error e;
c = getQContact(id, &e);
if (e == QContactManager::NoError)
contacts << *c;
else
*error = e;
}
// Non native sorting
return QContactManagerEngine::sortContacts(contacts, sortOrders);
}
/* Matching action filter */
//NOTE The code was not really tested */
if(filter.type() == QContactFilter::ActionFilter){
QContactActionFilter af(filter);
/* This looks a bit strange for me */
QList<QContactActionDescriptor> descriptors = QContactAction::actionDescriptors(af.actionName(), af.vendorName(), af.implementationVersion());
GList *masterContacts = osso_abook_aggregator_list_master_contacts(m_abookAgregator);
for(; masterContacts; ){
OssoABookContact *masterContact = A_CONTACT(masterContacts->data);
bool match = contactActionsMatch(masterContact, descriptors);
if(!match) {
GList *rosterContacts = osso_abook_contact_get_roster_contacts(masterContact);
for(; rosterContacts && !match; ){
OssoABookContact *rosterContact = A_CONTACT(rosterContacts->data);
match = contactActionsMatch(rosterContact, descriptors);
rosterContacts = g_list_delete_link(rosterContacts, rosterContacts);
}
g_list_free(rosterContacts);
}
if(match){
EContact *contact = E_CONTACT(masterContact);
const char* data = CONST_CHAR(e_contact_get_const(contact, E_CONTACT_UID));
QByteArray localId(data);
m_localIds << localId;
rtn.append(m_localIds[localId]);
QCM5_DEBUG << "eContactID " << localId << "has been stored in m_localIDs with key" << m_localIds[localId];
}
masterContacts = g_list_delete_link(masterContacts, masterContacts);
}
*error = QContactManager::NoError;
return rtn;
}
EBookQuery* query = convert(filter);
GList* l = osso_abook_aggregator_find_contacts(m_abookAgregator, query);
if (query)
e_book_query_unref(query);
while (l){
EContact *contact = E_CONTACT(l->data);
const char* data = CONST_CHAR(e_contact_get_const(contact, E_CONTACT_UID));
QByteArray localId(data);
m_localIds << localId;
rtn.append(m_localIds[localId]);
QCM5_DEBUG << "eContactID " << localId << "has been stored in m_localIDs with key" << m_localIds[localId];
l = g_list_delete_link(l, l);
}
*error = QContactManager::NoError;
return rtn;
}
QContact* QContactABook::getQContact(const QContactLocalId& contactId, QContactManager::Error* error) const
{
QContact *rtn;
OssoABookContact* aContact = getAContact(contactId);
if (!aContact) {
qWarning() << "Unable to get a valid AContact";
*error = QContactManager::DoesNotExistError;
return new QContact;
}
//Convert aContact => qContact
rtn = convert(E_CONTACT(aContact));
return rtn;
}
static void delContactCB(EBook *book, EBookStatus status, gpointer closure)
{
Q_UNUSED(book);
QCM5_DEBUG << "Contact Removed";
jobSharedData *sd = static_cast<jobSharedData*>(closure);
if (!sd)
return;
*sd->result = (status != E_BOOK_ERROR_OK &&
status != E_BOOK_ERROR_CONTACT_NOT_FOUND) ? false : true;
sd->that->_jobRemovingCompleted();
}
//### FIXME error is not managed
bool QContactABook::removeContact(const QContactLocalId& contactId, QContactManager::Error* error)
{
Q_UNUSED(error);
QMutexLocker locker(&m_delContactMutex);
bool ok = false;
OssoABookRoster *roster = A_ROSTER(m_abookAgregator);
EBook *book = osso_abook_roster_get_book(roster);
OssoABookContact *aContact = getAContact(contactId);
if (!OSSO_ABOOK_IS_CONTACT(aContact)){
qWarning() << "aCtontact is not a valid ABook contact";
return false;
}
// ASync => Sync
QEventLoop loop;
connect(this, SIGNAL(jobRemovingCompleted()), &loop, SLOT(quit()));
// Prepare shared data
if (m_deleteJobSD){
delete m_deleteJobSD;
m_deleteJobSD = 0;
}
m_deleteJobSD = new jobSharedData;
m_deleteJobSD->that = this;
m_deleteJobSD->result = &ok;
//Remove photos
EContactPhoto *photo = NULL;
GFile *file = NULL;
photo = (EContactPhoto*) e_contact_get(E_CONTACT (aContact), E_CONTACT_PHOTO);
if (photo) {
if (photo->type == E_CONTACT_PHOTO_TYPE_URI && photo->data.uri) {
file = g_file_new_for_uri(photo->data.uri);
g_file_delete(file, NULL, NULL);
g_object_unref (file);
}
e_contact_photo_free (photo);
}
//Remove all roster contacts from their roster
GList* rosterContacts = NULL;
rosterContacts = osso_abook_contact_get_roster_contacts(aContact);
const char *masterUid = CONST_CHAR(e_contact_get_const(E_CONTACT(aContact), E_CONTACT_UID));
while(rosterContacts){
OssoABookContact *rosterContact = A_CONTACT(rosterContacts->data);
osso_abook_contact_reject_for_uid(rosterContact, masterUid, NULL);
rosterContacts = rosterContacts->next;
}
// Remove contact
e_book_async_remove_contact(book, E_CONTACT(aContact),
delContactCB, m_deleteJobSD);
loop.exec(QEventLoop::AllEvents|QEventLoop::WaitForMoreEvents);
return ok;
}
static void commitContactCB(EBook* book, EBookStatus status, gpointer user_data)
{
Q_UNUSED(book)
jobSharedData *sd = static_cast<jobSharedData*>(user_data);
*sd->result = (status == E_BOOK_ERROR_OK) ? true : false;
sd->that->_jobSavingCompleted();
}
static void addContactCB(EBook* book, EBookStatus status, const char *uid, gpointer user_data)
{
jobSharedData *sd = static_cast<jobSharedData*>(user_data);
if (uid)
sd->uid = strdup(uid);
//osso_abook_contact_set_roster(OssoABookContact *contact, OssoABookRoster *roster)
commitContactCB(book, status, user_data);
}
bool QContactABook::saveContact(QContact* contact, QContactManager::Error* error)
{
QMutexLocker locker(&m_saveContactMutex);
if (!contact) {
*error = QContactManager::BadArgumentError;
return false;
}
bool ok = false;
OssoABookContact *aContact = NULL;
const char *uid;
EBook *book;
{
OssoABookRoster* roster = reinterpret_cast<OssoABookRoster*>(m_abookAgregator);
book = osso_abook_roster_get_book(roster);
}
// Conver QContact to AContact
aContact = convert(contact);
if (!aContact){
*error = QContactManager::UnspecifiedError;
return false;
}
// ASync => Sync
QEventLoop loop;
connect(this, SIGNAL(jobSavingCompleted()), &loop, SLOT(quit()));
// Prepare shared data
if (m_saveJobSD){
delete m_saveJobSD;
m_saveJobSD = 0;
}
m_saveJobSD = new jobSharedData;
m_saveJobSD->that = this;
m_saveJobSD->result = &ok;
// Add/Commit the contact
uid = CONST_CHAR(e_contact_get_const(E_CONTACT (aContact), E_CONTACT_UID));
if (uid) {
osso_abook_contact_async_commit(aContact, book, commitContactCB, m_saveJobSD);
} else {
osso_abook_contact_async_add(aContact, book, addContactCB, m_saveJobSD);
}
loop.exec(QEventLoop::AllEvents|QEventLoop::WaitForMoreEvents);
// set the id of the contact.
QContactId cId;
cId.setLocalId(m_localIds[m_saveJobSD->uid]);
contact->setId(cId);
//free(m_saveJobSD->uid);
return ok;
}
QContactLocalId QContactABook::selfContactId(QContactManager::Error* errors) const
{
QContactLocalId id;
EContact *self = E_CONTACT(osso_abook_self_contact_get_default());
if (self) {
*errors = QContactManager::NoError;
const char* data = CONST_CHAR(e_contact_get_const(self, E_CONTACT_UID));
const QByteArray eContactUID(data);
QContactLocalId localId = m_localIds[eContactUID];
if (localId)
id = localId;
else {
m_localIds << eContactUID; //FIXME MemLeak
id = m_localIds[eContactUID];
QCM5_DEBUG << "eContactID " << eContactUID << "has been stored in m_localIDs with key" << id;
}
} else {
qWarning() << "Cannot find self contact";
*errors = QContactManager::DoesNotExistError;
id = 0;
}
g_object_unref(self);
return id;
}
bool QContactABook::contactActionsMatch(OssoABookContact *contact, QList<QContactActionDescriptor> descriptors) const
{
OssoABookCapsFlags capsFlags = osso_abook_caps_get_capabilities(OSSO_ABOOK_CAPS(contact));
if(capsFlags & OSSO_ABOOK_CAPS_NONE)
return false;
/* ActionNames could be incorrect */
OssoABookCapsFlags actionFlags = OSSO_ABOOK_CAPS_NONE;
for(int i = 0; i < descriptors.size(); i++){
QString actionName = descriptors.at(i).actionName();
QCM5_DEBUG << actionName;
if(!actionName.compare("Phone"))
actionFlags = (OssoABookCapsFlags)(actionFlags | OSSO_ABOOK_CAPS_PHONE);
else if(!actionName.compare("Voice"))
actionFlags = (OssoABookCapsFlags)(actionFlags | OSSO_ABOOK_CAPS_VOICE);
else if(!actionName.compare("SendEmail"))
actionFlags = (OssoABookCapsFlags)(actionFlags | OSSO_ABOOK_CAPS_EMAIL);
else if(!actionName.compare("Chat"))
actionFlags = (OssoABookCapsFlags)(actionFlags | OSSO_ABOOK_CAPS_CHAT);
else if(!actionName.compare("ChatAdditional"))
actionFlags = (OssoABookCapsFlags)(actionFlags | OSSO_ABOOK_CAPS_CHAT_ADDITIONAL);
else if(!actionName.compare("VoiceAdditional"))
actionFlags = (OssoABookCapsFlags)(actionFlags | OSSO_ABOOK_CAPS_VOICE_ADDITIONAL);
else if(!actionName.compare("Video"))
actionFlags = (OssoABookCapsFlags)(actionFlags | OSSO_ABOOK_CAPS_VIDEO);
else if(!actionName.compare("Addressbook"))
actionFlags = (OssoABookCapsFlags)(actionFlags | OSSO_ABOOK_CAPS_ADDRESSBOOK);
}
return ((actionFlags & capsFlags) == actionFlags);
}
EBookQuery* QContactABook::convert(const QContactFilter& filter) const
{
EBookQuery* query = NULL;
switch(filter.type()){
case QContactFilter::DefaultFilter:
{
QCM5_DEBUG << "QContactFilter::DefaultFilter";
query = e_book_query_any_field_contains(""); //Match all contacts
} break;
case QContactFilter::LocalIdFilter:
{
QCM5_DEBUG << "LocalIdFilter";
const QContactLocalIdFilter f(filter);
QList<QContactLocalId> ids = f.ids();
if (ids.isEmpty())
return NULL;
query= NULL;
foreach(const QContactLocalId id, ids){
EBookQuery* q = NULL;
// Looking for the eContact local id inside the localId hash
const char* eContactId = m_localIds[id];
if (!eContactId[0])
return NULL;
q = e_book_query_field_test(E_CONTACT_UID, E_BOOK_QUERY_IS, eContactId);
if (!q)
continue;
if (query)
query = e_book_query_orv(query, q, NULL);
else
query = q;
}
} break;
case QContactFilter::ContactDetailFilter:
{
QCM5_DEBUG << "ContactDetailFilter";
const QContactDetailFilter f(filter);
QString queryStr;
if (!f.value().isValid())
return NULL;
switch (f.matchFlags()){
case QContactFilter::MatchContains: queryStr = "contains"; break;
case QContactFilter::MatchFixedString:
case QContactFilter::MatchCaseSensitive:
case QContactFilter::MatchExactly: queryStr = "is"; break;
case QContactFilter::MatchStartsWith: queryStr = "beginswith"; break;
case QContactFilter::MatchEndsWith: queryStr = "endswith"; break;
default:
queryStr = "contains";
qWarning() << "Match flag not supported";
}
static QHash<QString,QString> hash;
if (hash.isEmpty()){
hash[QContactAddress::DefinitionName] = "address";
hash[QContactBirthday::DefinitionName] = "birth-date";
hash[QContactDisplayLabel::DefinitionName] = "full-name"; //hack
hash[QContactEmailAddress::DefinitionName] = "email";
hash[QContactName::DefinitionName] = "full-name";
hash[QContactNickname::DefinitionName] = "nickname";
hash[QContactNote::DefinitionName] = "note";
hash[QContactOrganization::DefinitionName] = "title";
hash[QContactPhoneNumber::DefinitionName] = "phone";
hash[QContactUrl::DefinitionName] = "homepage-url";
}
QString eDetail = hash[f.detailDefinitionName()];
if (eDetail.isEmpty()){
qWarning() << "Unable to found an ebook detail for " << f.detailDefinitionName();
return NULL;
}
queryStr = queryStr + " \"" + eDetail + "\" \"" + f.value().toString() + "\"";
query = e_book_query_from_string(qPrintable(queryStr));
} break;
case QContactFilter::ActionFilter:
QCM5_DEBUG << "ActionFilter"; //eQuery doesn't support ActionFilter
break;
case QContactFilter::IntersectionFilter:
{
QCM5_DEBUG << "IntersectionFilter";
const QContactIntersectionFilter f(filter);
const QList<QContactFilter> fs= f.filters();
QContactFilter i;
foreach(i, fs){
EBookQuery* q = convert(i);
if (!q){
qWarning() << "Query is null";
continue;
}
if (query)
query = e_book_query_andv(query, q, NULL);
else
query = q;
}
} break;
case QContactFilter::UnionFilter:
{
QCM5_DEBUG << "UnionFilter";
const QContactUnionFilter f(filter);
const QList<QContactFilter> fs= f.filters();
QContactFilter i;
foreach(i, fs){
EBookQuery* q = convert(i);
if (!q){
qWarning() << "Query is null";
continue;
}
if (query)
query = e_book_query_orv(query, q, NULL);
else
query = q;
}
} break;
case QContactFilter::InvalidFilter:
{
QCM5_DEBUG << "InvalidFilter";
query = e_book_query_from_string("(is \"id\" \"-1\")");
} break;
default:
QCM5_DEBUG << "Filter not supported";
}
//Debugging
const char *queryString = e_book_query_to_string(query);
QCM5_DEBUG << "QUERY" << queryString;
FREE(queryString);
return query;
}
QContact* QContactABook::convert(EContact *eContact) const
{
QContact *contact = new QContact();
QList<QContactDetail*> detailList;
/* Id */
contact->setId(getContactId(eContact));
/* Address */
QList<QContactAddress*> addressList = getAddressDetail(eContact);
QContactAddress* address;
foreach(address, addressList)
detailList << address;
/* Avatar */
detailList << getAvatarDetail(eContact); // XXX TODO: FIXME
detailList << getThumbnailDetail(eContact);
/* BirthDay */
detailList << getBirthdayDetail(eContact);
/* Email */
QList<QContactEmailAddress*> emailList = getEmailDetail(eContact);
QContactEmailAddress* email;
foreach(email, emailList)
detailList << email;
/* Gender */
detailList << getGenderDetail(eContact);
/* Global UID*/
detailList << getGuidDetail(eContact);
/* Name & NickName*/
detailList << getNameDetail(eContact);
detailList << getNicknameDetail(eContact);
/* Note */
detailList << getNoteDetail(eContact);
/* Online Account */
QList<QContactOnlineAccount*> onlineAccountList = getOnlineAccountDetail(eContact);
QContactOnlineAccount* onlineAccount;
foreach(onlineAccount, onlineAccountList)
detailList << onlineAccount;
/* Organization */
detailList << getOrganizationDetail(eContact);
/* Phone*/
QList<QContactPhoneNumber*> phoneNumberList = getPhoneDetail(eContact);
QContactPhoneNumber* phoneNumber;
foreach(phoneNumber, phoneNumberList)
detailList << phoneNumber;
/* TimeStamp */
detailList << getTimestampDetail(eContact);
/* Url */
detailList << getUrlDetail(eContact);
bool ok;
QContactDetail* detail;
foreach(detail, detailList){
if (detail->isEmpty()){
delete detail;
continue;
}
ok = contact->saveDetail(detail);
if (!ok){
qWarning() << "Detail can't be saved into QContact";
delete detail;
continue;
}
delete detail;
}
return contact;
}
bool QContactABook::setDetailValues(const QVariantMap& data, QContactDetail* detail) const
{
QMapIterator<QString, QVariant> i(data);
QVariant value;
while (i.hasNext()) {
i.next();
value = i.value();
if (value.isNull())
continue;
if (value.canConvert<QString>() && value.toString().isEmpty())
continue;
QCM5_DEBUG << "Set" << i.key() << i.value();
detail->setValue(i.key(), i.value());
}
if (detail->isEmpty())
return false;
return true;
}
OssoABookContact* QContactABook::getAContact(const QContactLocalId& contactId) const
{
OssoABookContact* rtn = NULL;
QCM5_DEBUG << "Getting aContact with id " << m_localIds[contactId] << "local contactId is" << contactId;
if(QString(m_localIds[contactId]).compare("osso-abook-self") == 0) {
rtn = A_CONTACT(osso_abook_self_contact_get_default());
} else {
EBookQuery* query;
GList* contacts;
query = e_book_query_field_test(E_CONTACT_UID, E_BOOK_QUERY_IS, m_localIds[contactId]);
contacts = osso_abook_aggregator_find_contacts(m_abookAgregator, query);
if (query)
e_book_query_unref(query);
if (g_list_length(contacts) == 1) {
rtn = A_CONTACT(contacts->data);
} else {
qWarning("List is empty or several contacts have the same UID or contactId belongs to a roster contact.");
}
if (contacts)
g_list_free(contacts);
}
return rtn;
}
QContactId QContactABook::getContactId(EContact *eContact) const
{
QContactId rtn;
/* Set LocalId */
{
const char* data = CONST_CHAR(e_contact_get_const(eContact, E_CONTACT_UID));
const QByteArray eContactUID(data);
QContactLocalId localId = m_localIds[eContactUID];
if (!localId)
qWarning("Unable to get valid localId for the specified eContaact UID");
rtn.setLocalId(localId);
}
return rtn;
}
QList<QContactAddress*> QContactABook::getAddressDetail(EContact *eContact) const
{
QList<QContactAddress*> rtnList;
//Ordered list of Fields
QStringList addressFields;
addressFields << QContactAddress::FieldPostOfficeBox
<< "Estension" //FIXME I'm not sure we have to use a new field
<< QContactAddress::FieldStreet
<< QContactAddress::FieldLocality
<< QContactAddress::FieldRegion
<< QContactAddress::FieldPostcode
<< QContactAddress::FieldCountry;
GList* attrList = osso_abook_contact_get_attributes(eContact, EVC_ADR);
for (GList *node = g_list_last(attrList); node != NULL; node = g_list_previous(node)) {
QContactAddress *address = new QContactAddress;
QVariantMap map;
EVCardAttribute *attr = static_cast<EVCardAttribute*>(node->data);
// Set Address Context using attribute parameter value
EVCardAttributeParam *param = NULL;
GList* p = e_vcard_attribute_get_params(attr);
if (p)
param = static_cast<EVCardAttributeParam*>(p->data);
if (param){
GList *v = e_vcard_attribute_param_get_values(param);
QString context = CONST_CHAR(v->data);
if (context == "HOME")
address->setContexts(QContactDetail::ContextHome);
else if (context == "WORK")
address->setContexts(QContactDetail::ContextWork);
}
// Set Address Values
GList *v = NULL;
v =e_vcard_attribute_get_values(attr);
if (!v)
qFatal("ADR attribute data is corrupted");
if (g_list_length(v) != 7){
g_list_free(v);
qCritical() << "ADR attribute data is corrupted";
}
int i = 0;
while (v){
map[addressFields[i]] = QString::fromLatin1(CONST_CHAR(v->data));
i++;
v = v->next;
}
g_list_free(v);
map[QContactDetail::FieldDetailUri] = QString::number(g_list_position(attrList, node));
setDetailValues(map, address);
rtnList << address;
}
g_list_free(attrList);
return rtnList;
}
QContactName* QContactABook::getNameDetail(EContact *eContact) const
{
QContactName* rtn = new QContactName;
QVariantMap map;
//Try to get the structure (looks that this is not supported in Maemo5)
EContactName* eContactName = static_cast<EContactName*> (e_contact_get(eContact, E_CONTACT_NAME));
if (eContactName){
map[QContactName::FieldCustomLabel] = eContactName->additional;
map[QContactName::FieldFirstName] = eContactName->given;
map[QContactName::FieldLastName] = eContactName->family;
//map[QContactName::FieldMiddleName] = eContactName->
map[QContactName::FieldPrefix] = eContactName->prefixes;
map[QContactName::FieldSuffix] = eContactName->suffixes;
e_contact_name_free (eContactName);
} else {
//Looks that Maemo use just these two fields
map[QContactName::FieldFirstName] = CONST_CHAR(e_contact_get_const(eContact, E_CONTACT_GIVEN_NAME));
map[QContactName::FieldLastName] = CONST_CHAR(e_contact_get_const(eContact, E_CONTACT_FAMILY_NAME));
}
setDetailValues(map, rtn);
return rtn;
}
QContactNickname* QContactABook::getNicknameDetail(EContact *eContact) const
{
QContactNickname* rtn = new QContactNickname;
QVariantMap map;
map[QContactNickname::FieldNickname] = CONST_CHAR (e_contact_get_const(eContact, E_CONTACT_NICKNAME));
setDetailValues(map, rtn);
return rtn;
}
QList<QContactEmailAddress*> QContactABook::getEmailDetail(EContact *eContact) const
{
QList<QContactEmailAddress*> rtnList;
GList* attrList = osso_abook_contact_get_attributes(eContact, EVC_EMAIL); //FIXME MemLeak
for (GList *node = g_list_last(attrList); node != NULL; node = g_list_previous(node)) {
QContactEmailAddress *email = new QContactEmailAddress;
QVariantMap map;
EVCardAttribute *attr = static_cast<EVCardAttribute*>(node->data);
// Set Address Context using attribute parameter value
EVCardAttributeParam *param = NULL;
GList* p = e_vcard_attribute_get_params(attr);
if (p)
param = static_cast<EVCardAttributeParam*>(p->data);
if (param){
GList *v = e_vcard_attribute_param_get_values(param);
QString context = CONST_CHAR(v->data);
if (context == "HOME")
email->setContexts(QContactDetail::ContextHome);
else if (context == "WORK")
email->setContexts(QContactDetail::ContextWork);
}
// Set Address Values
GList *v = e_vcard_attribute_get_values(attr);
int i = 0;
while (v){
map[QContactEmailAddress::FieldEmailAddress] = QString::fromLatin1(CONST_CHAR(v->data));
i++;
v = v->next;
}
g_list_free(v);
map[QContactDetail::FieldDetailUri] = QString::number(g_list_position(attrList, node));
setDetailValues(map, email);
rtnList << email;
}
g_list_free(attrList);
return rtnList;
}
QContactAvatar* QContactABook::getAvatarDetail(EContact *eContact) const
{
Q_UNUSED(eContact);
// XXX TODO: FIXME
// QContactAvatar* rtn = new QContactAvatar;
// QVariantMap map;
//
// OssoABookContact *aContact = A_CONTACT(eContact);
// if (!aContact)
// return rtn;
//
// //GdkPixbuf* pixbuf = osso_abook_contact_get_avatar_pixbuf(aContact, NULL, NULL);
// GdkPixbuf* pixbuf = osso_abook_avatar_get_image_rounded(OSSO_ABOOK_AVATAR(aContact), 64, 64, true, 4, NULL);
// if (!GDK_IS_PIXBUF(pixbuf)){
// FREE(pixbuf);
// return rtn;
// }
//
// const uchar* bdata = (const uchar*)gdk_pixbuf_get_pixels(pixbuf);
// QSize bsize(gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf));
//
// //Convert GdkPixbuf to QPixmap
// QImage converted(bdata, bsize.width(), bsize.height(), QImage::Format_ARGB32_Premultiplied);
// map[QContactAvatar::FieldPixmap] = QPixmap::fromImage(converted);
// g_object_unref(pixbuf);
// setDetailValues(map, rtn);
//
// return rtn;
QContactAvatar* empty = new QContactAvatar;
return empty;
}
QContactBirthday* QContactABook::getBirthdayDetail(EContact *eContact) const
{
QContactBirthday* rtn = new QContactBirthday;
QVariantMap map;
EContactDate *date =static_cast<EContactDate*>(e_contact_get(eContact, E_CONTACT_BIRTH_DATE));
if (!date)
return rtn;
QDate birthday(date->year, date->month, date->day);
e_contact_date_free(date);
map[QContactBirthday::FieldBirthday] = birthday;
setDetailValues(map, rtn);
return rtn;
}
QContactGender* QContactABook::getGenderDetail(EContact *eContact) const
{
QContactGender* rtn = new QContactGender;
QVariantMap map;
const char* g = CONST_CHAR(osso_abook_contact_get_value(eContact, "X-GENDER"));
QString gender = QString::fromLatin1(g);
if (gender == "male")
gender = "Male";
else if (gender == "female")
gender = "Female";
else if (gender == "unspecified")
gender = "Unspecified";
map[QContactGender::FieldGender] = gender;
FREE(g);
setDetailValues(map, rtn);
return rtn;
}
//NOTE Using UID as GUID
QContactGuid* QContactABook::getGuidDetail(EContact *eContact) const
{
QContactGuid* rtn = new QContactGuid;
QVariantMap map;
const char* uid = CONST_CHAR(e_contact_get(eContact, E_CONTACT_UID));
map[QContactGuid::FieldGuid] = uid;
FREE(uid);
setDetailValues(map, rtn);
return rtn;
}
QContactNote* QContactABook::getNoteDetail(EContact *eContact) const
{
QContactNote* rtn = new QContactNote;
QVariantMap map;
const char* note = CONST_CHAR(e_contact_get(eContact, E_CONTACT_NOTE));
map[QContactNote::FieldNote] = QString::fromLatin1(note);
FREE(note);
setDetailValues(map, rtn);
return rtn;
}
static const QStringList vcardsManagedByTelepathy(){
QStringList rtn;
OssoABookAccountManager* accountMgr = osso_abook_account_manager_get_default();
const GList *vcardFields = osso_abook_account_manager_get_primary_vcard_fields(accountMgr);
while (vcardFields){
QString field = (const char*)vcardFields->data;
if (!rtn.contains(field))
rtn << field;
vcardFields = vcardFields->next;
}
return rtn;
}
QList<QContactOnlineAccount*> QContactABook::getOnlineAccountDetail(EContact *eContact) const
{
QList<QContactOnlineAccount*> rtnList;
QStringList evcardToSkip = vcardsManagedByTelepathy();
// Gets info of online accounts from roster contacts associated to the master one
if (!osso_abook_contact_is_roster_contact (A_CONTACT(eContact))) {
QContactOnlineAccount* rtn = new QContactOnlineAccount;
GList *contacts = osso_abook_contact_get_roster_contacts(A_CONTACT(eContact));
GList *node;
for (node = contacts; node != NULL; node = g_list_next(node)){
OssoABookContact *rosterContact = A_CONTACT(node->data);
McProfile* id = osso_abook_contact_get_profile(rosterContact);
McAccount* account = osso_abook_contact_get_account(rosterContact);
// Avoid to look for Roster contacts into the VCard
QString accountVCard = QString::fromLatin1(mc_profile_get_vcard_field(id));
evcardToSkip.removeOne(accountVCard);
// Presence
OssoABookPresence *presence = OSSO_ABOOK_PRESENCE (rosterContact);
TpConnectionPresenceType presenceType = osso_abook_presence_get_presence_type (presence);
QString presenceTypeString;
QContactPresence::PresenceState presenceTypeEnum;
switch (presenceType) {
case TP_CONNECTION_PRESENCE_TYPE_UNSET: presenceTypeString = "Unset"; presenceTypeEnum = QContactPresence::PresenceUnknown; break;
case TP_CONNECTION_PRESENCE_TYPE_OFFLINE: presenceTypeString = "Offline"; presenceTypeEnum = QContactPresence::PresenceOffline; break;
case TP_CONNECTION_PRESENCE_TYPE_AVAILABLE: presenceTypeString = "Available"; presenceTypeEnum = QContactPresence::PresenceAvailable; break;
case TP_CONNECTION_PRESENCE_TYPE_AWAY: presenceTypeString = "Away"; presenceTypeEnum = QContactPresence::PresenceAway; break;
case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY: presenceTypeString = "Extended Away"; presenceTypeEnum = QContactPresence::PresenceExtendedAway; break;
case TP_CONNECTION_PRESENCE_TYPE_HIDDEN: presenceTypeString = "Hidden"; presenceTypeEnum = QContactPresence::PresenceHidden; break;
case TP_CONNECTION_PRESENCE_TYPE_BUSY: presenceTypeString = "Busy"; presenceTypeEnum = QContactPresence::PresenceBusy; break;
case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN: presenceTypeString = "Unknown"; presenceTypeEnum = QContactPresence::PresenceUnknown; break;
case TP_CONNECTION_PRESENCE_TYPE_ERROR: presenceTypeString = "Error"; presenceTypeEnum = QContactPresence::PresenceUnknown; break;
default:
qCritical() << "Presence type is not vaild" << presenceType;
}
QVariantMap map;
map[QContactOnlineAccount::FieldServiceProvider] = mc_profile_get_unique_name(id);
map[QContactOnlineAccount::FieldDetailUri] = mc_profile_get_unique_name(id); // use this as detail URI so we can link to presence.
map["AccountPath"] = account->name; //MCAccount name: variable part of the D-Bus object path.
setDetailValues(map, rtn);
}
rtnList << rtn;
g_list_free (contacts);
}
/* Users can add Online account details manually. Eg: IRC username.
* evcardToSkip stringlist contains evCard attributes that have been already processed.
*/
GList *attributeList = e_vcard_get_attributes((EVCard*)eContact);
GList *node;
if (attributeList) {
for (node = attributeList; node != NULL; node = g_list_next (node)) {
EVCardAttribute* attr = (EVCardAttribute*)node->data;
if (!attr)
continue;
QString attributeName = QString::fromLatin1(e_vcard_attribute_get_name(attr));
// Skip attributes processed scanning roster contacts.
if (!evcardToSkip.contains(attributeName))
continue;
GList *params = e_vcard_attribute_get_params(attr);
GList *nodeP;
QString type;
// If the parameter list lenght is 1, X-OSSO-VALID is not specified
bool ossoValidIsOk = (g_list_length(params) == 1) ? true : false;
for (nodeP = params; nodeP != NULL; nodeP = g_list_next (nodeP)) {
EVCardAttributeParam* p = (EVCardAttributeParam*) nodeP->data;
QString paramName = QString::fromLatin1(e_vcard_attribute_param_get_name(p));
bool attrIsType = false;
bool attrIsOssoValid = false;
//If type is empty check if the attribute is "TYPE"
if (type.isEmpty())
attrIsType = paramName.contains(EVC_TYPE);
if(!ossoValidIsOk)
attrIsOssoValid = paramName.contains("X-OSSO-VALID");
if (!attrIsType && !attrIsOssoValid) {
qWarning () << "Skipping attribute parameter checking for" << paramName;
continue;
}
GList *values = e_vcard_attribute_param_get_values(p);
GList *node;
for (node = values; node != NULL; node = g_list_next (node)) {
QString attributeParameterValue = QString::fromLatin1(CONST_CHAR(node->data));
if (attrIsOssoValid) {
ossoValidIsOk = (attributeParameterValue == "yes")? true : false;
if (!ossoValidIsOk) {
qWarning() << "X-OSSO-VALID is false.";
break;
}
} else if (type.isEmpty()) {
type = attributeParameterValue;
if (type.isEmpty())
qCritical() << "TYPE is empty";
}
}
if (ossoValidIsOk && !type.isEmpty()) {
QContactOnlineAccount* rtn = new QContactOnlineAccount;
QVariantMap map;
map[QContactOnlineAccount::FieldServiceProvider] = type;
setDetailValues(map, rtn);
rtnList << rtn;
}
}
}
}
return rtnList;
}
QContactOrganization* QContactABook::getOrganizationDetail(EContact *eContact) const
{
QContactOrganization* rtn = new QContactOrganization;
QVariantMap map;
const char* title = CONST_CHAR(e_contact_get(eContact, E_CONTACT_TITLE));
map[QContactOrganization::FieldTitle] = title;
FREE(title);
setDetailValues(map, rtn);
return rtn;
}
QList<QContactPhoneNumber*> QContactABook::getPhoneDetail(EContact *eContact) const
{
QList<QContactPhoneNumber*> rtnList;
GList *l = osso_abook_contact_get_attributes(eContact, EVC_TEL);
for (GList *node = g_list_last(l); node != NULL; node = g_list_previous(node)) {
QContactPhoneNumber* phoneNumber = new QContactPhoneNumber;
QVariantMap map;
EVCardAttribute *attr = static_cast<EVCardAttribute*>(node->data);
GList* p = e_vcard_attribute_get_param(attr, EVC_TYPE);
//Set Contexts and SubTypes
while (p) {
QString value = QString::fromLatin1(CONST_CHAR(p->data));
if (value == "HOME")
phoneNumber->setContexts(QContactDetail::ContextHome);
else if (value == "WORK")
phoneNumber->setContexts(QContactDetail::ContextWork);
else
if (value == "CELL")
phoneNumber->setSubTypes(QContactPhoneNumber::SubTypeMobile);
else if (value == "VOICE")
phoneNumber->setSubTypes(QContactPhoneNumber::SubTypeVoice);
p = p->next;
}
g_list_free(p);
//Set Phone Number
GList* phoneNumbers = e_vcard_attribute_get_values(attr);
const char* normalized = e_normalize_phone_number(CONST_CHAR(phoneNumbers->data)); //FIXME Valgrind complains about this
QString phoneNumberStr(normalized);
FREE(normalized);
map[QContactPhoneNumber::FieldNumber] = phoneNumberStr;
map[QContactDetail::FieldDetailUri] = QString::number(g_list_position(l, node));
setDetailValues(map, phoneNumber);
rtnList << phoneNumber;
}
g_list_free(l);
return rtnList;
}
QList<QContactPresence*> QContactABook::getPresenceDetail(EContact *eContact) const
{
QList<QContactPresence*> rtnList;
QStringList evcardToSkip = vcardsManagedByTelepathy();
// Gets info of online accounts from roster contacts associated to the master one
if (!osso_abook_contact_is_roster_contact (A_CONTACT(eContact))) {
QContactPresence* rtn = new QContactPresence;
GList *contacts = osso_abook_contact_get_roster_contacts(A_CONTACT(eContact));
GList *node;
for (node = contacts; node != NULL; node = g_list_next(node)){
OssoABookContact *rosterContact = A_CONTACT(node->data);
McProfile* id = osso_abook_contact_get_profile(rosterContact);
McAccount* account = osso_abook_contact_get_account(rosterContact);
// Avoid to look for Roster contacts into the VCard
QString accountVCard = QString::fromLatin1(mc_profile_get_vcard_field(id));
evcardToSkip.removeOne(accountVCard);
// Presence
OssoABookPresence *presence = OSSO_ABOOK_PRESENCE (rosterContact);
TpConnectionPresenceType presenceType = osso_abook_presence_get_presence_type (presence);
QString presenceTypeString;
QContactPresence::PresenceState presenceTypeEnum;
switch (presenceType) {
case TP_CONNECTION_PRESENCE_TYPE_UNSET: presenceTypeString = "Unset"; presenceTypeEnum = QContactPresence::PresenceUnknown; break;
case TP_CONNECTION_PRESENCE_TYPE_OFFLINE: presenceTypeString = "Offline"; presenceTypeEnum = QContactPresence::PresenceOffline; break;
case TP_CONNECTION_PRESENCE_TYPE_AVAILABLE: presenceTypeString = "Available"; presenceTypeEnum = QContactPresence::PresenceAvailable; break;
case TP_CONNECTION_PRESENCE_TYPE_AWAY: presenceTypeString = "Away"; presenceTypeEnum = QContactPresence::PresenceAway; break;
case TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY: presenceTypeString = "Extended Away"; presenceTypeEnum = QContactPresence::PresenceExtendedAway; break;
case TP_CONNECTION_PRESENCE_TYPE_HIDDEN: presenceTypeString = "Hidden"; presenceTypeEnum = QContactPresence::PresenceHidden; break;
case TP_CONNECTION_PRESENCE_TYPE_BUSY: presenceTypeString = "Busy"; presenceTypeEnum = QContactPresence::PresenceBusy; break;
case TP_CONNECTION_PRESENCE_TYPE_UNKNOWN: presenceTypeString = "Unknown"; presenceTypeEnum = QContactPresence::PresenceUnknown; break;
case TP_CONNECTION_PRESENCE_TYPE_ERROR: presenceTypeString = "Error"; presenceTypeEnum = QContactPresence::PresenceUnknown; break;
default:
qCritical() << "Presence type is not valid" << presenceType;
}
QVariantMap map; // XXX FIXME
map[QContactPresence::FieldNickname] = osso_abook_contact_get_display_name(rosterContact);
map[QContactPresence::FieldPresenceState] = presenceTypeEnum;
map[QContactPresence::FieldPresenceStateText] = QString::fromLatin1(osso_abook_presence_get_presence_status_message(presence));
map[QContactPresence::FieldLinkedDetailUris] = mc_profile_get_unique_name(id); //use the unique name as a detail uri of the online account.
map["AccountPath"] = account->name; //MCAccount name: variable part of the D-Bus object path.
setDetailValues(map, rtn);
}
rtnList << rtn;
g_list_free (contacts);
}
/* Users can add Online account details manually. Eg: IRC username.
* evcardToSkip stringlist contains evCard attributes that have been already processed.
*/
GList *attributeList = e_vcard_get_attributes((EVCard*)eContact);
GList *node;
if (attributeList) {
for (node = attributeList; node != NULL; node = g_list_next (node)) {
EVCardAttribute* attr = (EVCardAttribute*)node->data;
if (!attr)
continue;
QString attributeName = QString::fromLatin1(e_vcard_attribute_get_name(attr));
// Skip attributes processed scanning roster contacts.
if (!evcardToSkip.contains(attributeName))
continue;
GList *params = e_vcard_attribute_get_params(attr);
GList *nodeP;
QString type;
// If the parameter list lenght is 1, X-OSSO-VALID is not specified
bool ossoValidIsOk = (g_list_length(params) == 1) ? true : false;
for (nodeP = params; nodeP != NULL; nodeP = g_list_next (nodeP)) {
EVCardAttributeParam* p = (EVCardAttributeParam*) nodeP->data;
QString paramName = QString::fromLatin1(e_vcard_attribute_param_get_name(p));
bool attrIsType = false;
bool attrIsOssoValid = false;
//If type is empty check if the attribute is "TYPE"
if (type.isEmpty())
attrIsType = paramName.contains(EVC_TYPE);
if(!ossoValidIsOk)
attrIsOssoValid = paramName.contains("X-OSSO-VALID");
if (!attrIsType && !attrIsOssoValid) {
qWarning () << "Skipping attribute parameter checking for" << paramName;
continue;
}
GList *values = e_vcard_attribute_param_get_values(p);
GList *node;
for (node = values; node != NULL; node = g_list_next (node)) {
QString attributeParameterValue = QString::fromLatin1(CONST_CHAR(node->data));
if (attrIsOssoValid) {
ossoValidIsOk = (attributeParameterValue == "yes")? true : false;
if (!ossoValidIsOk) {
qWarning() << "X-OSSO-VALID is false.";
break;
}
} else if (type.isEmpty()) {
type = attributeParameterValue;
if (type.isEmpty())
qCritical() << "TYPE is empty";
}
}
if (ossoValidIsOk && !type.isEmpty()) {
QContactPresence* rtn = new QContactPresence;
QVariantMap map;
map[QContactPresence::FieldNickname] = QString::fromLatin1(e_vcard_attribute_get_value(attr));
map[QContactPresence::FieldLinkedDetailUris] = type; // XXX FIXME
setDetailValues(map, rtn);
rtnList << rtn;
}
}
}
}
return rtnList;
}
QContactTimestamp* QContactABook::getTimestampDetail(EContact *eContact) const
{
QContactTimestamp* rtn = new QContactTimestamp;
QVariantMap map;
const char* rev = CONST_CHAR(e_contact_get(eContact, E_CONTACT_REV));
map[QContactTimestamp::FieldModificationTimestamp] = QDateTime::fromString(rev, Qt::ISODate);
FREE(rev);
setDetailValues(map, rtn);
return rtn;
}
QContactThumbnail* QContactABook::getThumbnailDetail(EContact *eContact) const
{
QContactThumbnail* rtn = new QContactThumbnail;
QVariantMap map;
OssoABookContact *aContact = A_CONTACT(eContact);
if (!aContact)
return rtn;
//GdkPixbuf* pixbuf = osso_abook_contact_get_avatar_pixbuf(aContact, NULL, NULL);
GdkPixbuf* pixbuf = osso_abook_avatar_get_image_rounded(OSSO_ABOOK_AVATAR(aContact), 64, 64, true, 4, NULL);
if (!GDK_IS_PIXBUF(pixbuf)){
FREE(pixbuf);
return rtn;
}
const uchar* bdata = (const uchar*)gdk_pixbuf_get_pixels(pixbuf);
QSize bsize(gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf));
//Convert GdkPixbuf to QPixmap
QImage converted(bdata, bsize.width(), bsize.height(), QImage::Format_ARGB32_Premultiplied);
map[QContactThumbnail::FieldThumbnail] = converted;
g_object_unref(pixbuf);
setDetailValues(map, rtn);
return rtn;
}
QContactUrl* QContactABook::getUrlDetail(EContact *eContact) const
{
QContactUrl* rtn = new QContactUrl;
QVariantMap map;
const char* url = CONST_CHAR(e_contact_get(eContact, E_CONTACT_HOMEPAGE_URL));
map[QContactUrl::FieldUrl] = url;
FREE(url);
setDetailValues(map, rtn);
return rtn;
}
static void addAttributeToAContact(const OssoABookContact* contact,
const QString& attrName, const QStringList& attrValues,
const QString& paramName = QString(), const QStringList& paramValues = QStringList(),
bool overwrite = true,
const int index = 0)
{
if (!contact)
return;
EVCard *vcard = E_VCARD (contact);
EVCardAttribute *attr = NULL;
EVCardAttributeParam* param = NULL;
QCM5_DEBUG << "Adding attribute" << attrName << "AttrValues:" << attrValues
<< "ParamName:" << paramName << "ParamValues:" << paramValues
<< "overwrite" << overwrite << "Index" << index;
if (overwrite)
{
GList *attributeList = osso_abook_contact_get_attributes(E_CONTACT(contact), qPrintable(attrName));
for (GList *node = g_list_last(attributeList); node != NULL; node = g_list_previous(node)) {
EVCardAttribute* eAttr = (EVCardAttribute*)node->data;
int pos = g_list_position(attributeList, node);
if (index > pos){
qWarning() << "Attribute doesn't found at position" << index;
return;
}
if (index != pos)
continue;
// Select the current EVCard Attribute if it contains the same parameters of
// attribute we want to modify/add
int matchedParams = 0;
GList* p = NULL;
p = e_vcard_attribute_get_param(eAttr, qPrintable(paramName));
while (p){
foreach(QString paramV, paramValues){
QString value = CONST_CHAR(p->data);
if (paramV == value)
++matchedParams;
}
p = p->next;
}
g_list_free(p);
if (matchedParams == paramValues.count()) {
attr = eAttr;
break;
}
}
}
// Check if attrValues contains something
bool noValues = true;
foreach(QString s, attrValues){
if (!s.isEmpty()){
noValues = false;
break;
}
}
if (attr) {
if (noValues){
e_vcard_remove_attribute(vcard, attr);
return;
} else {
e_vcard_attribute_remove_values(attr);
}
} else {
if (noValues)
return;
// Create Attribute with right parameters
attr = e_vcard_attribute_new(NULL, qPrintable(attrName));
if (!paramName.isEmpty()){
param = e_vcard_attribute_param_new(qPrintable(paramName));
foreach(QString paramV, paramValues)
e_vcard_attribute_param_add_value(param, qPrintable(paramV));
e_vcard_attribute_add_param(attr, param);
}
// Save the attribute to the VCard
e_vcard_add_attribute(vcard, attr);
}
// Add values to the attribute
foreach(QString attrV, attrValues) {
e_vcard_attribute_add_value(attr, qPrintable(attrV));
}
// Debugging
{
const char* dbgStr = e_vcard_to_string(vcard, EVC_FORMAT_VCARD_30);
QCM5_DEBUG << "Modified VCard" << dbgStr;
FREE(dbgStr);
}
}
OssoABookContact* QContactABook::convert(const QContact *contact) const
{
Q_CHECK_PTR(contact);
OssoABookContact* rtn;
// Get aContact if it exists or create a new one if it doesn't
QContactLocalId id = contact->localId();
QCM5_DEBUG << "Converting QContact id:" << id << " to aContact";
if (id){
rtn = getAContact(id);
// It's not safe to commit changes to a contact that has been modified.
// This problem affects attributes with the same name and parameters such as
// EMail, Address...
QContactTimestamp* ts = getTimestampDetail(E_CONTACT(rtn));
if (*ts != contact->detail<QContactTimestamp>()){
delete ts;
return NULL;
}
delete ts;
} else {
rtn = osso_abook_contact_new();
}
QList<QContactDetail> allDetails = contact->details();
foreach(const QContactDetail &detail, allDetails){
QString definitionName = detail.definitionName();
QCM5_DEBUG << "Saving" << definitionName;
//QContactDisplayLabel::DefinitionName
if (definitionName == QContactAddress::DefinitionName){
setAddressDetail(rtn, detail);
} else
if (definitionName == QContactAvatar::DefinitionName){
setAvatarDetail(rtn, detail);
} else
if (definitionName == QContactBirthday::DefinitionName){
setBirthdayDetail(rtn, detail);
} else
if (definitionName == QContactEmailAddress::DefinitionName){
setEmailDetail(rtn, detail);
} else
if (definitionName == QContactGender::DefinitionName){
setGenderDetail(rtn, detail);
} else
if (definitionName == QContactName::DefinitionName){
setNameDetail(rtn, detail);
} else
if (definitionName == QContactNickname::DefinitionName){
setNicknameDetail(rtn, detail);
} else
if (definitionName == QContactNote::DefinitionName){
setNoteDetail(rtn, detail);
} else
if (definitionName == QContactOnlineAccount::DefinitionName){
setOnlineAccountDetail(rtn, detail);
} else
if (definitionName == QContactOrganization::DefinitionName){
setOrganizationDetail(rtn, detail);
} else
if (definitionName == QContactPhoneNumber::DefinitionName){
setPhoneDetail(rtn, detail);
} else
if (definitionName == QContactThumbnail::DefinitionName){
setThumbnailDetail(rtn, detail);
} else
if (definitionName == QContactUrl::DefinitionName){
setUrlDetail(rtn, detail);
}
}
return rtn;
}
void QContactABook::setAddressDetail(const OssoABookContact* aContact, const QContactAddress& detail) const
{
if (!aContact) return;
uint detailUri;
const uint nAddressElems = 7;
QStringList adrAttrValues,
lblAttrValues,
paramValues;
// Get parameters
foreach(QString c, detail.contexts())
paramValues << c.toUpper();
// Initialize adrAttrValues;
for (uint i = 0; i < nAddressElems; ++i)
adrAttrValues << "";
// Fill adrAttrValues
QVariantMap vm = detail.variantValues();
QMapIterator<QString, QVariant> i(vm);
while (i.hasNext()) {
i.next();
int index = -1;
QString key = i.key();
if (key == QContactAddress::FieldPostOfficeBox) index = 0;
else if (key == "Estension") index = 1;
else if (key == QContactAddress::FieldStreet) index = 2;
else if (key == QContactAddress::FieldLocality) index = 3;
else if (key == QContactAddress::FieldRegion) index = 4;
else if (key == QContactAddress::FieldPostcode) index = 5;
else if (key == QContactAddress::FieldCountry) index = 6;
else if (key == QContactDetail::FieldContext) continue;
else if (key == QContactDetail::FieldDetailUri) detailUri = i.value().toInt();
else {
qWarning() << "Address contains an invalid field:" << key;
return;
}
if (index != -1)
adrAttrValues[index] = i.value().toString();
}
// Fill lblAttrValues
QStringList labelValues;
labelValues << adrAttrValues[1]
<< adrAttrValues[2]
<< adrAttrValues[0]
<< adrAttrValues[3]
<< adrAttrValues[4]
<< adrAttrValues[5]
<< adrAttrValues[6];
lblAttrValues << labelValues.join(", ");
// Skip if adrAttrValues contains only empty strings
bool noValues = true;
foreach(QString s, adrAttrValues){
if (!s.isEmpty()){
noValues = false;
break;
}
}
if (noValues)
return;
// Saving LABEL and ADR attributes into the VCard
addAttributeToAContact(aContact, EVC_ADR, adrAttrValues, EVC_TYPE, paramValues, true, detailUri);
//BUG Label attribute contains a bug
//It contains TYPE(TYPE) if ADDRESS doesn't contain any parameter value.
if (paramValues.isEmpty())
paramValues << EVC_TYPE;
addAttributeToAContact(aContact, EVC_LABEL, lblAttrValues, EVC_TYPE, paramValues, true, detailUri);
}
void QContactABook::setThumbnailDetail(const OssoABookContact* aContact, const QContactThumbnail& detail) const
{
if (!aContact) return;
EBook *book;
{
OssoABookRoster* roster = A_ROSTER(m_abookAgregator);
book = osso_abook_roster_get_book(roster);
}
QImage image = detail.thumbnail();
if (image.isNull())
return;
if (image.format() != QImage::Format_ARGB32_Premultiplied)
image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data(image.bits(), GDK_COLORSPACE_RGB,
image.hasAlphaChannel(), 8,
image.width(), image.height(),
image.bytesPerLine(), 0, 0);
osso_abook_contact_set_pixbuf((OssoABookContact*)aContact, pixbuf, book, 0);
g_object_unref(pixbuf);
}
void QContactABook::setAvatarDetail(const OssoABookContact* aContact, const QContactAvatar& detail) const
{
Q_UNUSED(aContact)
Q_UNUSED(detail);
// XXX TODO: FIXME
// if (!aContact) return;
//
// EBook *book;
// {
// OssoABookRoster* roster = A_ROSTER(m_abookAgregator);
// book = osso_abook_roster_get_book(roster);
// }
//
// QImage image = detail.pixmap().toImage();
// if (image.format() != QImage::Format_ARGB32_Premultiplied)
// image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
// GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data(image.bits(), GDK_COLORSPACE_RGB,
// image.hasAlphaChannel(), 8,
// image.width(), image.height(),
// image.bytesPerLine(), 0, 0);
// osso_abook_contact_set_pixbuf((OssoABookContact*)aContact, pixbuf, book, 0);
// g_object_unref(pixbuf);
}
void QContactABook::setBirthdayDetail(const OssoABookContact* aContact, const QContactBirthday& detail) const
{
if (!aContact) return;
QStringList attrValues;
attrValues << detail.value(QContactBirthday::FieldBirthday);
addAttributeToAContact(aContact, EVC_BDAY, attrValues);
}
void QContactABook::setEmailDetail(const OssoABookContact* aContact, const QContactEmailAddress& detail) const
{
if (!aContact) return;
QStringList attrValues,
paramValues;
QVariantMap vm = detail.variantValues();
QMapIterator<QString, QVariant> i(vm);
while (i.hasNext()) {
i.next();
QString key = i.key();
// We don't want to save the Detail URI
if (key == QContactDetail::FieldDetailUri)
continue;
if (key == QContactDetail::FieldContext)
paramValues << i.value().toString().toUpper();
else
attrValues << i.value().toString();
}
addAttributeToAContact(aContact, EVC_EMAIL, attrValues, EVC_TYPE, paramValues, true, detail.detailUri().toInt());
}
void QContactABook::setGenderDetail(const OssoABookContact* aContact, const QContactGender& detail) const
{
if (!aContact) return;
QStringList attrValues;
attrValues << detail.value(QContactGender::FieldGender).toLower();
addAttributeToAContact(aContact, "X-GENDER", attrValues);
}
void QContactABook::setNameDetail(const OssoABookContact* aContact, const QContactName& detail) const
{
if (!aContact) return;
QStringList attrValues;
// Save First and Last name in the N vcard attribute
{
QStringList supportedDetailValues;
supportedDetailValues << QContactName::FieldFirstName << QContactName::FieldLastName;
foreach(QString key, supportedDetailValues){
attrValues << detail.value(key);
}
//REMOVE ME - We don't want to support custom label
if (attrValues[0].isEmpty()){
qWarning() << "QContactName::FieldFirstName is empty";
attrValues[0] = detail.customLabel();
}
addAttributeToAContact(aContact, EVC_N, attrValues);
}
// Save Fist + Last name in the FN card attribute
{
attrValues << attrValues.join(" ");
addAttributeToAContact(aContact, EVC_FN, attrValues);
}
}
void QContactABook::setNicknameDetail(const OssoABookContact* aContact, const QContactNickname& detail) const
{
if (!aContact) return;
QStringList attrValues;
attrValues << detail.value(QContactNickname::FieldNickname);
addAttributeToAContact(aContact, EVC_NICKNAME, attrValues);
}
void QContactABook::setNoteDetail(const OssoABookContact* aContact, const QContactNote& detail) const
{
if (!aContact) return;
QStringList attrValues;
attrValues << detail.value(QContactNote::FieldNote);
addAttributeToAContact(aContact, EVC_NOTE, attrValues);
}
/*NOTE: Online details comes from Telepathy or can be added manually by the user.
* OnlineDetals coming from Telepathy/Roster contacts can't be saved.
*/
void QContactABook::setOnlineAccountDetail(const OssoABookContact* aContact, const QContactOnlineAccount& detail) const
{
if (!aContact)
return;
Q_UNUSED(detail);
}
void QContactABook::setOrganizationDetail(const OssoABookContact* aContact, const QContactOrganization& detail) const
{
if (!aContact) return;
QStringList attrValues;
attrValues << detail.value(QContactOrganization::FieldTitle);
addAttributeToAContact(aContact, EVC_ORG, attrValues);
}
void QContactABook::setPhoneDetail(const OssoABookContact* aContact, const QContactPhoneNumber& detail) const
{
if (!aContact) return;
QStringList attrValues,
paramValues;
QVariantMap vm = detail.variantValues();
QMapIterator<QString, QVariant> i(vm);
while (i.hasNext()) {
i.next();
const QString key = i.key();
// We don't want to save the Detail URI
if (key == QContactDetail::FieldDetailUri)
continue;
if (key == QContactDetail::FieldContext ||
key == QContactPhoneNumber::FieldSubTypes){
QString value = i.value().toString();
if (value == QContactPhoneNumber::SubTypeMobile)
value = "CELL";
paramValues << value.toUpper();
} else
attrValues << i.value().toString();
}
// Avoid unsupported type
if (paramValues.isEmpty())
paramValues << "VOICE";
addAttributeToAContact(aContact, EVC_TEL, attrValues, EVC_TYPE, paramValues, true, detail.detailUri().toInt());
}
void QContactABook::setUrlDetail(const OssoABookContact* aContact, const QContactUrl& detail) const
{
if (!aContact) return;
QStringList attrValues;
attrValues << detail.value(QContactUrl::FieldUrl);
addAttributeToAContact(aContact, EVC_URL, attrValues);
}