2005-11-05 23:04:33 +01:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2005 Patrick Ohly
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <memory>
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
#include "EvolutionContactSource.h"
|
|
|
|
|
2005-12-03 15:35:11 +01:00
|
|
|
#include <common/base/Log.h>
|
|
|
|
|
2005-11-05 23:04:33 +01:00
|
|
|
EvolutionContactSource::EvolutionContactSource( const string &name,
|
|
|
|
const string &changeId,
|
|
|
|
const string &id,
|
2005-11-26 22:16:03 +01:00
|
|
|
EVCardFormat vcardFormat ) :
|
|
|
|
EvolutionSyncSource( name, changeId, id ),
|
|
|
|
m_vcardFormat( vcardFormat )
|
2005-11-05 23:04:33 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
EvolutionContactSource::~EvolutionContactSource()
|
|
|
|
{
|
|
|
|
close();
|
|
|
|
}
|
|
|
|
|
|
|
|
EvolutionSyncSource::sources EvolutionContactSource::getSyncBackends()
|
|
|
|
{
|
|
|
|
ESourceList *sources = NULL;
|
|
|
|
|
|
|
|
if (!e_book_get_addressbooks(&sources, NULL)) {
|
|
|
|
throw "unable to access address books";
|
|
|
|
}
|
|
|
|
|
|
|
|
EvolutionSyncSource::sources result;
|
|
|
|
|
|
|
|
for (GSList *g = e_source_list_peek_groups (sources); g; g = g->next) {
|
|
|
|
ESourceGroup *group = E_SOURCE_GROUP (g->data);
|
|
|
|
for (GSList *s = e_source_group_peek_sources (group); s; s = s->next) {
|
|
|
|
ESource *source = E_SOURCE (s->data);
|
|
|
|
result.push_back( EvolutionSyncSource::source( e_source_peek_name(source),
|
|
|
|
e_source_get_uri(source) ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EvolutionContactSource::open()
|
|
|
|
{
|
|
|
|
ESourceList *sources;
|
|
|
|
if (!e_book_get_addressbooks(&sources, NULL)) {
|
|
|
|
throw "unable to access address books";
|
|
|
|
}
|
|
|
|
|
2005-11-26 22:16:03 +01:00
|
|
|
ESource *source = findSource( sources, m_id );
|
2005-11-05 23:04:33 +01:00
|
|
|
if (!source) {
|
2005-11-26 22:16:03 +01:00
|
|
|
throw string(getName()) + ": no such address book: '" + m_id + "'";
|
2005-11-05 23:04:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
GError *gerror = NULL;
|
|
|
|
m_addressbook.set( e_book_new( source, &gerror ), "address book" );
|
|
|
|
|
|
|
|
if (!e_book_open( m_addressbook, TRUE, &gerror) ) {
|
|
|
|
throwError( "opening address book", gerror );
|
|
|
|
}
|
2005-11-26 22:16:03 +01:00
|
|
|
}
|
2005-11-05 23:04:33 +01:00
|
|
|
|
2005-11-26 22:16:03 +01:00
|
|
|
int EvolutionContactSource::beginSync()
|
|
|
|
{
|
2005-12-10 18:48:07 +01:00
|
|
|
string buffer = "sync mode is: ";
|
|
|
|
SyncMode mode = getSyncMode();
|
|
|
|
buffer += mode == SYNC_SLOW ? "slow" :
|
|
|
|
mode == SYNC_TWO_WAY ? "two-way" :
|
|
|
|
mode == SYNC_ONE_WAY_FROM_SERVER ? "one-way" :
|
|
|
|
mode == SYNC_REFRESH_FROM_SERVER ? "refresh" :
|
|
|
|
"???";
|
|
|
|
LOG.info( buffer.c_str() );
|
|
|
|
|
2005-11-26 22:16:03 +01:00
|
|
|
try {
|
|
|
|
GError *gerror = NULL;
|
|
|
|
|
2005-12-10 22:10:43 +01:00
|
|
|
// reset state
|
|
|
|
m_isModified = false;
|
|
|
|
m_allItems.clear();
|
|
|
|
m_newItems.clear();
|
|
|
|
m_updatedItems.clear();
|
|
|
|
m_deletedItems.clear();
|
|
|
|
|
2005-12-11 18:13:36 +01:00
|
|
|
// determine what to do
|
|
|
|
bool needAll = false;
|
|
|
|
bool needPartial = false;
|
|
|
|
bool deleteLocal = false;
|
2005-12-10 22:10:43 +01:00
|
|
|
switch (mode) {
|
2005-12-11 18:13:36 +01:00
|
|
|
case SYNC_SLOW:
|
|
|
|
needAll = true;
|
|
|
|
m_isModified = true;
|
|
|
|
break;
|
|
|
|
case SYNC_TWO_WAY:
|
|
|
|
needPartial = true;
|
|
|
|
break;
|
|
|
|
case SYNC_REFRESH_FROM_SERVER:
|
|
|
|
deleteLocal = true;
|
|
|
|
m_isModified = true;
|
|
|
|
break;
|
|
|
|
case SYNC_NONE:
|
|
|
|
// special mode for testing: prepare both all and partial lists
|
|
|
|
needAll = needPartial = true;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw "unsupported sync mode, valid are only: slow, two-way, refresh";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (deleteLocal) {
|
|
|
|
gptr<EBookQuery> allItemsQuery( e_book_query_any_field_contains(""), "query" );
|
|
|
|
GList *nextItem;
|
|
|
|
if (!e_book_get_contacts( m_addressbook, allItemsQuery, &nextItem, &gerror )) {
|
|
|
|
throwError( "reading all items", gerror );
|
|
|
|
}
|
|
|
|
while (nextItem) {
|
|
|
|
const char *uid = (const char *)e_contact_get_const(E_CONTACT(nextItem->data),
|
|
|
|
E_CONTACT_UID);
|
|
|
|
if (!e_book_remove_contact( m_addressbook, uid, &gerror ) ) {
|
|
|
|
throwError( string( "deleting contact" ) + uid,
|
|
|
|
gerror );
|
|
|
|
}
|
|
|
|
nextItem = nextItem->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (needAll) {
|
2005-12-10 22:10:43 +01:00
|
|
|
gptr<EBookQuery> allItemsQuery( e_book_query_any_field_contains(""), "query" );
|
|
|
|
GList *nextItem;
|
|
|
|
if (!e_book_get_contacts( m_addressbook, allItemsQuery, &nextItem, &gerror )) {
|
|
|
|
throwError( "reading all items", gerror );
|
|
|
|
}
|
|
|
|
while (nextItem) {
|
|
|
|
const char *uid = (const char *)e_contact_get_const(E_CONTACT(nextItem->data),
|
2005-12-03 15:35:11 +01:00
|
|
|
E_CONTACT_UID);
|
2005-12-10 22:10:43 +01:00
|
|
|
logItem( string(uid), "existing item" );
|
|
|
|
m_allItems.push_back(uid);
|
|
|
|
nextItem = nextItem->next;
|
|
|
|
}
|
2005-12-11 18:13:36 +01:00
|
|
|
}
|
2005-12-10 22:10:43 +01:00
|
|
|
|
2005-12-11 18:13:36 +01:00
|
|
|
if (needPartial) {
|
2005-12-10 22:10:43 +01:00
|
|
|
GList *nextItem;
|
|
|
|
if (!e_book_get_changes( m_addressbook, (char *)m_changeId.c_str(), &nextItem, &gerror )) {
|
|
|
|
throwError( "reading changes", gerror );
|
|
|
|
}
|
|
|
|
while (nextItem) {
|
|
|
|
EBookChange *ebc = (EBookChange *)nextItem->data;
|
|
|
|
const char *uid = (const char *)e_contact_get_const( ebc->contact, E_CONTACT_UID );
|
|
|
|
|
|
|
|
switch (ebc->change_type) {
|
|
|
|
case E_BOOK_CHANGE_CARD_ADDED:
|
|
|
|
logItem( string(uid), "was added" );
|
|
|
|
m_newItems.push_back( uid );
|
|
|
|
break;
|
|
|
|
case E_BOOK_CHANGE_CARD_MODIFIED:
|
|
|
|
logItem( string(uid), "was modified" );
|
|
|
|
m_updatedItems.push_back( uid );
|
|
|
|
break;
|
|
|
|
case E_BOOK_CHANGE_CARD_DELETED:
|
|
|
|
logItem( string(uid), "was deleted" );
|
|
|
|
m_deletedItems.push_back( uid );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
nextItem = nextItem->next;
|
|
|
|
}
|
2005-11-05 23:04:33 +01:00
|
|
|
}
|
2005-11-26 22:16:03 +01:00
|
|
|
} catch( ... ) {
|
|
|
|
m_hasFailed = true;
|
|
|
|
// TODO: properly set error
|
|
|
|
return 1;
|
2005-11-05 23:04:33 +01:00
|
|
|
}
|
2005-11-26 22:16:03 +01:00
|
|
|
return 0;
|
2005-11-05 23:04:33 +01:00
|
|
|
}
|
|
|
|
|
2005-11-26 22:16:03 +01:00
|
|
|
int EvolutionContactSource::endSync()
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
endSyncThrow();
|
|
|
|
} catch ( ... ) {
|
|
|
|
m_hasFailed = true;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EvolutionContactSource::endSyncThrow()
|
2005-11-05 23:04:33 +01:00
|
|
|
{
|
2005-12-03 15:35:11 +01:00
|
|
|
LOG.info( m_isModified ? "EvolutionContactSource: address book was modified" : "EvolutionContactSource: no modifications" );
|
2005-11-26 22:16:03 +01:00
|
|
|
if (m_isModified) {
|
2005-11-05 23:04:33 +01:00
|
|
|
GError *gerror = NULL;
|
|
|
|
GList *nextItem;
|
|
|
|
// move change_id forward so that our own changes are not listed the next time
|
|
|
|
if (!e_book_get_changes( m_addressbook, (char *)m_changeId.c_str(), &nextItem, &gerror )) {
|
|
|
|
throwError( "reading changes", gerror );
|
|
|
|
}
|
|
|
|
}
|
2005-11-26 22:16:03 +01:00
|
|
|
resetItems();
|
|
|
|
m_isModified = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EvolutionContactSource::close()
|
|
|
|
{
|
|
|
|
endSyncThrow();
|
2005-11-05 23:04:33 +01:00
|
|
|
m_addressbook = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-11-25 21:53:04 +01:00
|
|
|
SyncItem *EvolutionContactSource::createItem( const string &uid, SyncState state )
|
2005-11-05 23:04:33 +01:00
|
|
|
{
|
|
|
|
// this function must never throw an exception
|
|
|
|
// because it is called inside the Sync4j C++ API library
|
|
|
|
// which cannot handle exceptions
|
|
|
|
try {
|
2005-12-03 15:35:11 +01:00
|
|
|
logItem( uid, "extracting from EV" );
|
|
|
|
|
2005-11-05 23:04:33 +01:00
|
|
|
EContact *contact;
|
|
|
|
GError *gerror = NULL;
|
|
|
|
if (! e_book_get_contact( m_addressbook,
|
|
|
|
uid.c_str(),
|
|
|
|
&contact,
|
|
|
|
&gerror ) ) {
|
|
|
|
throwError( string( "reading contact" ) + uid,
|
|
|
|
gerror );
|
|
|
|
}
|
|
|
|
gptr<EContact, GObject> contactptr( contact, "contact" );
|
|
|
|
gptr<char> vcardstr(e_vcard_to_string( &contactptr->parent,
|
|
|
|
m_vcardFormat ) );
|
|
|
|
if (!vcardstr) {
|
|
|
|
throwError( string( "converting contact" ) + uid, NULL );
|
|
|
|
}
|
|
|
|
|
2005-12-03 15:35:11 +01:00
|
|
|
// hack: patch version so that Sync4j 2.3 accepts it
|
|
|
|
char *ver = strstr(vcardstr, "VERSION:3.0" );
|
|
|
|
if (ver) {
|
|
|
|
ver[8] = '2';
|
|
|
|
ver[10] = '1';
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG.debug( vcardstr );
|
2005-11-05 23:04:33 +01:00
|
|
|
auto_ptr<SyncItem> item( new SyncItem( uid.c_str() ) );
|
2005-12-03 15:35:11 +01:00
|
|
|
item->setData( vcardstr, strlen( vcardstr ) + 1 );
|
2005-11-05 23:04:33 +01:00
|
|
|
item->setDataType( getMimeType() );
|
2005-11-25 21:53:04 +01:00
|
|
|
item->setModificationTime( 0 );
|
|
|
|
item->setState( state );
|
2005-11-05 23:04:33 +01:00
|
|
|
|
|
|
|
return item.release();
|
|
|
|
} catch (...) {
|
|
|
|
m_hasFailed = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int EvolutionContactSource::addItem(SyncItem& item)
|
|
|
|
{
|
|
|
|
try {
|
2005-12-03 15:35:11 +01:00
|
|
|
logItem( item, "adding" );
|
|
|
|
|
2005-11-25 21:53:04 +01:00
|
|
|
string data = getData(item);
|
|
|
|
gptr<EContact, GObject> contact(e_contact_new_from_vcard(data.c_str()));
|
2005-11-05 23:04:33 +01:00
|
|
|
if( contact ) {
|
|
|
|
GError *gerror = NULL;
|
|
|
|
e_contact_set(contact, E_CONTACT_UID, NULL);
|
|
|
|
if (e_book_add_contact(m_addressbook, contact, &gerror)) {
|
|
|
|
item.setKey( (const char *)e_contact_get_const( contact, E_CONTACT_UID ) );
|
|
|
|
} else {
|
|
|
|
throwError( "storing new contact", gerror );
|
|
|
|
}
|
|
|
|
} else {
|
2005-11-25 21:53:04 +01:00
|
|
|
throwError( string( "parsing vcard" ) + data,
|
2005-11-05 23:04:33 +01:00
|
|
|
NULL );
|
|
|
|
}
|
2005-11-26 22:16:03 +01:00
|
|
|
|
|
|
|
m_isModified = true;
|
2005-11-05 23:04:33 +01:00
|
|
|
} catch ( ... ) {
|
|
|
|
m_hasFailed = true;
|
2005-11-26 22:16:03 +01:00
|
|
|
return STC_COMMAND_FAILED;
|
2005-11-05 23:04:33 +01:00
|
|
|
}
|
2005-11-26 22:16:03 +01:00
|
|
|
return STC_OK;
|
2005-11-05 23:04:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int EvolutionContactSource::updateItem(SyncItem& item)
|
|
|
|
{
|
|
|
|
try {
|
2005-12-03 15:35:11 +01:00
|
|
|
logItem( item, "updating" );
|
|
|
|
|
2005-11-25 21:53:04 +01:00
|
|
|
string data = getData(item);
|
|
|
|
gptr<EContact, GObject> contact(e_contact_new_from_vcard(data.c_str()));
|
2005-11-05 23:04:33 +01:00
|
|
|
if( contact ) {
|
|
|
|
GError *gerror = NULL;
|
2005-11-25 21:53:04 +01:00
|
|
|
e_contact_set( contact, E_CONTACT_UID, item.getKey() );
|
2005-11-05 23:04:33 +01:00
|
|
|
if ( e_book_commit_contact(m_addressbook, contact, &gerror) ) {
|
|
|
|
const char *uid = (const char *)e_contact_get_const(contact, E_CONTACT_UID);
|
|
|
|
if (uid) {
|
|
|
|
item.setKey( uid );
|
|
|
|
}
|
|
|
|
} else {
|
2005-11-25 21:53:04 +01:00
|
|
|
throwError( string( "updating contact" ) + item.getKey(), gerror );
|
2005-11-05 23:04:33 +01:00
|
|
|
}
|
|
|
|
} else {
|
2005-11-25 21:53:04 +01:00
|
|
|
throwError( string( "parsing vcard" ) + data,
|
2005-11-05 23:04:33 +01:00
|
|
|
NULL );
|
|
|
|
}
|
2005-11-26 22:16:03 +01:00
|
|
|
|
|
|
|
m_isModified = true;
|
2005-11-05 23:04:33 +01:00
|
|
|
} catch ( ... ) {
|
|
|
|
m_hasFailed = true;
|
2005-11-26 22:16:03 +01:00
|
|
|
return STC_COMMAND_FAILED;
|
2005-11-05 23:04:33 +01:00
|
|
|
}
|
2005-11-26 22:16:03 +01:00
|
|
|
return STC_OK;
|
2005-11-05 23:04:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int EvolutionContactSource::deleteItem(SyncItem& item)
|
|
|
|
{
|
|
|
|
try {
|
2005-12-03 15:35:11 +01:00
|
|
|
logItem( item, "deleting" );
|
|
|
|
|
2005-11-05 23:04:33 +01:00
|
|
|
GError *gerror = NULL;
|
2005-11-25 21:53:04 +01:00
|
|
|
if (!e_book_remove_contact( m_addressbook, item.getKey(), &gerror ) ) {
|
|
|
|
throwError( string( "deleting contact" ) + item.getKey(),
|
2005-11-05 23:04:33 +01:00
|
|
|
gerror );
|
|
|
|
}
|
2005-11-26 22:16:03 +01:00
|
|
|
|
|
|
|
m_isModified = true;
|
2005-11-05 23:04:33 +01:00
|
|
|
} catch( ... ) {
|
|
|
|
m_hasFailed = true;
|
2005-11-26 22:16:03 +01:00
|
|
|
return STC_COMMAND_FAILED;
|
2005-11-05 23:04:33 +01:00
|
|
|
}
|
2005-11-26 22:16:03 +01:00
|
|
|
return STC_OK;
|
2005-11-05 23:04:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *EvolutionContactSource::getMimeType()
|
|
|
|
{
|
|
|
|
// todo: be more precise here
|
|
|
|
switch( m_vcardFormat ) {
|
|
|
|
case EVC_FORMAT_VCARD_21:
|
|
|
|
return "text/vcard";
|
|
|
|
break;
|
|
|
|
case EVC_FORMAT_VCARD_30:
|
|
|
|
return "text/vcard";
|
|
|
|
}
|
|
|
|
|
|
|
|
return "test/vcard";
|
|
|
|
}
|
2005-12-03 15:35:11 +01:00
|
|
|
|
|
|
|
void EvolutionContactSource::logItem( const string &uid, const string &info )
|
|
|
|
{
|
|
|
|
if (LOG.getLevel() >= LOG_LEVEL_INFO) {
|
|
|
|
string line;
|
|
|
|
EContact *contact;
|
|
|
|
GError *gerror = NULL;
|
|
|
|
|
|
|
|
if (e_book_get_contact( m_addressbook,
|
|
|
|
uid.c_str(),
|
|
|
|
&contact,
|
|
|
|
&gerror )) {
|
2005-12-04 20:49:31 +01:00
|
|
|
const char *fileas = (const char *)e_contact_get_const( contact, E_CONTACT_FILE_AS );
|
|
|
|
const char *name = (const char *)e_contact_get_const( contact, E_CONTACT_FULL_NAME );
|
|
|
|
|
|
|
|
line += fileas ? fileas :
|
|
|
|
name ? name :
|
|
|
|
"<unnamed contact>";
|
2005-12-03 15:35:11 +01:00
|
|
|
} else {
|
|
|
|
line += "<unknown contact>";
|
|
|
|
}
|
|
|
|
line += " (";
|
|
|
|
line += uid;
|
|
|
|
line += "): ";
|
|
|
|
line += info;
|
|
|
|
|
|
|
|
LOG.info( line.c_str() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EvolutionContactSource::logItem( SyncItem &item, const string &info )
|
|
|
|
{
|
|
|
|
if (LOG.getLevel() >= LOG_LEVEL_INFO) {
|
|
|
|
string line;
|
2005-12-12 20:15:13 +01:00
|
|
|
const char *data = (const char *)item.getData();
|
|
|
|
int datasize = item.getDataSize();
|
|
|
|
if (datasize <= 0) {
|
|
|
|
data = "";
|
|
|
|
datasize = 0;
|
|
|
|
}
|
|
|
|
string vcard( data, datasize );
|
2005-12-03 15:35:11 +01:00
|
|
|
|
|
|
|
int offset = vcard.find( "FN:");
|
|
|
|
if (offset != vcard.npos) {
|
|
|
|
int len = vcard.find( "\r", offset ) - offset - 3;
|
|
|
|
line += vcard.substr( offset + 3, len );
|
|
|
|
} else {
|
|
|
|
line += "<unnamed SyncItem>";
|
|
|
|
}
|
|
|
|
|
2005-12-04 20:49:31 +01:00
|
|
|
if (!item.getKey() ) {
|
|
|
|
line += ", NULL UID (?!)";
|
|
|
|
} else if (!strlen( item.getKey() )) {
|
|
|
|
line += ", empty UID";
|
2005-12-03 15:35:11 +01:00
|
|
|
} else {
|
|
|
|
line += ", ";
|
|
|
|
line += item.getKey();
|
|
|
|
|
|
|
|
EContact *contact;
|
|
|
|
GError *gerror = NULL;
|
|
|
|
if (e_book_get_contact( m_addressbook,
|
|
|
|
item.getKey(),
|
|
|
|
&contact,
|
|
|
|
&gerror )) {
|
|
|
|
line += "EV ";
|
2005-12-04 20:49:31 +01:00
|
|
|
const char *fileas = (const char *)e_contact_get_const( contact, E_CONTACT_FILE_AS );
|
|
|
|
const char *name = (const char *)e_contact_get_const( contact, E_CONTACT_FULL_NAME );
|
|
|
|
|
|
|
|
line += fileas ? fileas :
|
|
|
|
name ? name :
|
|
|
|
"<unnamed contact>";
|
2005-12-03 15:35:11 +01:00
|
|
|
} else {
|
|
|
|
line += ", not in Evolution";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
line += ": ";
|
|
|
|
line += info;
|
|
|
|
|
|
|
|
LOG.info( line.c_str() );
|
|
|
|
}
|
|
|
|
}
|