Applied Nicolas Vivien's opensync-plugin-0.4x patch
[barry/progweb.git] / opensync-plugin-0.4x / src / barry_sync.cc
blob24d9ecefb7272911c6b710a516a327d0040f06f7
1 //
2 // \file barry_sync.cc
3 // Opensync module for the USB Blackberry handheld
4 //
6 /*
7 Copyright (C) 2006-2009, Net Direct Inc. (http://www.netdirect.ca/)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
22 #include <opensync/opensync.h>
23 #include <opensync/opensync-data.h>
24 #include <opensync/opensync-format.h>
25 #include <opensync/opensync-plugin.h>
26 #include <opensync/opensync-context.h>
27 #include <opensync/opensync-helper.h>
28 #include <opensync/opensync-version.h>
30 #include <barry/barry.h>
31 #include <barry/dll.h>
32 #include "barry_sync.h"
33 #include "environment.h"
34 #include "vevent.h"
35 #include "vcard.h"
36 #include "trace.h"
37 #include <string>
38 #include <glib.h>
39 #include <stdint.h>
40 #include <string.h>
42 // All functions that are callable from outside must look like C
43 extern "C" {
44 static void *initialize(OSyncPlugin *plugin, OSyncPluginInfo *info, OSyncError **error);
45 static osync_bool discover(void *userdata, OSyncPluginInfo *info, OSyncError **error);
46 static void connect(void *userdata, OSyncPluginInfo *info, OSyncContext *ctx);
47 static void get_changes(void *userdata, OSyncPluginInfo *info, OSyncContext *ctx);
48 static void commit_change(void *userdata, OSyncPluginInfo *info, OSyncContext *ctx, OSyncChange *change);
49 static void sync_done(void *userdata, OSyncPluginInfo *info, OSyncContext *ctx);
50 static void disconnect(void *userdata, OSyncPluginInfo *info, OSyncContext *ctx);
51 static void finalize(void *userdata);
52 BXEXPORT osync_bool get_sync_info(OSyncPluginEnv *env, OSyncError **error);
56 //////////////////////////////////////////////////////////////////////////////
58 // Support functions and classes
62 void GetChanges(OSyncPluginInfo *info, OSyncContext *ctx, BarryEnvironment *env,
63 DatabaseSyncState *pSync,
64 const char *DBDBName,
65 const char *ObjTypeName, const char *FormatName,
66 GetData_t getdata)
68 Trace trace("GetChanges");
70 OSyncError *error = NULL;
72 // shortcut references
73 using namespace Barry;
74 using Barry::RecordStateTable;
75 Mode::Desktop &desktop = *env->m_pDesktop;
77 // find the matching cache, state table, and id map for this change
78 DatabaseSyncState::cache_type &cache = pSync->m_Cache;
79 idmap &map = pSync->m_IdMap;
81 // check if slow sync has been requested, and if so, empty the
82 // cache and id map and start fresh
83 if (osync_objtype_sink_get_slowsync(pSync->sink)) {
84 trace.log("GetChanges: slow sync request detected, clearing cache and id map");
85 cache.clear();
86 map.clear();
89 // fetch state table
90 unsigned int dbId = desktop.GetDBID(DBDBName);
91 RecordStateTable &table = pSync->m_Table;
92 desktop.GetRecordStateTable(dbId, table);
94 OSyncFormatEnv *formatenv = osync_plugin_info_get_format_env(info);
96 // OSyncObjTypeSink *sink = osync_plugin_info_get_sink(info);
99 // cycle through the state table...
100 // - if not in cache, it is added.
101 // - if in cache, check Blackberry's dirty flag
102 RecordStateTable::StateMapType::const_iterator i = table.StateMap.begin();
103 for( ; i != table.StateMap.end(); ++i ) {
105 OSyncChange *change = 0;
106 const RecordStateTable::IndexType &index = i->first;
107 const RecordStateTable::State &state = i->second;
109 // search the idmap for the UID
110 std::string uid = pSync->Map2Uid(state.RecordId);
112 // search the cache
113 DatabaseSyncState::cache_type::const_iterator c = cache.find(state.RecordId);
114 if( c == cache.end() ) {
115 // not in cache, this is a new item
116 trace.log("found an ADDED change");
117 change = osync_change_new(&error);
118 osync_change_set_changetype(change, OSYNC_CHANGE_TYPE_ADDED);
120 else {
121 // in the cache... dirty?
122 if( state.Dirty ) {
123 // modified
124 trace.log("found a MODIFIED change");
125 change = osync_change_new(&error);
126 osync_change_set_changetype(change, OSYNC_CHANGE_TYPE_MODIFIED);
128 else {
129 trace.log("no change detected");
133 // finish filling out the change object
134 if( change ) {
135 // osync_change_set_member(change, env->member);
136 // osync_change_set_objformat_string(change, FormatName);
138 osync_change_set_uid(change, uid.c_str());
139 trace.logf("change record ID: %s", uid.c_str());
141 OSyncObjFormat *format = osync_format_env_find_objformat(formatenv, FormatName);
143 // Now you can set the data for the object
144 // Set the last argument to FALSE if the real data
145 // should be queried later in a "get_data" function
146 char *data = (*getdata)(env, dbId, index);
147 OSyncData *odata = osync_data_new(data, strlen(data), format, &error);
149 // osync_data_set_objtype(odata, osync_objtype_sink_get_name(sink));
151 osync_change_set_data(change, odata);
152 osync_data_unref(odata);
154 // just report the change via
155 osync_context_report_change(ctx, change);
157 osync_change_unref(change);
159 // map our IDs for later
160 map.Map(uid, state.RecordId);
164 // now cycle through the cache... any objects in the cache
165 // but not found in the state table means that they have been
166 // deleted in the device
167 DatabaseSyncState::cache_type::const_iterator c = cache.begin();
168 for( ; c != cache.end(); ++c ) {
169 uint32_t recordId = c->first;
171 // search the idmap for the UID
172 std::string uid = pSync->Map2Uid(recordId);
174 // search the state table
175 i = table.StateMap.begin();
176 for( ; i != table.StateMap.end(); ++i ) {
178 if( i->second.RecordId == recordId )
179 break; // found
182 // check if not found...
183 if( i == table.StateMap.end() ) {
184 // register a DELETE, no data
185 trace.log("found DELETE change");
187 OSyncChange *change = osync_change_new(&error);
189 osync_change_set_changetype(change, OSYNC_CHANGE_TYPE_DELETED);
191 // osync_change_set_member(change, env->member);
192 // osync_change_set_objformat_string(change, FormatName);
194 osync_change_set_uid(change, uid.c_str());
195 trace.log(uid.c_str());
197 OSyncObjFormat *format = osync_format_env_find_objformat(formatenv, FormatName);
199 OSyncData *odata = osync_data_new(NULL, 0, format, &error);
201 // osync_data_set_objtype(odata, osync_objtype_sink_get_name(sink));
203 osync_change_set_data(change, odata);
204 osync_data_unref(odata);
206 // report the change
207 osync_context_report_change(ctx, change);
209 osync_change_unref(change);
213 // finally, cycle through the state map again, and overwrite the
214 // cache with the current state table. Memory only... if successful,
215 // it will be written back to disk later on.
217 // start fresh
218 cache.clear();
220 for( i = table.StateMap.begin(); i != table.StateMap.end(); ++i ) {
221 const RecordStateTable::State &state = i->second;
222 cache[state.RecordId] = false;
226 CommitData_t GetCommitFunction(OSyncChange *change)
228 const char *name = osync_change_get_objtype(change);
230 if( strcmp(name, "event") == 0 ) {
231 return &VEventConverter::CommitRecordData;
233 else if( strcmp(name, "contact") == 0 ) {
234 return &VCardConverter::CommitRecordData;
236 else {
237 return 0;
241 bool FinishSync(OSyncContext *ctx, BarryEnvironment *env, DatabaseSyncState *pSync)
243 Trace trace("FinishSync()");
245 if( !pSync->m_Sync ) {
246 // this mode is disabled in config, skip
247 return true;
250 Barry::Mode::Desktop &desktop = *env->m_pDesktop;
252 // get the state table again, so we can update
253 // the cache properly
254 desktop.GetRecordStateTable(pSync->m_dbId, pSync->m_Table);
256 // update the cache
257 if( !pSync->SaveCache() ) {
258 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR,
259 "Error saving calendar cache");
260 return false;
263 // save the id map
264 pSync->CleanupMap();
265 if( !pSync->SaveMap() ) {
266 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR,
267 "Error saving calendar id map");
268 return false;
271 // clear all dirty flags in device
272 env->ClearDirtyFlags(pSync->m_Table, pSync->m_dbName);
273 return true;
277 static bool barry_contact_initialize(DatabaseSyncState *env, OSyncPluginInfo *info, OSyncError **error)
279 OSyncObjTypeSink *sink = osync_plugin_info_find_objtype(info, "contact");
280 if (!sink)
281 return false;
282 osync_bool sinkEnabled = osync_objtype_sink_is_enabled(sink);
283 if (!sinkEnabled)
284 return false;
286 OSyncObjTypeSinkFunctions functions;
287 memset(&functions, 0, sizeof(functions));
289 functions.connect = connect;
290 functions.disconnect = disconnect;
291 functions.get_changes = get_changes;
292 functions.commit = commit_change;
293 functions.sync_done = sync_done;
295 OSyncPluginConfig *config = osync_plugin_info_get_config(info);
296 OSyncPluginResource *resource = osync_plugin_config_find_active_resource(config, "contact");
298 OSyncList *objformatsinks = osync_plugin_resource_get_objformat_sinks(resource);
300 bool hasObjFormat = false;
302 OSyncList *r;
303 for(r = objformatsinks;r;r = r->next) {
304 OSyncObjFormatSink *objformatsink = (OSyncObjFormatSink *) r->data;
306 if(!strcmp("vcard30", osync_objformat_sink_get_objformat(objformatsink))) {
307 hasObjFormat = true;
308 break;
312 if (!hasObjFormat) {
313 return false;
316 // OSyncFormatEnv *formatenv = osync_plugin_info_get_format_env(info);
318 // env->format = osync_format_env_find_objformat(formatenv, "vcard30");
319 env->sink = sink;
321 osync_objtype_sink_set_functions(sink, functions, NULL);
323 return true;
327 static bool barry_calendar_initialize(DatabaseSyncState *env, OSyncPluginInfo *info, OSyncError **error)
329 OSyncObjTypeSink *sink = osync_plugin_info_find_objtype(info, "event");
330 if (!sink)
331 return false;
332 osync_bool sinkEnabled = osync_objtype_sink_is_enabled(sink);
333 if (!sinkEnabled)
334 return false;
336 OSyncObjTypeSinkFunctions functions;
337 memset(&functions, 0, sizeof(functions));
339 functions.connect = connect;
340 functions.disconnect = disconnect;
341 functions.get_changes = get_changes;
342 functions.commit = commit_change;
343 functions.sync_done = sync_done;
345 OSyncPluginConfig *config = osync_plugin_info_get_config(info);
346 OSyncPluginResource *resource = osync_plugin_config_find_active_resource(config, "event");
348 OSyncList *objformatsinks = osync_plugin_resource_get_objformat_sinks(resource);
350 bool hasObjFormat = false;
352 OSyncList *r;
353 for(r = objformatsinks;r;r = r->next) {
354 OSyncObjFormatSink *objformatsink = (OSyncObjFormatSink *) r->data;
356 if(!strcmp("vevent20", osync_objformat_sink_get_objformat(objformatsink))) {
357 hasObjFormat = true;
358 break;
362 if (!hasObjFormat) {
363 return false;
366 // OSyncFormatEnv *formatenv = osync_plugin_info_get_format_env(info);
368 // env->format = osync_format_env_find_objformat(formatenv, "vevent20");
369 env->sink = sink;
371 osync_objtype_sink_set_functions(sink, functions, NULL);
373 return true;
379 //////////////////////////////////////////////////////////////////////////////
381 // OpenSync API
384 static void *initialize(OSyncPlugin *plugin, OSyncPluginInfo *info, OSyncError **error)
386 Trace trace("initialize");
388 BarryEnvironment *env = 0;
390 try {
391 env = new BarryEnvironment(info);
394 * Read plugin config
396 OSyncPluginConfig *config = osync_plugin_info_get_config(info);
398 if (!config) {
399 osync_error_set(error, OSYNC_ERROR_GENERIC, "Unable to get config.");
401 delete env;
403 return NULL;
407 * Process plugin specific advanced options
409 OSyncList *optslist = osync_plugin_config_get_advancedoptions(config);
410 for (; optslist; optslist = optslist->next) {
411 OSyncPluginAdvancedOption *option = (OSyncPluginAdvancedOption *) optslist->data;
413 const char *val = osync_plugin_advancedoption_get_value(option);
414 const char *name = osync_plugin_advancedoption_get_name(option);
416 if (!strcmp(name, "PinCode")) {
417 env->m_pin = atoi(val);
419 else if (!strcmp(name, "Debug")) {
420 env->m_DebugMode = (!strcmp(val, "1")) ? true : false;
424 OSyncPluginAuthentication *optauth = osync_plugin_config_get_authentication(config);
426 if (osync_plugin_authentication_option_is_supported(optauth, OSYNC_PLUGIN_AUTHENTICATION_PASSWORD)) {
427 const char *val = osync_plugin_authentication_get_password(optauth);
429 env->m_password = val;
433 // FIXME - near the end of release, do a run with
434 // this set to true, and look for USB protocol
435 // inefficiencies.
436 Barry::Init(env->m_DebugMode);
440 * Process Ressource options
442 if (!barry_calendar_initialize(&env->m_CalendarSync, info, error)) {
443 env->m_CalendarSync.LoadCache();
444 env->m_CalendarSync.LoadMap();
446 else
447 env->m_CalendarSync.m_Sync = false;
449 if (!barry_contact_initialize(&env->m_ContactsSync, info, error)) {
450 env->m_ContactsSync.LoadCache();
451 env->m_ContactsSync.LoadMap();
453 else
454 env->m_ContactsSync.m_Sync = false;
457 return (void *) env;
459 catch( std::exception &e ) {
460 delete env;
462 return NULL;
467 static osync_bool discover(void *userdata, OSyncPluginInfo *info, OSyncError **error)
469 int i, numobjs = osync_plugin_info_num_objtypes(info);
471 for (i = 0; i < numobjs; i++) {
472 OSyncObjTypeSink *sink = osync_plugin_info_nth_objtype(info, i);
474 osync_objtype_sink_set_available(sink, true);
477 OSyncVersion *version = osync_version_new(error);
478 osync_version_set_plugin(version, "Barry");
479 osync_version_set_modelversion(version, "1");
480 //osync_version_set_firmwareversion(version, "firmwareversion");
481 //osync_version_set_softwareversion(version, "softwareversion");
482 //osync_version_set_hardwareversion(version, "hardwareversion");
483 osync_plugin_info_set_version(info, version);
484 osync_version_unref(version);
486 return true;
490 static void connect(void *userdata, OSyncPluginInfo *info, OSyncContext *ctx)
492 Trace trace("connect");
494 try {
496 // Each time you get passed a context (which is used to track
497 // calls to your plugin) you can get the data your returned in
498 // initialize via this call:
499 BarryEnvironment *env = (BarryEnvironment *) userdata;
501 // Probe for available devices
502 Barry::Probe probe;
503 int nIndex = probe.FindActive(env->m_pin);
504 if( nIndex == -1 ) {
505 osync_context_report_error(ctx, OSYNC_ERROR_NO_CONNECTION, "Unable to find PIN %lx", env->m_pin);
506 return;
508 env->m_ProbeResult = probe.Get(nIndex);
510 env->Connect(probe.Get(nIndex));
512 // Success!
513 osync_context_report_success(ctx);
516 // Don't let exceptions escape to the C modules
517 catch( std::bad_alloc &ba ) {
518 osync_context_report_error(ctx, OSYNC_ERROR_INITIALIZATION,
519 "Unable to allocate memory for controller: %s", ba.what());
521 catch( std::exception &e ) {
522 osync_context_report_error(ctx, OSYNC_ERROR_INITIALIZATION,
523 "%s", e.what());
528 static void get_changes(void *userdata, OSyncPluginInfo *info, OSyncContext *ctx)
530 Trace trace("get_changeinfo");
532 try {
534 BarryEnvironment *env = (BarryEnvironment *) userdata;
536 if( env->m_CalendarSync.m_Sync ) {
537 GetChanges(info, ctx, env, &env->m_CalendarSync,
538 "Calendar", "event", "vevent20",
539 &VEventConverter::GetRecordData);
542 if( env->m_ContactsSync.m_Sync ) {
543 GetChanges(info, ctx, env, &env->m_ContactsSync,
544 "Address Book", "contact", "vcard30",
545 &VCardConverter::GetRecordData);
548 // Success!
549 osync_context_report_success(ctx);
551 // don't let exceptions escape to the C modules
552 catch( std::exception &e ) {
553 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
557 static void commit_change(void *userdata, OSyncPluginInfo *info, OSyncContext *ctx, OSyncChange *change)
559 Trace trace("commit_change");
561 // We can rely on a valid record state table, since get_changeinfo()
562 // will be called first, and will fill the table.
564 try {
566 BarryEnvironment *env = (BarryEnvironment *) userdata;
568 // find the needed commit function, based on objtype of the change
569 CommitData_t CommitData = GetCommitFunction(change);
570 if( !CommitData ) {
571 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC,
572 "unable to get commit function pointer");
573 return;
576 // find the matching cache, state table, and id map for this change
577 DatabaseSyncState *pSync = env->GetSyncObject(change);
578 if( !pSync ) {
579 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC,
580 "unable to get sync object that matches change type");
581 return;
584 // is syncing turned on for this type?
585 if( !pSync->m_Sync ) {
586 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC,
587 "This object type is disabled in the barry-sync config");
588 return;
591 // make references instead of pointers
592 DatabaseSyncState::cache_type &cache = pSync->m_Cache;
593 Barry::RecordStateTable &table = pSync->m_Table;
594 idmap &map = pSync->m_IdMap;
595 Barry::Mode::Desktop &desktop = *env->m_pDesktop;
596 unsigned int dbId = pSync->m_dbId;
599 // extract RecordId from change's UID,
600 // and update the ID map if necessary
601 const char *uid = osync_change_get_uid(change);
602 trace.logf("uid from change: %s", uid);
603 if( strlen(uid) == 0 ) {
604 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC,
605 "uid from change object is blank!");
607 unsigned long RecordId = pSync->GetMappedRecordId(uid);
609 // search for the RecordId in the state table, to find the
610 // index... we only need the index if we are deleting or
611 // modifying
612 Barry::RecordStateTable::IndexType StateIndex;
613 if( osync_change_get_changetype(change) != OSYNC_CHANGE_TYPE_ADDED ) {
614 if( !table.GetIndex(RecordId, &StateIndex) ) {
615 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC,
616 "unable to get state table index for RecordId: %lu",
617 RecordId);
618 return;
622 std::string errmsg;
623 bool status;
625 OSyncData *odata = NULL;
626 char *plain = NULL;
628 switch( osync_change_get_changetype(change) )
630 case OSYNC_CHANGE_TYPE_DELETED:
631 desktop.DeleteRecord(dbId, StateIndex);
632 cache.erase(RecordId);
633 map.UnmapUid(uid);
634 break;
636 case OSYNC_CHANGE_TYPE_ADDED:
637 odata = osync_change_get_data(change);
638 osync_data_get_data(odata, &plain, NULL);
639 status = (*CommitData)(env, dbId, StateIndex, RecordId,
640 plain, true, errmsg);
641 if( !status ) {
642 trace.logf("CommitData() for ADDED state returned false: %s", errmsg.c_str());
643 osync_context_report_error(ctx, OSYNC_ERROR_PARAMETER, "%s", errmsg.c_str());
644 map.UnmapUid(uid);
645 return;
647 cache[RecordId] = false;
648 break;
650 case OSYNC_CHANGE_TYPE_MODIFIED:
651 odata = osync_change_get_data(change);
652 osync_data_get_data(odata, &plain, NULL);
653 status = (*CommitData)(env, dbId, StateIndex, RecordId,
654 plain, false, errmsg);
655 if( !status ) {
656 trace.logf("CommitData() for MODIFIED state returned false: %s", errmsg.c_str());
657 osync_context_report_error(ctx, OSYNC_ERROR_PARAMETER, "%s", errmsg.c_str());
658 map.UnmapUid(uid);
659 return;
661 break;
663 default:
664 trace.log("Unknown change type");
665 break;
668 // Answer the call
669 osync_context_report_success(ctx);
670 return;
674 catch( std::exception &e ) {
675 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
677 // we don't worry about unmapping ids here, as there
678 // is still a possibility that the record was added...
679 // plus, the map might not get written out to disk anyway
680 // in a plugin error state
682 return;
686 static void sync_done(void *userdata, OSyncPluginInfo *info, OSyncContext *ctx)
689 // This function will only be called if the sync was successfull
692 Trace trace("sync_done");
694 try {
696 BarryEnvironment *env = (BarryEnvironment *) userdata;
698 // we reconnect to the device here, since dirty flags
699 // for records we've just touched do not show up until
700 // a disconnect... as far as I can tell.
701 env->Reconnect();
703 // do cleanup for each database
704 if( FinishSync(ctx, env, &env->m_CalendarSync) &&
705 FinishSync(ctx, env, &env->m_ContactsSync) )
707 // Success
708 osync_context_report_success(ctx);
712 catch( std::exception &e ) {
713 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
717 static void disconnect(void *userdata, OSyncPluginInfo *info, OSyncContext *ctx)
719 Trace trace("disconnect");
721 // Disconnect the controller, which closes our connection
722 BarryEnvironment *env = (BarryEnvironment *) userdata;
723 env->Disconnect();
725 // Done!
726 osync_context_report_success(ctx);
729 static void finalize(void *userdata)
731 Trace trace("finalize");
733 BarryEnvironment *env = (BarryEnvironment *) userdata;
735 delete env;
739 osync_bool get_sync_info(OSyncPluginEnv *env, OSyncError **error)
741 // Create a new OpenSync plugin
742 OSyncPlugin *plugin = osync_plugin_new(error);
744 if (!plugin) {
745 osync_error_unref(error);
747 return false;
750 // Describe our plugin
751 osync_plugin_set_name(plugin, "barry-sync");
752 osync_plugin_set_longname(plugin, "Barry OpenSync plugin v0.15 for the Blackberry handheld");
753 osync_plugin_set_description(plugin, "Plugin to synchronize calendar and contact entries on USB Blackberry handhelds");
755 // Set the callback functions
756 osync_plugin_set_initialize(plugin, initialize);
757 osync_plugin_set_finalize(plugin, finalize);
758 osync_plugin_set_discover(plugin, discover);
760 osync_plugin_env_register_plugin(env, plugin);
761 osync_plugin_unref(plugin);
763 return true;
767 int get_version(void)
769 return 1;