kde-playground/kdepim-runtime/opensync/datasink.cpp
2015-04-14 22:08:21 +00:00

433 lines
12 KiB
C++

/*
Copyright (c) 2008 Volker Krause <vkrause@kde.org>
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This library 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 Library General Public
License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
#include "datasink.h"
// calendar includes
#include <kcal/incidence.h>
#include <kcal/icalformat.h>
// contact includes
#include <kabc/vcardconverter.h>
#include <kabc/addressee.h>
// notes includes
// TODO
#include <KDebug>
#include <KLocale>
#include <KUrl>
using namespace Akonadi;
typedef boost::shared_ptr<KCal::Incidence> IncidencePtr;
DataSink::DataSink( int type ) :
SinkBase( GetChanges | Commit | SyncDone )
{
m_type = type;
}
DataSink::~DataSink() {
if ( m_hashtable )
osync_hashtable_unref( m_hashtable );
}
bool DataSink::initialize(OSyncPlugin * plugin, OSyncPluginInfo * info, OSyncObjTypeSink *sink, OSyncError ** error)
{
Q_UNUSED( plugin );
Q_UNUSED( info );
Q_UNUSED( error );
bool enabled = osync_objtype_sink_is_enabled( sink );
if ( !enabled ) {
kDebug() << "sink is not enabled..";
return false;
}
QString configdir( osync_plugin_info_get_configdir( info ) );
QString hashfile = QString( "%1/%2-hash.db" ).arg( configdir, osync_objtype_sink_get_name( sink ) );
m_hashtable = osync_hashtable_new( hashfile.toUtf8(), osync_objtype_sink_get_name( sink ), error );
if ( !osync_hashtable_load( m_hashtable, error ) ) {
osync_trace( TRACE_EXIT_ERROR, "%s: %s", __PRETTY_FUNCTION__, osync_error_print( error ) );
return false;
}
wrapSink( sink );
return true;
}
Akonadi::Collection DataSink::collection() const
{
OSyncPluginConfig *config = osync_plugin_info_get_config( pluginInfo() );
Q_ASSERT( config );
const char *objtype = osync_objtype_sink_get_name( sink() );
OSyncPluginResource *res = osync_plugin_config_find_active_resource( config, objtype );
if ( !res ) {
error( OSYNC_ERROR_MISCONFIGURATION, i18n( "No active resource for type \"%1\" found", objtype ) );
return Collection();
}
const KUrl url = KUrl( osync_plugin_resource_get_url( res ) );
if ( url.isEmpty() ) {
error( OSYNC_ERROR_MISCONFIGURATION, i18n( "Url for object type \"%1\" is not configured.", objtype ) );
return Collection();
}
return Collection::fromUrl( url );
}
void DataSink::getChanges()
{
// ### broken in OpenSync, I don't get valid configuration here!
#if 1
Collection col = collection();
if ( !col.isValid() )
return;
#else
Collection col( 409 );
#endif
OSyncError *oerror = 0;
if ( osync_objtype_sink_get_slowsync( sink() ) ) {
kDebug() << "we're in the middle of slow-syncing...";
osync_trace( TRACE_INTERNAL, "Got slow-sync, resetting hashtable" );
if ( !osync_hashtable_slowsync( m_hashtable, &oerror ) ) {
warning( oerror );
osync_trace( TRACE_EXIT_ERROR, "%s: %s", __PRETTY_FUNCTION__, osync_error_print( &oerror ) );
return;
}
}
ItemFetchJob *job = new ItemFetchJob( col );
job->fetchScope().fetchFullPayload();
QObject::connect( job, SIGNAL(itemsReceived(Akonadi::Item::List)), this, SLOT(slotItemsReceived(Akonadi::Item::List)) );
QObject::connect( job, SIGNAL(result(KJob*)), this, SLOT(slotGetChangesFinished(KJob*)) );
// FIXME give me a real eventloop please!
if ( !job->exec() ) {
error( OSYNC_ERROR_IO_ERROR, job->errorText() );
return;
}
}
void DataSink::slotItemsReceived( const Item::List &items )
{
kDebug() << "retrieved" << items.count() << "items";
Q_FOREACH ( const Item& item, items ) {
//kDebug() << item.payloadData();
reportChange( item );
}
}
void DataSink::reportChange( const Item& item )
{
OSyncFormatEnv *formatenv = osync_plugin_info_get_format_env( pluginInfo() );
OSyncObjFormat *format = osync_format_env_find_objformat( formatenv, formatName().toLatin1() );
OSyncError *error = 0;
OSyncChange *change = osync_change_new( &error );
if ( !change ) {
warning( error );
return;
}
osync_change_set_uid( change, QString::number( item.id() ).toLatin1() );
error = 0;
OSyncData *odata = osync_data_new( item.payloadData().data(), item.payloadData().size(), format, &error );
if ( !odata ) {
osync_change_unref( change );
warning( error );
return;
}
osync_data_set_objtype( odata, osync_objtype_sink_get_name( sink() ) );
osync_change_set_data( change, odata );
kDebug() << item.id() << "DATA:" << osync_data_get_printable( odata ) << "\n" << "ORIG:" << item.payloadData().data();
osync_data_unref( odata );
osync_change_set_hash( change, QString::number( item.revision() ).toLatin1() );
OSyncChangeType changeType = osync_hashtable_get_changetype( m_hashtable, change );
osync_change_set_changetype( change, changeType );
osync_hashtable_update_change( m_hashtable, change );
/*
kDebug() << "changeid:" << osync_change_get_uid( change )
<< "itemid:" << item.id()
<< "revision:" << item.revision()
<< "changetype:" << changeType
<< "hash:" << osync_hashtable_get_hash( m_hashtable, osync_change_get_uid( change ) )
<< "objtype:" << osync_change_get_objtype( change )
<< "objform:" << osync_objformat_get_name( osync_change_get_objformat( change ) )
<< "sinkname:" << osync_objtype_sink_get_name( sink() );
*/
if ( changeType != OSYNC_CHANGE_TYPE_UNMODIFIED )
osync_context_report_change( context(), change );
// perhaps this should be only called when an error has happened ie. never after _context_report_change()?
//osync_change_unref( change ); // if this gets called, we get broken pipes. any ideas?
}
void DataSink::slotGetChangesFinished( KJob * )
{
OSyncFormatEnv *formatenv = osync_plugin_info_get_format_env( pluginInfo() );
OSyncObjFormat *format = osync_format_env_find_objformat( formatenv, formatName().toLatin1() );
// after the items have been processed, see what's been deleted and send them to opensync
OSyncError *error = 0;
OSyncList *u, *uids = osync_hashtable_get_deleted( m_hashtable );
for ( u = uids; u; u = u->next ) {
QString uid( (char *)u->data );
kDebug() << "going to delete with uid:" << uid;
OSyncChange *change = osync_change_new( &error );
if ( !change ) {
warning( error );
continue;
}
osync_change_set_changetype( change, OSYNC_CHANGE_TYPE_DELETED );
osync_change_set_uid( change, uid.toLatin1() );
error = 0;
OSyncData *data = osync_data_new( 0, 0, format, &error );
if ( !data ) {
osync_change_unref( change );
warning( error );
continue;
}
osync_data_set_objtype( data, osync_objtype_sink_get_name( sink() ) );
osync_change_set_data( change, data );
osync_hashtable_update_change( m_hashtable, change );
osync_change_unref( change );
}
osync_list_free( uids );
kDebug() << "got all changes..";
success();
}
void DataSink::commit(OSyncChange *change)
{
kDebug() << "change uid:" << osync_change_get_uid( change );
kDebug() << "objtype:" << osync_change_get_objtype( change );
kDebug() << "objform:" << osync_objformat_get_name( osync_change_get_objformat( change ) );
switch( osync_change_get_changetype( change ) ) {
case OSYNC_CHANGE_TYPE_ADDED: {
const Item item = createItem( change );
osync_change_set_uid( change, QString::number( item.id() ).toLatin1() );
osync_change_set_hash( change, QString::number( item.revision() ).toLatin1() );
kDebug() << "ADDED:" << osync_change_get_uid( change );
break; }
case OSYNC_CHANGE_TYPE_MODIFIED: {
const Item item = modifyItem( change );
osync_change_set_uid( change, QString::number( item.id() ).toLatin1() );
osync_change_set_hash( change, QString::number( item.revision() ).toLatin1() );
kDebug() << "MODIFIED:" << osync_change_get_uid( change );
break; }
case OSYNC_CHANGE_TYPE_DELETED: {
deleteItem( change );
kDebug() << "DELETED:" << osync_change_get_uid( change );
break; }
case OSYNC_CHANGE_TYPE_UNMODIFIED: {
kDebug() << "UNMODIFIED";
// should we do something here?
break; }
default:
kDebug() << "got invalid changetype?";
}
osync_hashtable_update_change( m_hashtable, change );
success();
}
const Item DataSink::createItem( OSyncChange *change )
{
Collection col = collection();
if ( !col.isValid() ) // error handling
return Item();
kDebug() << "cuid:" << osync_change_get_uid( change );
ItemCreateJob *job = new Akonadi::ItemCreateJob( createAkonadiItem( change ), col );
if ( !job->exec() )
kDebug() << "creating an item failed";
return job->item(); // handle !job->exec in return too..
}
const Item DataSink::modifyItem( OSyncChange *change )
{
char *plain = 0;
osync_data_get_data( osync_change_get_data( change ), &plain, /*size*/0 );
QString str = QString::fromUtf8( plain );
QString id = QString( osync_change_get_uid( change ) );
Item item = fetchItem( id );
if ( !item.isValid() ) // TODO proper error handling
return Item();
//event.setMimeType( "application/x-vnd.akonadi.calendar.event" );
//Item newItem = createAkonadiItem( change );
setPayload( &item, str );
ItemModifyJob *modifyJob = new Akonadi::ItemModifyJob( item );
if ( modifyJob->exec() ) {
kDebug() << "modification completed";
return modifyJob->item();
} else
kDebug() << "unable to modify";
return Item();
}
void DataSink::deleteItem( OSyncChange *change )
{
QString id = QString( osync_change_get_uid( change ) );
Item item = fetchItem( id );
if ( !item.isValid() ) // TODO proper error handling
return;
kDebug() << "delete with id: " << item.id();
/*ItemDeleteJob *job = new ItemDeleteJob( item );
if ( job->exec() ) {
kDebug() << "item deleted";
}
else
kDebug() << "unable to delete item";*/
}
bool DataSink::setPayload( Item *item, const QString &str )
{
switch( m_type ) {
case Calendar: {
KCal::ICalFormat format;
KCal::Incidence *calEntry = format.fromString( str );
item->setMimeType( "application/x-vnd.akonadi.calendar.event" );
item->setPayload<IncidencePtr>( IncidencePtr( calEntry->clone() ) );
break;
}
case Contacts: {
KABC::VCardConverter converter;
KABC::Addressee vcard = converter.parseVCard( str.toUtf8() );
kDebug() << vcard.uid() << vcard.name();
item->setMimeType( Addressee::mimeType() );
item->setPayload<KABC::Addressee>( vcard );
break;
}
case Notes: {
kDebug() << "notes";
break;
}
default:
// should not happen
return false;
}
return true;
}
const Item DataSink::createAkonadiItem( OSyncChange *change )
{
char *plain = 0;
osync_data_get_data( osync_change_get_data( change ), &plain, /*size*/0 );
QString str = QString::fromUtf8( plain );
Akonadi::Item item;
setPayload( &item, str );
return item;
}
const QString DataSink::formatName()
{
QString formatName;
switch( m_type ) {
case Calendar:
formatName = "vevent20";
break;
case Contacts:
formatName = "vcard10";
break;
case Notes:
formatName = "vnote10";
break;
default:
kDebug() << "invalid datasink type";
return QString();
}
return formatName;
}
const Item DataSink::fetchItem( const QString& id )
{
ItemFetchJob *fetchJob = new ItemFetchJob( Item( id ) );
fetchJob->fetchScope().fetchFullPayload();
if ( fetchJob->exec() ) {
foreach ( const Item &item, fetchJob->items() ) {
if ( QString::number( item.id() ) == id ) {
kDebug() << "got item";
return item;
}
}
}
// no such item found?
return Item();
}
void DataSink::syncDone()
{
kDebug();
OSyncError *error = 0;
osync_hashtable_save( m_hashtable, &error );
//TODO check for errors
success();
}