Warning: That file was not part of the compilation database. It may have many parsing errors.

1/*
2 Copyright (c) 2008 Volker Krause <vkrause@kde.org>
3
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
8
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA.
18*/
19
20#include "datasink.h"
21
22// calendar includes
23#include <kcal/incidence.h>
24#include <kcal/icalformat.h>
25
26// contact includes
27#include <kabc/vcardconverter.h>
28#include <kabc/addressee.h>
29
30// notes includes
31// TODO
32
33#include <KDebug>
34#include <KLocale>
35#include <KUrl>
36
37using namespace Akonadi;
38
39typedef boost::shared_ptr<KCal::Incidence> IncidencePtr;
40
41
42DataSink::DataSink( int type ) :
43 SinkBase( GetChanges | Commit | SyncDone )
44{
45 m_type = type;
46}
47
48DataSink::~DataSink() {
49 if ( m_hashtable )
50 osync_hashtable_unref( m_hashtable );
51}
52
53bool DataSink::initialize(OSyncPlugin * plugin, OSyncPluginInfo * info, OSyncObjTypeSink *sink, OSyncError ** error)
54{
55 Q_UNUSED( plugin );
56 Q_UNUSED( info );
57 Q_UNUSED( error );
58
59 bool enabled = osync_objtype_sink_is_enabled( sink );
60 if ( !enabled ) {
61 kDebug() << "sink is not enabled..";
62 return false;
63 }
64
65 QString configdir( osync_plugin_info_get_configdir( info ) );
66 QString hashfile = QString( "%1/%2-hash.db" ).arg( configdir, osync_objtype_sink_get_name( sink ) );
67
68 m_hashtable = osync_hashtable_new( hashfile.toUtf8(), osync_objtype_sink_get_name( sink ), error );
69
70 if ( !osync_hashtable_load( m_hashtable, error ) ) {
71 osync_trace( TRACE_EXIT_ERROR, "%s: %s", __PRETTY_FUNCTION__, osync_error_print( error ) );
72 return false;
73 }
74
75 wrapSink( sink );
76
77 return true;
78}
79
80Akonadi::Collection DataSink::collection() const
81{
82 OSyncPluginConfig *config = osync_plugin_info_get_config( pluginInfo() );
83 Q_ASSERT( config );
84
85 const char *objtype = osync_objtype_sink_get_name( sink() );
86
87 OSyncPluginResource *res = osync_plugin_config_find_active_resource( config, objtype );
88
89 if ( !res ) {
90 error( OSYNC_ERROR_MISCONFIGURATION, i18n( "No active resource for type \"%1\" found", objtype ) );
91 return Collection();
92 }
93
94 const KUrl url = KUrl( osync_plugin_resource_get_url( res ) );
95 if ( url.isEmpty() ) {
96 error( OSYNC_ERROR_MISCONFIGURATION, i18n( "Url for object type \"%1\" is not configured.", objtype ) );
97 return Collection();
98 }
99
100 return Collection::fromUrl( url );
101}
102
103void DataSink::getChanges()
104{
105 // ### broken in OpenSync, I don't get valid configuration here!
106#if 1
107 Collection col = collection();
108 if ( !col.isValid() )
109 return;
110#else
111 Collection col( 409 );
112#endif
113
114 OSyncError *oerror = 0;
115
116 if ( osync_objtype_sink_get_slowsync( sink() ) ) {
117 kDebug() << "we're in the middle of slow-syncing...";
118 osync_trace( TRACE_INTERNAL, "Got slow-sync, resetting hashtable" );
119 if ( !osync_hashtable_slowsync( m_hashtable, &oerror ) ) {
120 warning( oerror );
121 osync_trace( TRACE_EXIT_ERROR, "%s: %s", __PRETTY_FUNCTION__, osync_error_print( &oerror ) );
122 return;
123 }
124 }
125
126 ItemFetchJob *job = new ItemFetchJob( col );
127 job->fetchScope().fetchFullPayload();
128
129 QObject::connect( job, SIGNAL(itemsReceived(Akonadi::Item::List)), this, SLOT(slotItemsReceived(Akonadi::Item::List)) );
130 QObject::connect( job, SIGNAL(result(KJob*)), this, SLOT(slotGetChangesFinished(KJob*)) );
131
132 // FIXME give me a real eventloop please!
133 if ( !job->exec() ) {
134 error( OSYNC_ERROR_IO_ERROR, job->errorText() );
135 return;
136 }
137}
138
139void DataSink::slotItemsReceived( const Item::List &items )
140{
141 kDebug() << "retrieved" << items.count() << "items";
142 Q_FOREACH ( const Item& item, items ) {
143 //kDebug() << item.payloadData();
144 reportChange( item );
145 }
146}
147
148void DataSink::reportChange( const Item& item )
149{
150 OSyncFormatEnv *formatenv = osync_plugin_info_get_format_env( pluginInfo() );
151 OSyncObjFormat *format = osync_format_env_find_objformat( formatenv, formatName().toLatin1() );
152
153 OSyncError *error = 0;
154
155 OSyncChange *change = osync_change_new( &error );
156 if ( !change ) {
157 warning( error );
158 return;
159 }
160
161 osync_change_set_uid( change, QString::number( item.id() ).toLatin1() );
162
163 error = 0;
164
165 OSyncData *odata = osync_data_new( item.payloadData().data(), item.payloadData().size(), format, &error );
166 if ( !odata ) {
167 osync_change_unref( change );
168 warning( error );
169 return;
170 }
171
172 osync_data_set_objtype( odata, osync_objtype_sink_get_name( sink() ) );
173
174 osync_change_set_data( change, odata );
175
176 kDebug() << item.id() << "DATA:" << osync_data_get_printable( odata ) << "\n" << "ORIG:" << item.payloadData().data();
177
178 osync_data_unref( odata );
179
180 osync_change_set_hash( change, QString::number( item.revision() ).toLatin1() );
181
182 OSyncChangeType changeType = osync_hashtable_get_changetype( m_hashtable, change );
183 osync_change_set_changetype( change, changeType );
184
185 osync_hashtable_update_change( m_hashtable, change );
186
187/*
188kDebug() << "changeid:" << osync_change_get_uid( change )
189 << "itemid:" << item.id()
190 << "revision:" << item.revision()
191 << "changetype:" << changeType
192 << "hash:" << osync_hashtable_get_hash( m_hashtable, osync_change_get_uid( change ) )
193 << "objtype:" << osync_change_get_objtype( change )
194 << "objform:" << osync_objformat_get_name( osync_change_get_objformat( change ) )
195 << "sinkname:" << osync_objtype_sink_get_name( sink() );
196*/
197 if ( changeType != OSYNC_CHANGE_TYPE_UNMODIFIED )
198 osync_context_report_change( context(), change );
199
200 // perhaps this should be only called when an error has happened ie. never after _context_report_change()?
201 //osync_change_unref( change ); // if this gets called, we get broken pipes. any ideas?
202}
203
204void DataSink::slotGetChangesFinished( KJob * )
205{
206 OSyncFormatEnv *formatenv = osync_plugin_info_get_format_env( pluginInfo() );
207 OSyncObjFormat *format = osync_format_env_find_objformat( formatenv, formatName().toLatin1() );
208
209 // after the items have been processed, see what's been deleted and send them to opensync
210 OSyncError *error = 0;
211 OSyncList *u, *uids = osync_hashtable_get_deleted( m_hashtable );
212
213 for ( u = uids; u; u = u->next ) {
214 QString uid( (char *)u->data );
215 kDebug() << "going to delete with uid:" << uid;
216
217 OSyncChange *change = osync_change_new( &error );
218 if ( !change ) {
219 warning( error );
220 continue;
221 }
222
223 osync_change_set_changetype( change, OSYNC_CHANGE_TYPE_DELETED );
224 osync_change_set_uid( change, uid.toLatin1() );
225
226 error = 0;
227 OSyncData *data = osync_data_new( 0, 0, format, &error );
228 if ( !data ) {
229 osync_change_unref( change );
230 warning( error );
231 continue;
232 }
233
234 osync_data_set_objtype( data, osync_objtype_sink_get_name( sink() ) );
235 osync_change_set_data( change, data );
236
237 osync_hashtable_update_change( m_hashtable, change );
238
239 osync_change_unref( change );
240 }
241 osync_list_free( uids );
242
243 kDebug() << "got all changes..";
244 success();
245}
246
247void DataSink::commit(OSyncChange *change)
248{
249 kDebug() << "change uid:" << osync_change_get_uid( change );
250 kDebug() << "objtype:" << osync_change_get_objtype( change );
251 kDebug() << "objform:" << osync_objformat_get_name( osync_change_get_objformat( change ) );
252
253 switch( osync_change_get_changetype( change ) ) {
254 case OSYNC_CHANGE_TYPE_ADDED: {
255 const Item item = createItem( change );
256 osync_change_set_uid( change, QString::number( item.id() ).toLatin1() );
257 osync_change_set_hash( change, QString::number( item.revision() ).toLatin1() );
258 kDebug() << "ADDED:" << osync_change_get_uid( change );
259 break; }
260
261 case OSYNC_CHANGE_TYPE_MODIFIED: {
262 const Item item = modifyItem( change );
263 osync_change_set_uid( change, QString::number( item.id() ).toLatin1() );
264 osync_change_set_hash( change, QString::number( item.revision() ).toLatin1() );
265 kDebug() << "MODIFIED:" << osync_change_get_uid( change );
266 break; }
267
268 case OSYNC_CHANGE_TYPE_DELETED: {
269 deleteItem( change );
270 kDebug() << "DELETED:" << osync_change_get_uid( change );
271 break; }
272
273 case OSYNC_CHANGE_TYPE_UNMODIFIED: {
274 kDebug() << "UNMODIFIED";
275 // should we do something here?
276 break; }
277
278 default:
279 kDebug() << "got invalid changetype?";
280 }
281
282 osync_hashtable_update_change( m_hashtable, change );
283 success();
284}
285
286const Item DataSink::createItem( OSyncChange *change )
287{
288 Collection col = collection();
289 if ( !col.isValid() ) // error handling
290 return Item();
291 kDebug() << "cuid:" << osync_change_get_uid( change );
292
293 ItemCreateJob *job = new Akonadi::ItemCreateJob( createAkonadiItem( change ), col );
294
295 if ( !job->exec() )
296 kDebug() << "creating an item failed";
297
298 return job->item(); // handle !job->exec in return too..
299}
300
301const Item DataSink::modifyItem( OSyncChange *change )
302{
303 char *plain = 0;
304 osync_data_get_data( osync_change_get_data( change ), &plain, /*size*/0 );
305 QString str = QString::fromUtf8( plain );
306
307 QString id = QString( osync_change_get_uid( change ) );
308 Item item = fetchItem( id );
309 if ( !item.isValid() ) // TODO proper error handling
310 return Item();
311
312 //event.setMimeType( "application/x-vnd.akonadi.calendar.event" );
313 //Item newItem = createAkonadiItem( change );
314 setPayload( &item, str );
315 ItemModifyJob *modifyJob = new Akonadi::ItemModifyJob( item );
316 if ( modifyJob->exec() ) {
317 kDebug() << "modification completed";
318 return modifyJob->item();
319 } else
320 kDebug() << "unable to modify";
321
322 return Item();
323}
324
325void DataSink::deleteItem( OSyncChange *change )
326{
327 QString id = QString( osync_change_get_uid( change ) );
328 Item item = fetchItem( id );
329 if ( !item.isValid() ) // TODO proper error handling
330 return;
331
332 kDebug() << "delete with id: " << item.id();
333 /*ItemDeleteJob *job = new ItemDeleteJob( item );
334
335 if ( job->exec() ) {
336 kDebug() << "item deleted";
337 }
338 else
339 kDebug() << "unable to delete item";*/
340}
341
342bool DataSink::setPayload( Item *item, const QString &str )
343{
344 switch( m_type ) {
345 case Calendar: {
346 KCal::ICalFormat format;
347 KCal::Incidence *calEntry = format.fromString( str );
348
349 item->setMimeType( "application/x-vnd.akonadi.calendar.event" );
350 item->setPayload<IncidencePtr>( IncidencePtr( calEntry->clone() ) );
351
352 break;
353 }
354 case Contacts: {
355 KABC::VCardConverter converter;
356 KABC::Addressee vcard = converter.parseVCard( str.toUtf8() );
357 kDebug() << vcard.uid() << vcard.name();
358
359 item->setMimeType( Addressee::mimeType() );
360 item->setPayload<KABC::Addressee>( vcard );
361 break;
362 }
363 case Notes: {
364 kDebug() << "notes";
365 break;
366 }
367 default:
368 // should not happen
369 return false;
370 }
371
372 return true;
373}
374
375const Item DataSink::createAkonadiItem( OSyncChange *change )
376{
377 char *plain = 0;
378 osync_data_get_data( osync_change_get_data( change ), &plain, /*size*/0 );
379 QString str = QString::fromUtf8( plain );
380 Akonadi::Item item;
381 setPayload( &item, str );
382 return item;
383}
384
385const QString DataSink::formatName()
386{
387 QString formatName;
388 switch( m_type ) {
389 case Calendar:
390 formatName = "vevent20";
391 break;
392 case Contacts:
393 formatName = "vcard10";
394 break;
395 case Notes:
396 formatName = "vnote10";
397 break;
398 default:
399 kDebug() << "invalid datasink type";
400 return QString();
401 }
402
403 return formatName;
404}
405
406const Item DataSink::fetchItem( const QString& id )
407{
408 ItemFetchJob *fetchJob = new ItemFetchJob( Item( id ) );
409 fetchJob->fetchScope().fetchFullPayload();
410
411 if ( fetchJob->exec() ) {
412 foreach ( const Item &item, fetchJob->items() ) {
413 if ( QString::number( item.id() ) == id ) {
414 kDebug() << "got item";
415 return item;
416 }
417 }
418 }
419
420 // no such item found?
421 return Item();
422}
423
424void DataSink::syncDone()
425{
426 kDebug();
427 OSyncError *error = 0;
428 osync_hashtable_save( m_hashtable, &error );
429
430 //TODO check for errors
431 success();
432}
433
434

Warning: That file was not part of the compilation database. It may have many parsing errors.