mirror of
https://bitbucket.org/smil3y/kde-playground.git
synced 2025-02-23 18:32:51 +00:00
433 lines
12 KiB
C++
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();
|
|
}
|
|
|