lib: set ProbeResult's constructor to private
[barry.git] / opensync-plugin-0.4x / src / barry_sync.cc
blobf3d81b3d92d7f17c23f19096d075630ea1e38388
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-2010, 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->m_pDesktop;
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->m_pDesktop;
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->m_password = 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;
709 env->m_ProbeResult.reset( new Barry::ProbeResult(probe.Get(nIndex)) );
711 trace.log("connecting...");
713 env->Connect(probe.Get(nIndex));
715 trace.log("connected !");
718 // Success!
719 osync_context_report_success(ctx);
721 trace.log("connect success");
723 // Don't let exceptions escape to the C modules
724 catch( std::bad_alloc &ba ) {
725 osync_context_report_error(ctx, OSYNC_ERROR_NO_CONNECTION,
726 "Unable to allocate memory for controller: %s", ba.what());
728 catch( std::exception &e ) {
729 osync_context_report_error(ctx, OSYNC_ERROR_NO_CONNECTION,
730 "%s", e.what());
735 static void contact_get_changes(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, osync_bool slow_sync, void *userdata)
737 Trace trace("contact_get_changeinfo");
739 try {
740 BarryEnvironment *env = (BarryEnvironment *) userdata;
742 GetChanges(sink, info, ctx, env, &env->m_ContactsSync,
743 "Address Book", "contact", "vcard30",
744 &VCardConverter::GetRecordData,
745 slow_sync);
747 // Success!
748 osync_context_report_success(ctx);
751 // don't let exceptions escape to the C modules
752 catch( std::exception &e ) {
753 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
757 static void event_get_changes(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, osync_bool slow_sync, void *userdata)
759 Trace trace("event_get_changeinfo");
761 try {
762 BarryEnvironment *env = (BarryEnvironment *) userdata;
764 GetChanges(sink, info, ctx, env, &env->m_CalendarSync,
765 "Calendar", "event", "vevent20",
766 &VEventConverter::GetRecordData,
767 slow_sync);
769 // Success!
770 osync_context_report_success(ctx);
772 // don't let exceptions escape to the C modules
773 catch( std::exception &e ) {
774 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
779 static void journal_get_changes(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, osync_bool slow_sync, void *userdata)
781 Trace trace("journal_get_changeinfo");
783 try {
784 BarryEnvironment *env = (BarryEnvironment *) userdata;
786 GetChanges(sink, info, ctx, env, &env->m_JournalSync,
787 "Memos", "note", "vjournal",
788 &VJournalConverter::GetRecordData,
789 slow_sync);
791 // Success!
792 osync_context_report_success(ctx);
794 // don't let exceptions escape to the C modules
795 catch( std::exception &e ) {
796 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
801 static void todo_get_changes(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, osync_bool slow_sync, void *userdata)
803 Trace trace("todo_get_changeinfo");
805 try {
806 BarryEnvironment *env = (BarryEnvironment *) userdata;
808 GetChanges(sink, info, ctx, env, &env->m_TodoSync,
809 "Tasks", "todo", "vtodo20",
810 &VTodoConverter::GetRecordData,
811 slow_sync);
813 // Success!
814 osync_context_report_success(ctx);
816 // don't let exceptions escape to the C modules
817 catch( std::exception &e ) {
818 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
823 static void commit_change(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, OSyncChange *change, void *userdata)
825 Trace trace("commit_change");
827 // We can rely on a valid record state table, since get_changeinfo()
828 // will be called first, and will fill the table.
830 try {
832 BarryEnvironment *env = (BarryEnvironment *) userdata;
834 OSyncHashTable *hashtable = osync_objtype_sink_get_hashtable(sink);
836 // find the needed commit function, based on objtype of the change
837 CommitData_t CommitData = GetCommitFunction(change);
838 if( !CommitData ) {
839 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC,
840 "unable to get commit function pointer");
841 return;
844 // find the matching cache, state table, and id map for this change
845 DatabaseSyncState *pSync = env->GetSyncObject(change);
846 if( !pSync ) {
847 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC,
848 "unable to get sync object that matches change type");
849 return;
852 // is syncing turned on for this type?
853 if( !pSync->m_Sync ) {
854 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC,
855 "This object type is disabled in the barry-sync config");
856 return;
859 // make references instead of pointers
860 Barry::RecordStateTable &table = pSync->m_Table;
861 Barry::Mode::Desktop &desktop = *env->m_pDesktop;
862 unsigned int dbId = pSync->m_dbId;
865 // either generate or retrieve the record ID, based on type
866 Barry::RecordStateTable::IndexType StateIndex;
867 unsigned long RecordId = 0;
868 if( osync_change_get_changetype(change) == OSYNC_CHANGE_TYPE_ADDED ) {
869 // create new ID for this record
870 RecordId = table.MakeNewRecordId();
872 // tell opensync to save our ID
873 char *puid = g_strdup_printf("%lu", RecordId);
874 osync_change_set_uid(change, puid);
875 g_free(puid);
877 else {
878 // extract RecordId from change's UID,
879 const char *uid = osync_change_get_uid(change);
880 trace.logf("uid from change: %s", uid);
882 // convert existing UID string to RecordId
883 if( strlen(uid) == 0 ||
884 sscanf(uid, "%lu", &RecordId) != 1 ||
885 RecordId == 0)
887 trace.logf("Unable to extract a valid record ID from: %s", uid);
888 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "Unable to extract a valid record ID from: %s", uid);
889 return;
892 // search for the RecordId in the state table, to find the
893 // index... we only need the index if we are deleting or
894 // modifying
895 if( !table.GetIndex(RecordId, &StateIndex) ) {
896 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC,
897 "unable to get state table index for RecordId: %lu",
898 RecordId);
899 return;
903 // if we get here, we are about to update the device,
904 // and dirty flags will change in such a way that a
905 // reconnect will be required later... so flag this state
906 env->RequireDirtyReconnect();
909 std::string errmsg;
910 bool status;
912 OSyncData *odata = NULL;
913 char *plain = NULL;
915 switch( osync_change_get_changetype(change) )
917 case OSYNC_CHANGE_TYPE_DELETED:
918 desktop.DeleteRecord(dbId, StateIndex);
919 break;
921 case OSYNC_CHANGE_TYPE_ADDED:
922 odata = osync_change_get_data(change);
923 osync_data_get_data(odata, &plain, NULL);
924 status = (*CommitData)(env, dbId, StateIndex, RecordId,
925 plain, true, errmsg);
926 if( !status ) {
927 trace.logf("CommitData() for ADDED state returned false: %s", errmsg.c_str());
928 osync_context_report_error(ctx, OSYNC_ERROR_PARAMETER, "%s", errmsg.c_str());
929 return;
931 osync_change_set_hash(change, "0");
932 break;
934 case OSYNC_CHANGE_TYPE_MODIFIED:
935 odata = osync_change_get_data(change);
936 osync_data_get_data(odata, &plain, NULL);
937 status = (*CommitData)(env, dbId, StateIndex, RecordId,
938 plain, false, errmsg);
939 if( !status ) {
940 trace.logf("CommitData() for MODIFIED state returned false: %s", errmsg.c_str());
941 osync_context_report_error(ctx, OSYNC_ERROR_PARAMETER, "%s", errmsg.c_str());
942 return;
944 osync_change_set_hash(change, "0");
945 break;
947 default:
948 trace.log("Unknown change type");
949 break;
952 // Update hashtable
953 osync_hashtable_update_change(hashtable, change);
955 // Answer the call
956 osync_context_report_success(ctx);
957 return;
961 catch( std::exception &e ) {
962 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
963 return;
968 static void contact_sync_done(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata)
971 // This function will only be called if the sync was successfull
974 Trace trace("contact_sync_done");
976 try {
978 BarryEnvironment *env = (BarryEnvironment *) userdata;
980 // do cleanup for each database
981 if( FinishSync(ctx, env, &env->m_ContactsSync) )
983 // Success
984 osync_context_report_success(ctx);
988 catch( std::exception &e ) {
989 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
994 static void event_sync_done(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata)
997 // This function will only be called if the sync was successfull
1000 Trace trace("event_sync_done");
1002 try {
1004 BarryEnvironment *env = (BarryEnvironment *) userdata;
1006 // do cleanup for each database
1007 if( FinishSync(ctx, env, &env->m_CalendarSync) )
1009 // Success
1010 osync_context_report_success(ctx);
1014 catch( std::exception &e ) {
1015 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
1020 static void journal_sync_done(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata)
1023 // This function will only be called if the sync was successfull
1026 Trace trace("journal_sync_done");
1028 try {
1030 BarryEnvironment *env = (BarryEnvironment *) userdata;
1032 // do cleanup for each database
1033 if( FinishSync(ctx, env, &env->m_JournalSync) )
1035 // Success
1036 osync_context_report_success(ctx);
1040 catch( std::exception &e ) {
1041 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
1046 static void todo_sync_done(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata)
1049 // This function will only be called if the sync was successfull
1052 Trace trace("todo_sync_done");
1054 try {
1056 BarryEnvironment *env = (BarryEnvironment *) userdata;
1058 // do cleanup for each database
1059 if( FinishSync(ctx, env, &env->m_TodoSync) )
1061 // Success
1062 osync_context_report_success(ctx);
1066 catch( std::exception &e ) {
1067 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
1072 static void disconnect(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata)
1074 Trace trace("contact_disconnect");
1076 // Done!
1077 osync_context_report_success(ctx);
1081 static void finalize(void *data)
1083 Trace trace("finalize");
1085 BarryEnvironment *env = (BarryEnvironment *) data;
1087 // Disconnect the controller, which closes our connection
1088 if (env->isConnected())
1089 env->Disconnect();
1091 delete env;
1095 osync_bool get_sync_info(OSyncPluginEnv *env, OSyncError **error)
1097 Trace trace("get_sync_info");
1099 // Create a new OpenSync plugin
1100 OSyncPlugin *plugin = osync_plugin_new(error);
1101 if( !plugin ) {
1102 trace.log(osync_error_print(error));
1103 return false;
1106 // Describe our plugin
1107 osync_plugin_set_name(plugin, "barry-sync");
1108 osync_plugin_set_longname(plugin, "Barry OpenSync plugin v0.17 for the Blackberry handheld");
1109 osync_plugin_set_description(plugin, "Plugin to synchronize note, task, calendar and contact entries on USB Blackberry handhelds");
1111 // Set the callback functions
1112 osync_plugin_set_initialize(plugin, initialize);
1113 osync_plugin_set_finalize(plugin, finalize);
1114 osync_plugin_set_discover(plugin, discover);
1115 osync_plugin_set_start_type(plugin, OSYNC_START_TYPE_PROCESS);
1117 if( !osync_plugin_env_register_plugin(env, plugin, error) ) {
1118 trace.log(osync_error_print(error));
1119 return false;
1122 osync_plugin_unref(plugin);
1124 return true;
1128 int get_version(void)
1130 return 1;