Bumped copyright dates to 2012
[barry/progweb.git] / opensync-plugin-0.4x / src / barry_sync.cc
blob03990fb4d51a3e0fb3c5ac61e1109e170cf8a031
1 //
2 // \file barry_sync.cc
3 // Opensync module for the USB Blackberry handheld
4 //
6 /*
7 Copyright (C) 2009, Nicolas VIVIEN (opensync plugin porting on opensync 0.4x ; Task & Memo support)
8 Copyright (C) 2006-2012, Net Direct Inc. (http://www.netdirect.ca/)
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 See the GNU General Public License in the COPYING file at the
20 root directory of this project for more details.
23 #include <opensync/opensync.h>
24 #include <opensync/opensync-data.h>
25 #include <opensync/opensync-format.h>
26 #include <opensync/opensync-plugin.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 "vtodo.h"
35 #include "vjournal.h"
36 #include "vevent.h"
37 #include "vcard.h"
38 #include "trace.h"
39 #include <string>
40 #include <glib.h>
41 #include <stdint.h>
42 #include <string.h>
43 #include <errno.h>
45 typedef Barry::vSmartPtr<OSyncList, OSyncList, &osync_list_free> AutoOSyncList;
47 // All functions that are callable from outside must look like C
48 extern "C" {
49 BXEXPORT int get_version(void);
50 static void *initialize(OSyncPlugin *plugin, OSyncPluginInfo *info, OSyncError **error);
51 static osync_bool discover(OSyncPluginInfo *info, void *userdata, OSyncError **error);
53 static void contact_get_changes(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, osync_bool slow_sync, void *userdata);
54 static void contact_sync_done(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata);
56 static void event_get_changes(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, osync_bool slow_sync, void *userdata);
57 static void event_sync_done(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata);
59 static void journal_get_changes(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, osync_bool slow_sync, void *userdata);
60 static void journal_sync_done(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata);
62 static void todo_get_changes(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, osync_bool slow_sync, void *userdata);
63 static void todo_sync_done(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata);
65 static void connect(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata);
66 static void disconnect(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata);
67 static void commit_change(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, OSyncChange *change, void *userdata);
68 static void finalize(void *userdata);
69 BXEXPORT osync_bool get_sync_info(OSyncPluginEnv *env, OSyncError **error);
74 //////////////////////////////////////////////////////////////////////////////
76 // Support functions and classes
79 // Generates a "hash" by grabbing the existing hash
80 // of the given uid, and adding 1 to it if dirty.
81 std::string GenerateHash(OSyncHashTable *hashtable,
82 const std::string &uid,
83 bool dirty)
85 unsigned long hashcount = 0;
87 const char *hash = osync_hashtable_get_hash(hashtable, uid.c_str());
88 if( hash ) {
89 errno = 0;
90 hashcount = strtoul(hash, NULL, 10);
91 if( errno )
92 throw std::runtime_error("Error converting string to unsigned long: " + std::string(hash));
95 hashcount += (dirty ? 1 : 0);
97 std::ostringstream oss;
98 oss << std::dec << hashcount;
99 return oss.str();
102 void GetChanges(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx,
103 BarryEnvironment *env,
104 DatabaseSyncState *pSync,
105 const char *DBDBName,
106 const char *ObjTypeName, const char *FormatName,
107 GetData_t getdata,
108 osync_bool slow_sync)
110 Trace trace("GetChanges");
112 OSyncError *error = NULL;
114 // shortcut references
115 using namespace Barry;
116 using Barry::RecordStateTable;
117 Mode::Desktop &desktop = *env->GetDesktop();
119 // find hash table
121 // Note: Since the Blackberry tracks dirty flags for us, we don't
122 // need the hash table to help determine what records have
123 // changed, and therefore we don't need to actually load
124 // all the record data across USB either.
126 // The hashtable only needs the hash to change when data
127 // has changed, so we set each change object's hash to:
128 // last_hash + (dirty ? 1 : 0)
130 OSyncHashTable *hashtable = osync_objtype_sink_get_hashtable(sink);
132 // check if slow sync has been requested
133 if (slow_sync) {
134 trace.log("GetChanges: slow sync request detected");
136 if( !osync_hashtable_slowsync(hashtable, &error) ) {
137 std::ostringstream oss;
138 oss << "GetChanges: slow sync error: " << osync_error_print(&error);
139 osync_error_unref(&error);
141 trace.log(oss.str().c_str());
142 throw std::runtime_error(oss.str());
146 // fetch state table
147 unsigned int dbId = desktop.GetDBID(DBDBName);
148 RecordStateTable &table = pSync->m_Table;
149 desktop.GetRecordStateTable(dbId, table);
151 OSyncFormatEnv *formatenv = osync_plugin_info_get_format_env(info);
154 // cycle through the state table... for each record in the state
155 // table, register a change and the fake hash (see note above)
156 // and let the hash table determine what changetype it is
157 RecordStateTable::StateMapType::const_iterator i = table.StateMap.begin();
158 for( ; i != table.StateMap.end(); ++i ) {
160 OSyncChange *change = 0;
161 const RecordStateTable::IndexType &index = i->first;
162 const RecordStateTable::State &state = i->second;
164 // create change to pass to hashtable
165 change = osync_change_new(&error);
166 if( !change ) {
167 osync_context_report_osyncwarning(ctx, error);
168 osync_error_unref(&error);
169 continue;
172 // convert record ID to uid string
173 std::string uid = pSync->Map2Uid(state.RecordId);
175 // setup change, just enough for hashtable use
176 osync_change_set_uid(change, uid.c_str());
177 trace.logf("change record ID: %s", uid.c_str());
178 std::string hash = GenerateHash(hashtable, uid, state.Dirty);
179 osync_change_set_hash(change, hash.c_str());
182 // let hashtable determine what's going to happen
183 OSyncChangeType changetype = osync_hashtable_get_changetype(hashtable, change);
184 osync_change_set_changetype(change, changetype);
186 // let hashtable know we've processed this change
187 osync_hashtable_update_change(hashtable, change);
190 // Decision time: if nothing has changed, skip
191 if( changetype == OSYNC_CHANGE_TYPE_UNMODIFIED ) {
192 osync_change_unref(change);
193 continue;
198 // finish filling out the change object
201 // Now you can set the data for the object
202 // Set the last argument to FALSE if the real data
203 // should be queried later in a "get_data" function
204 OSyncObjFormat *format = osync_format_env_find_objformat(formatenv, FormatName);
205 char *data = (*getdata)(env, dbId, index);
206 OSyncData *odata = osync_data_new(data, strlen(data), format, &error);
208 if (!odata) {
209 osync_change_unref(change);
210 osync_context_report_osyncwarning(ctx, error);
211 osync_error_unref(&error);
212 continue;
215 // FIXME ? Is this line is usefull ?
216 // osync_data_set_objtype(odata, osync_objtype_sink_get_name(sink));
218 osync_change_set_data(change, odata);
219 osync_data_unref(odata);
221 // just report the change via
222 osync_context_report_change(ctx, change);
224 osync_change_unref(change);
227 // the hashtable can now give us a linked list of deleted
228 // entries, after the above processing
229 AutoOSyncList uids = osync_hashtable_get_deleted(hashtable);
230 for( OSyncList *u = uids.Get(); u; u = u->next) {
232 const char *uid = (const char*) u->data;
233 uint32_t recordId = strtoul(uid, NULL, 10);
235 // search the state table
236 i = table.StateMap.begin();
237 for( ; i != table.StateMap.end(); ++i ) {
239 if( i->second.RecordId == recordId ) {
240 throw std::runtime_error("Found deleted record ID in state map! " + std::string(uid));
244 // register a DELETE, no data
245 trace.log("found DELETE change");
247 OSyncChange *change = osync_change_new(&error);
248 if( !change ) {
249 osync_context_report_osyncwarning(ctx, error);
250 osync_error_unref(&error);
251 continue;
254 osync_change_set_uid(change, uid);
255 trace.log(uid);
256 osync_change_set_changetype(change, OSYNC_CHANGE_TYPE_DELETED);
258 // osync_change_set_objformat_string(change, FormatName);
260 OSyncObjFormat *format = osync_format_env_find_objformat(formatenv, FormatName);
261 OSyncData *odata = osync_data_new(NULL, 0, format, &error);
262 if( !odata ) {
263 osync_change_unref(change);
264 osync_context_report_osyncwarning(ctx, error);
265 osync_error_unref(&error);
266 continue;
269 osync_data_set_objtype(odata, osync_objtype_sink_get_name(sink));
271 osync_change_set_data(change, odata);
272 osync_data_unref(odata);
274 // report the change
275 osync_context_report_change(ctx, change);
277 // tell hashtable we've processed this item
278 osync_hashtable_update_change(hashtable, change);
280 osync_change_unref(change);
284 CommitData_t GetCommitFunction(OSyncChange *change)
286 Trace trace("GetCommitFunction()");
288 const char *name = osync_change_get_objtype(change);
290 if( strcmp(name, "event") == 0 ) {
291 return &VEventConverter::CommitRecordData;
293 else if( strcmp(name, "contact") == 0 ) {
294 return &VCardConverter::CommitRecordData;
296 else if( strcmp(name, "note") == 0 ) {
297 return &VJournalConverter::CommitRecordData;
299 else if( strcmp(name, "todo") == 0 ) {
300 return &VTodoConverter::CommitRecordData;
302 else {
303 trace.log("unknown !");
304 trace.log(name);
306 return 0;
310 bool FinishSync(OSyncContext *ctx, BarryEnvironment *env, DatabaseSyncState *pSync)
312 Trace trace("FinishSync()");
314 if( !pSync->m_Sync ) {
315 // this mode is disabled in config, skip
316 return true;
319 // we reconnect to the device here, since dirty flags
320 // for records we've just touched do not show up until
321 // a disconnect... as far as I can tell.
322 env->ReconnectForDirtyFlags();
324 // get the state table again, so we can update
325 // the cache properly
326 Barry::Mode::Desktop &desktop = *env->GetDesktop();
327 desktop.GetRecordStateTable(pSync->m_dbId, pSync->m_Table);
329 // clear all dirty flags in device
330 env->ClearDirtyFlags(pSync->m_Table, pSync->m_dbName);
331 return true;
335 static bool barry_contact_initialize(BarryEnvironment *env, OSyncPluginInfo *info, OSyncError **error)
337 Trace trace("contact initialize");
339 OSyncObjTypeSink *sink = osync_plugin_info_find_objtype(info, "contact");
340 if (!sink)
341 return false;
342 osync_bool sinkEnabled = osync_objtype_sink_is_enabled(sink);
343 if (!sinkEnabled)
344 return false;
346 trace.log("contact enabled");
348 osync_objtype_sink_set_connect_func(sink, connect);
349 osync_objtype_sink_set_disconnect_func(sink, disconnect);
350 osync_objtype_sink_set_get_changes_func(sink, contact_get_changes);
351 osync_objtype_sink_set_commit_func(sink, commit_change);
352 osync_objtype_sink_set_sync_done_func(sink, contact_sync_done);
354 OSyncPluginConfig *config = osync_plugin_info_get_config(info);
355 OSyncPluginResource *resource = osync_plugin_config_find_active_resource(config, "contact");
357 AutoOSyncList objformatsinks = osync_plugin_resource_get_objformat_sinks(resource);
359 bool hasObjFormat = false;
361 OSyncList *r;
362 for(r = objformatsinks.Get();r;r = r->next) {
363 OSyncObjFormatSink *objformatsink = (OSyncObjFormatSink *) r->data;
365 if(!strcmp("vcard30", osync_objformat_sink_get_objformat(objformatsink))) {
366 trace.log("vcard30 found in barry-sync");
367 hasObjFormat = true;
368 break;
372 if (!hasObjFormat) {
373 return false;
376 // OSyncFormatEnv *formatenv = osync_plugin_info_get_format_env(info);
378 // env->format = osync_format_env_find_objformat(formatenv, "vcard30");
379 env->m_ContactsSync.sink = sink;
381 osync_objtype_sink_set_userdata(sink, env);
383 osync_objtype_sink_enable_hashtable(sink, TRUE);
385 trace.log("contact initialize OK");
387 return true;
391 static bool barry_calendar_initialize(BarryEnvironment *env, OSyncPluginInfo *info, OSyncError **error)
393 Trace trace("calendar initialize");
395 OSyncObjTypeSink *sink = osync_plugin_info_find_objtype(info, "event");
396 if (!sink)
397 return false;
398 osync_bool sinkEnabled = osync_objtype_sink_is_enabled(sink);
399 if (!sinkEnabled)
400 return false;
402 trace.log("calendar enabled");
404 osync_objtype_sink_set_connect_func(sink, connect);
405 osync_objtype_sink_set_disconnect_func(sink, disconnect);
406 osync_objtype_sink_set_get_changes_func(sink, event_get_changes);
407 osync_objtype_sink_set_commit_func(sink, commit_change);
408 osync_objtype_sink_set_sync_done_func(sink, event_sync_done);
410 OSyncPluginConfig *config = osync_plugin_info_get_config(info);
411 OSyncPluginResource *resource = osync_plugin_config_find_active_resource(config, "event");
413 AutoOSyncList objformatsinks = osync_plugin_resource_get_objformat_sinks(resource);
415 bool hasObjFormat = false;
417 OSyncList *r;
418 for(r = objformatsinks.Get();r;r = r->next) {
419 OSyncObjFormatSink *objformatsink = (OSyncObjFormatSink *) r->data;
421 if(!strcmp("vevent20", osync_objformat_sink_get_objformat(objformatsink))) {
422 hasObjFormat = true;
423 break;
427 if (!hasObjFormat) {
428 return false;
431 // OSyncFormatEnv *formatenv = osync_plugin_info_get_format_env(info);
433 // env->format = osync_format_env_find_objformat(formatenv, "vevent20");
434 env->m_CalendarSync.sink = sink;
436 osync_objtype_sink_set_userdata(sink, env);
438 osync_objtype_sink_enable_hashtable(sink, TRUE);
440 return true;
444 static bool barry_journal_initialize(BarryEnvironment *env, OSyncPluginInfo *info, OSyncError **error)
446 Trace trace("journal initialize");
448 OSyncObjTypeSink *sink = osync_plugin_info_find_objtype(info, "note");
449 if (!sink)
450 return false;
451 osync_bool sinkEnabled = osync_objtype_sink_is_enabled(sink);
452 if (!sinkEnabled)
453 return false;
455 trace.log("journal enabled");
457 osync_objtype_sink_set_connect_func(sink, connect);
458 osync_objtype_sink_set_disconnect_func(sink, disconnect);
459 osync_objtype_sink_set_get_changes_func(sink, journal_get_changes);
460 osync_objtype_sink_set_commit_func(sink, commit_change);
461 osync_objtype_sink_set_sync_done_func(sink, journal_sync_done);
463 OSyncPluginConfig *config = osync_plugin_info_get_config(info);
464 OSyncPluginResource *resource = osync_plugin_config_find_active_resource(config, "note");
466 AutoOSyncList objformatsinks = osync_plugin_resource_get_objformat_sinks(resource);
468 bool hasObjFormat = false;
470 OSyncList *r;
471 for(r = objformatsinks.Get();r;r = r->next) {
472 OSyncObjFormatSink *objformatsink = (OSyncObjFormatSink *) r->data;
474 if(!strcmp("vjournal", osync_objformat_sink_get_objformat(objformatsink))) {
475 hasObjFormat = true;
476 break;
480 if (!hasObjFormat) {
481 return false;
484 // OSyncFormatEnv *formatenv = osync_plugin_info_get_format_env(info);
486 // env->format = osync_format_env_find_objformat(formatenv, "vjournal");
487 env->m_JournalSync.sink = sink;
489 osync_objtype_sink_set_userdata(sink, env);
491 osync_objtype_sink_enable_hashtable(sink, TRUE);
493 return true;
497 static bool barry_todo_initialize(BarryEnvironment *env, OSyncPluginInfo *info, OSyncError **error)
499 Trace trace("todo initialize");
501 OSyncObjTypeSink *sink = osync_plugin_info_find_objtype(info, "todo");
502 if (!sink)
503 return false;
504 osync_bool sinkEnabled = osync_objtype_sink_is_enabled(sink);
505 if (!sinkEnabled)
506 return false;
508 trace.log("todo enabled");
510 osync_objtype_sink_set_connect_func(sink, connect);
511 osync_objtype_sink_set_disconnect_func(sink, disconnect);
512 osync_objtype_sink_set_get_changes_func(sink, todo_get_changes);
513 osync_objtype_sink_set_commit_func(sink, commit_change);
514 osync_objtype_sink_set_sync_done_func(sink, todo_sync_done);
516 OSyncPluginConfig *config = osync_plugin_info_get_config(info);
517 OSyncPluginResource *resource = osync_plugin_config_find_active_resource(config, "todo");
519 AutoOSyncList objformatsinks = osync_plugin_resource_get_objformat_sinks(resource);
521 bool hasObjFormat = false;
523 OSyncList *r;
524 for(r = objformatsinks.Get();r;r = r->next) {
525 OSyncObjFormatSink *objformatsink = (OSyncObjFormatSink *) r->data;
527 if(!strcmp("vtodo20", osync_objformat_sink_get_objformat(objformatsink))) {
528 hasObjFormat = true;
529 break;
533 if (!hasObjFormat) {
534 return false;
537 // OSyncFormatEnv *formatenv = osync_plugin_info_get_format_env(info);
539 // env->format = osync_format_env_find_objformat(formatenv, "vtodo20");
540 env->m_TodoSync.sink = sink;
542 osync_objtype_sink_set_userdata(sink, env);
544 osync_objtype_sink_enable_hashtable(sink, TRUE);
546 return true;
552 //////////////////////////////////////////////////////////////////////////////
554 // OpenSync API
557 static void *initialize(OSyncPlugin *plugin, OSyncPluginInfo *info, OSyncError **error)
559 Trace trace("initialize");
561 BarryEnvironment *env = 0;
563 try {
564 env = new BarryEnvironment(info);
567 * Read plugin config
569 OSyncPluginConfig *config = osync_plugin_info_get_config(info);
571 if (!config) {
572 osync_error_set(error, OSYNC_ERROR_GENERIC, "Unable to get config.");
574 delete env;
576 return NULL;
580 * Process plugin specific advanced options
582 AutoOSyncList optslist = osync_plugin_config_get_advancedoptions(config);
583 for (OSyncList *o = optslist.Get(); o; o = o->next) {
584 OSyncPluginAdvancedOption *option = (OSyncPluginAdvancedOption *) o->data;
586 const char *val = osync_plugin_advancedoption_get_value(option);
587 const char *name = osync_plugin_advancedoption_get_name(option);
589 if (!strcmp(name, "PinCode")) {
590 env->m_pin = strtol(val, NULL, 16);
592 else if (!strcmp(name, "Debug")) {
593 env->m_DebugMode = (!strcmp(val, "1")) ? true : false;
597 OSyncPluginAuthentication *optauth = osync_plugin_config_get_authentication(config);
599 if (osync_plugin_authentication_option_is_supported(optauth, OSYNC_PLUGIN_AUTHENTICATION_PASSWORD)) {
600 const char *val = osync_plugin_authentication_get_password(optauth);
602 env->SetPassword(val);
606 // FIXME - near the end of release, do a run with
607 // this set to true, and look for USB protocol
608 // inefficiencies.
609 Barry::Init(env->m_DebugMode);
613 * Process Ressource options
615 trace.log("Process Ressource options...");
617 if (barry_calendar_initialize(env, info, error)) {
618 env->m_CalendarSync.m_Sync = true;
620 else {
621 trace.log("No sync Calendar");
622 env->m_CalendarSync.m_Sync = false;
625 if (barry_contact_initialize(env, info, error)) {
626 env->m_ContactsSync.m_Sync = true;
628 else {
629 trace.log("No sync Contact");
630 env->m_ContactsSync.m_Sync = false;
633 if (barry_journal_initialize(env, info, error)) {
634 env->m_JournalSync.m_Sync = true;
636 else {
637 trace.log("No sync Journal");
638 env->m_JournalSync.m_Sync = false;
641 if (barry_todo_initialize(env, info, error)) {
642 env->m_TodoSync.m_Sync = true;
644 else {
645 trace.log("No sync Todo");
646 env->m_TodoSync.m_Sync = false;
649 return (void *) env;
651 // Don't let exceptions escape to the C modules
652 catch( std::bad_alloc &ba ) {
653 trace.logf("Unable to allocate memory for controller: %s", ba.what());
654 delete env;
655 return NULL;
657 catch( std::exception &e ) {
658 trace.logf("exception: %s", e.what());
659 delete env;
660 return NULL;
665 static osync_bool discover(OSyncPluginInfo *info, void *userdata, OSyncError **error)
667 Trace trace("discover");
669 AutoOSyncList sinks = osync_plugin_info_get_objtype_sinks(info);
670 for (OSyncList *s = sinks.Get(); s; s = s->next) {
671 OSyncObjTypeSink *sink = (OSyncObjTypeSink*) s->data;
673 osync_objtype_sink_set_available(sink, true);
676 OSyncVersion *version = osync_version_new(error);
677 osync_version_set_plugin(version, "Barry");
678 osync_version_set_modelversion(version, "1");
679 //osync_version_set_firmwareversion(version, "firmwareversion");
680 //osync_version_set_softwareversion(version, "softwareversion");
681 //osync_version_set_hardwareversion(version, "hardwareversion");
682 osync_plugin_info_set_version(info, version);
683 osync_version_unref(version);
685 return true;
689 static void connect(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata)
691 Trace trace("connect");
693 trace.logf("%s(%p, %p, %p, %p)\n", __func__, sink, info, ctx, userdata);
695 try {
696 BarryEnvironment *env = (BarryEnvironment *) userdata;
698 // I have to test if the device is already connected.
699 // Indeed, if I sync both contact and event, the connect
700 // function is called two times.
701 if (!env->isConnected()) {
702 // Probe for available devices
703 Barry::Probe probe;
704 int nIndex = probe.FindActive(env->m_pin);
705 if( nIndex == -1 ) {
706 osync_context_report_error(ctx, OSYNC_ERROR_NO_CONNECTION, "Unable to find PIN %x", env->m_pin);
707 return;
710 trace.log("connecting...");
712 env->Connect(probe.Get(nIndex));
714 trace.log("connected !");
717 // Success!
718 osync_context_report_success(ctx);
720 trace.log("connect success");
722 // Don't let exceptions escape to the C modules
723 catch( std::bad_alloc &ba ) {
724 osync_context_report_error(ctx, OSYNC_ERROR_NO_CONNECTION,
725 "Unable to allocate memory for controller: %s", ba.what());
727 catch( std::exception &e ) {
728 osync_context_report_error(ctx, OSYNC_ERROR_NO_CONNECTION,
729 "%s", e.what());
734 static void contact_get_changes(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, osync_bool slow_sync, void *userdata)
736 Trace trace("contact_get_changeinfo");
738 try {
739 BarryEnvironment *env = (BarryEnvironment *) userdata;
741 GetChanges(sink, info, ctx, env, &env->m_ContactsSync,
742 "Address Book", "contact", "vcard30",
743 &VCardConverter::GetRecordData,
744 slow_sync);
746 // Success!
747 osync_context_report_success(ctx);
750 // don't let exceptions escape to the C modules
751 catch( std::exception &e ) {
752 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
756 static void event_get_changes(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, osync_bool slow_sync, void *userdata)
758 Trace trace("event_get_changeinfo");
760 try {
761 BarryEnvironment *env = (BarryEnvironment *) userdata;
763 GetChanges(sink, info, ctx, env, &env->m_CalendarSync,
764 "Calendar", "event", "vevent20",
765 &VEventConverter::GetRecordData,
766 slow_sync);
768 // Success!
769 osync_context_report_success(ctx);
771 // don't let exceptions escape to the C modules
772 catch( std::exception &e ) {
773 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
778 static void journal_get_changes(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, osync_bool slow_sync, void *userdata)
780 Trace trace("journal_get_changeinfo");
782 try {
783 BarryEnvironment *env = (BarryEnvironment *) userdata;
785 GetChanges(sink, info, ctx, env, &env->m_JournalSync,
786 "Memos", "note", "vjournal",
787 &VJournalConverter::GetRecordData,
788 slow_sync);
790 // Success!
791 osync_context_report_success(ctx);
793 // don't let exceptions escape to the C modules
794 catch( std::exception &e ) {
795 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
800 static void todo_get_changes(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, osync_bool slow_sync, void *userdata)
802 Trace trace("todo_get_changeinfo");
804 try {
805 BarryEnvironment *env = (BarryEnvironment *) userdata;
807 GetChanges(sink, info, ctx, env, &env->m_TodoSync,
808 "Tasks", "todo", "vtodo20",
809 &VTodoConverter::GetRecordData,
810 slow_sync);
812 // Success!
813 osync_context_report_success(ctx);
815 // don't let exceptions escape to the C modules
816 catch( std::exception &e ) {
817 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
822 static void commit_change(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, OSyncChange *change, void *userdata)
824 Trace trace("commit_change");
826 // We can rely on a valid record state table, since get_changeinfo()
827 // will be called first, and will fill the table.
829 try {
831 BarryEnvironment *env = (BarryEnvironment *) userdata;
833 OSyncHashTable *hashtable = osync_objtype_sink_get_hashtable(sink);
835 // find the needed commit function, based on objtype of the change
836 CommitData_t CommitData = GetCommitFunction(change);
837 if( !CommitData ) {
838 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC,
839 "unable to get commit function pointer");
840 return;
843 // find the matching cache, state table, and id map for this change
844 DatabaseSyncState *pSync = env->GetSyncObject(change);
845 if( !pSync ) {
846 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC,
847 "unable to get sync object that matches change type");
848 return;
851 // is syncing turned on for this type?
852 if( !pSync->m_Sync ) {
853 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC,
854 "This object type is disabled in the barry-sync config");
855 return;
858 // make references instead of pointers
859 Barry::RecordStateTable &table = pSync->m_Table;
860 Barry::Mode::Desktop &desktop = *env->GetDesktop();
861 unsigned int dbId = pSync->m_dbId;
864 // either generate or retrieve the record ID, based on type
865 Barry::RecordStateTable::IndexType StateIndex;
866 unsigned long RecordId = 0;
867 if( osync_change_get_changetype(change) == OSYNC_CHANGE_TYPE_ADDED ) {
868 // create new ID for this record
869 RecordId = table.MakeNewRecordId();
871 // tell opensync to save our ID
872 char *puid = g_strdup_printf("%lu", RecordId);
873 osync_change_set_uid(change, puid);
874 g_free(puid);
876 else {
877 // extract RecordId from change's UID,
878 const char *uid = osync_change_get_uid(change);
879 trace.logf("uid from change: %s", uid);
881 // convert existing UID string to RecordId
882 if( strlen(uid) == 0 ||
883 sscanf(uid, "%lu", &RecordId) != 1 ||
884 RecordId == 0)
886 trace.logf("Unable to extract a valid record ID from: %s", uid);
887 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "Unable to extract a valid record ID from: %s", uid);
888 return;
891 // search for the RecordId in the state table, to find the
892 // index... we only need the index if we are deleting or
893 // modifying
894 if( !table.GetIndex(RecordId, &StateIndex) ) {
895 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC,
896 "unable to get state table index for RecordId: %lu",
897 RecordId);
898 return;
902 // if we get here, we are about to update the device,
903 // and dirty flags will change in such a way that a
904 // reconnect will be required later... so flag this state
905 env->RequireDirtyReconnect();
908 std::string errmsg;
909 bool status;
911 OSyncData *odata = NULL;
912 char *plain = NULL;
914 switch( osync_change_get_changetype(change) )
916 case OSYNC_CHANGE_TYPE_DELETED:
917 desktop.DeleteRecord(dbId, StateIndex);
918 break;
920 case OSYNC_CHANGE_TYPE_ADDED:
921 odata = osync_change_get_data(change);
922 osync_data_get_data(odata, &plain, NULL);
923 status = (*CommitData)(env, dbId, StateIndex, RecordId,
924 plain, true, errmsg);
925 if( !status ) {
926 trace.logf("CommitData() for ADDED state returned false: %s", errmsg.c_str());
927 osync_context_report_error(ctx, OSYNC_ERROR_PARAMETER, "%s", errmsg.c_str());
928 return;
930 osync_change_set_hash(change, "0");
931 break;
933 case OSYNC_CHANGE_TYPE_MODIFIED:
934 odata = osync_change_get_data(change);
935 osync_data_get_data(odata, &plain, NULL);
936 status = (*CommitData)(env, dbId, StateIndex, RecordId,
937 plain, false, errmsg);
938 if( !status ) {
939 trace.logf("CommitData() for MODIFIED state returned false: %s", errmsg.c_str());
940 osync_context_report_error(ctx, OSYNC_ERROR_PARAMETER, "%s", errmsg.c_str());
941 return;
943 osync_change_set_hash(change, "0");
944 break;
946 default:
947 trace.log("Unknown change type");
948 break;
951 // Update hashtable
952 osync_hashtable_update_change(hashtable, change);
954 // Answer the call
955 osync_context_report_success(ctx);
956 return;
960 catch( std::exception &e ) {
961 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
962 return;
967 static void contact_sync_done(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata)
970 // This function will only be called if the sync was successfull
973 Trace trace("contact_sync_done");
975 try {
977 BarryEnvironment *env = (BarryEnvironment *) userdata;
979 // do cleanup for each database
980 if( FinishSync(ctx, env, &env->m_ContactsSync) )
982 // Success
983 osync_context_report_success(ctx);
987 catch( std::exception &e ) {
988 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
993 static void event_sync_done(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata)
996 // This function will only be called if the sync was successfull
999 Trace trace("event_sync_done");
1001 try {
1003 BarryEnvironment *env = (BarryEnvironment *) userdata;
1005 // do cleanup for each database
1006 if( FinishSync(ctx, env, &env->m_CalendarSync) )
1008 // Success
1009 osync_context_report_success(ctx);
1013 catch( std::exception &e ) {
1014 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
1019 static void journal_sync_done(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata)
1022 // This function will only be called if the sync was successfull
1025 Trace trace("journal_sync_done");
1027 try {
1029 BarryEnvironment *env = (BarryEnvironment *) userdata;
1031 // do cleanup for each database
1032 if( FinishSync(ctx, env, &env->m_JournalSync) )
1034 // Success
1035 osync_context_report_success(ctx);
1039 catch( std::exception &e ) {
1040 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
1045 static void todo_sync_done(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata)
1048 // This function will only be called if the sync was successfull
1051 Trace trace("todo_sync_done");
1053 try {
1055 BarryEnvironment *env = (BarryEnvironment *) userdata;
1057 // do cleanup for each database
1058 if( FinishSync(ctx, env, &env->m_TodoSync) )
1060 // Success
1061 osync_context_report_success(ctx);
1065 catch( std::exception &e ) {
1066 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
1071 static void disconnect(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata)
1073 Trace trace("contact_disconnect");
1075 // Done!
1076 osync_context_report_success(ctx);
1080 static void finalize(void *data)
1082 Trace trace("finalize");
1084 BarryEnvironment *env = (BarryEnvironment *) data;
1086 // Disconnect the controller, which closes our connection
1087 if (env->isConnected())
1088 env->Disconnect();
1090 delete env;
1094 osync_bool get_sync_info(OSyncPluginEnv *env, OSyncError **error)
1096 Trace trace("get_sync_info");
1098 // Create a new OpenSync plugin
1099 OSyncPlugin *plugin = osync_plugin_new(error);
1100 if( !plugin ) {
1101 trace.log(osync_error_print(error));
1102 return false;
1105 // Describe our plugin
1106 osync_plugin_set_name(plugin, "barry-sync");
1107 osync_plugin_set_longname(plugin, "Barry OpenSync plugin v0.18.0 for the Blackberry handheld");
1108 osync_plugin_set_description(plugin, "Plugin to synchronize note, task, calendar and contact entries on USB Blackberry handhelds");
1110 // Set the callback functions
1111 osync_plugin_set_initialize_func(plugin, initialize);
1112 osync_plugin_set_finalize_func(plugin, finalize);
1113 osync_plugin_set_discover_func(plugin, discover);
1114 osync_plugin_set_start_type(plugin, OSYNC_START_TYPE_PROCESS);
1116 if( !osync_plugin_env_register_plugin(env, plugin, error) ) {
1117 trace.log(osync_error_print(error));
1118 return false;
1121 osync_plugin_unref(plugin);
1123 return true;
1127 int get_version(void)
1129 return 1;