menu: added new Keywords tag to .desktop files
[barry.git] / opensync-plugin-0.4x / src / barry_sync.cc
blob842b173dff90dbef26957fae2ed9db3b8eb4c3cb
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-2013, 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 "config.h"
40 #include <string>
41 #include <glib.h>
42 #include <stdint.h>
43 #include <string.h>
44 #include <errno.h>
45 #include "i18n.h"
47 typedef Barry::vSmartPtr<OSyncList, OSyncList, &osync_list_free> AutoOSyncList;
49 // All functions that are callable from outside must look like C
50 extern "C" {
51 BXEXPORT int get_version(void);
52 static void *initialize(OSyncPlugin *plugin, OSyncPluginInfo *info, OSyncError **error);
53 static osync_bool discover(OSyncPluginInfo *info, void *userdata, OSyncError **error);
55 static void contact_get_changes(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, osync_bool slow_sync, void *userdata);
56 static void contact_sync_done(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata);
58 static void event_get_changes(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, osync_bool slow_sync, void *userdata);
59 static void event_sync_done(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata);
61 static void journal_get_changes(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, osync_bool slow_sync, void *userdata);
62 static void journal_sync_done(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata);
64 static void todo_get_changes(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, osync_bool slow_sync, void *userdata);
65 static void todo_sync_done(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata);
67 static void connect(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata);
68 static void disconnect(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata);
69 static void commit_change(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, OSyncChange *change, void *userdata);
70 static void finalize(void *userdata);
71 BXEXPORT osync_bool get_sync_info(OSyncPluginEnv *env, OSyncError **error);
76 //////////////////////////////////////////////////////////////////////////////
78 // Support functions and classes
81 // Generates a "hash" by grabbing the existing hash
82 // of the given uid, and adding 1 to it if dirty.
83 std::string GenerateHash(OSyncHashTable *hashtable,
84 const std::string &uid,
85 bool dirty)
87 unsigned long hashcount = 0;
89 const char *hash = osync_hashtable_get_hash(hashtable, uid.c_str());
90 if( hash ) {
91 errno = 0;
92 hashcount = strtoul(hash, NULL, 10);
93 if( errno )
94 throw std::runtime_error(_("Error converting string to unsigned long: ") + std::string(hash));
97 hashcount += (dirty ? 1 : 0);
99 std::ostringstream oss;
100 oss << std::dec << hashcount;
101 return oss.str();
104 void GetChanges(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx,
105 BarryEnvironment *env,
106 DatabaseSyncState *pSync,
107 const char *DBDBName,
108 const char *ObjTypeName, const char *FormatName,
109 GetData_t getdata,
110 osync_bool slow_sync)
112 Trace trace("GetChanges");
114 OSyncError *error = NULL;
116 // shortcut references
117 using namespace Barry;
118 using Barry::RecordStateTable;
119 Mode::Desktop &desktop = *env->GetDesktop();
121 // find hash table
123 // Note: Since the Blackberry tracks dirty flags for us, we don't
124 // need the hash table to help determine what records have
125 // changed, and therefore we don't need to actually load
126 // all the record data across USB either.
128 // The hashtable only needs the hash to change when data
129 // has changed, so we set each change object's hash to:
130 // last_hash + (dirty ? 1 : 0)
132 OSyncHashTable *hashtable = osync_objtype_sink_get_hashtable(sink);
134 // check if slow sync has been requested
135 if (slow_sync) {
136 trace.log(_("GetChanges: slow sync request detected"));
138 if( !osync_hashtable_slowsync(hashtable, &error) ) {
139 std::ostringstream oss;
140 oss << _("GetChanges: slow sync error: ") << osync_error_print(&error);
141 osync_error_unref(&error);
143 trace.log(oss.str().c_str());
144 throw std::runtime_error(oss.str());
148 // fetch state table
149 unsigned int dbId = desktop.GetDBID(DBDBName);
150 RecordStateTable &table = pSync->m_Table;
151 desktop.GetRecordStateTable(dbId, table);
153 OSyncFormatEnv *formatenv = osync_plugin_info_get_format_env(info);
156 // cycle through the state table... for each record in the state
157 // table, register a change and the fake hash (see note above)
158 // and let the hash table determine what changetype it is
159 RecordStateTable::StateMapType::const_iterator i = table.StateMap.begin();
160 for( ; i != table.StateMap.end(); ++i ) {
162 OSyncChange *change = 0;
163 const RecordStateTable::IndexType &index = i->first;
164 const RecordStateTable::State &state = i->second;
166 // create change to pass to hashtable
167 change = osync_change_new(&error);
168 if( !change ) {
169 osync_context_report_osyncwarning(ctx, error);
170 osync_error_unref(&error);
171 continue;
174 // convert record ID to uid string
175 std::string uid = pSync->Map2Uid(state.RecordId);
177 // setup change, just enough for hashtable use
178 osync_change_set_uid(change, uid.c_str());
179 trace.logf(_("change record ID: %s"), uid.c_str());
180 std::string hash = GenerateHash(hashtable, uid, state.Dirty);
181 osync_change_set_hash(change, hash.c_str());
184 // let hashtable determine what's going to happen
185 OSyncChangeType changetype = osync_hashtable_get_changetype(hashtable, change);
186 osync_change_set_changetype(change, changetype);
188 // let hashtable know we've processed this change
189 osync_hashtable_update_change(hashtable, change);
192 // Decision time: if nothing has changed, skip
193 if( changetype == OSYNC_CHANGE_TYPE_UNMODIFIED ) {
194 osync_change_unref(change);
195 continue;
200 // finish filling out the change object
203 // Now you can set the data for the object
204 // Set the last argument to FALSE if the real data
205 // should be queried later in a "get_data" function
206 OSyncObjFormat *format = osync_format_env_find_objformat(formatenv, FormatName);
207 char *data = (*getdata)(env, dbId, index);
208 OSyncData *odata = osync_data_new(data, strlen(data), format, &error);
210 if (!odata) {
211 osync_change_unref(change);
212 osync_context_report_osyncwarning(ctx, error);
213 osync_error_unref(&error);
214 continue;
217 // FIXME ? Is this line is usefull ?
218 // osync_data_set_objtype(odata, osync_objtype_sink_get_name(sink));
220 osync_change_set_data(change, odata);
221 osync_data_unref(odata);
223 // just report the change via
224 osync_context_report_change(ctx, change);
226 osync_change_unref(change);
229 // the hashtable can now give us a linked list of deleted
230 // entries, after the above processing
231 AutoOSyncList uids = osync_hashtable_get_deleted(hashtable);
232 for( OSyncList *u = uids.Get(); u; u = u->next) {
234 const char *uid = (const char*) u->data;
235 uint32_t recordId = strtoul(uid, NULL, 10);
237 // search the state table
238 i = table.StateMap.begin();
239 for( ; i != table.StateMap.end(); ++i ) {
241 if( i->second.RecordId == recordId ) {
242 throw std::runtime_error(_("Found deleted record ID in state map! ") + std::string(uid));
246 // register a DELETE, no data
247 trace.log(_("found DELETE change"));
249 OSyncChange *change = osync_change_new(&error);
250 if( !change ) {
251 osync_context_report_osyncwarning(ctx, error);
252 osync_error_unref(&error);
253 continue;
256 osync_change_set_uid(change, uid);
257 trace.log(uid);
258 osync_change_set_changetype(change, OSYNC_CHANGE_TYPE_DELETED);
260 // osync_change_set_objformat_string(change, FormatName);
262 OSyncObjFormat *format = osync_format_env_find_objformat(formatenv, FormatName);
263 OSyncData *odata = osync_data_new(NULL, 0, format, &error);
264 if( !odata ) {
265 osync_change_unref(change);
266 osync_context_report_osyncwarning(ctx, error);
267 osync_error_unref(&error);
268 continue;
271 osync_data_set_objtype(odata, osync_objtype_sink_get_name(sink));
273 osync_change_set_data(change, odata);
274 osync_data_unref(odata);
276 // report the change
277 osync_context_report_change(ctx, change);
279 // tell hashtable we've processed this item
280 osync_hashtable_update_change(hashtable, change);
282 osync_change_unref(change);
286 CommitData_t GetCommitFunction(OSyncChange *change)
288 Trace trace("GetCommitFunction()");
290 const char *name = osync_change_get_objtype(change);
292 if( strcmp(name, "event") == 0 ) {
293 return &VEventConverter::CommitRecordData;
295 else if( strcmp(name, "contact") == 0 ) {
296 return &VCardConverter::CommitRecordData;
298 else if( strcmp(name, "note") == 0 ) {
299 return &VJournalConverter::CommitRecordData;
301 else if( strcmp(name, "todo") == 0 ) {
302 return &VTodoConverter::CommitRecordData;
304 else {
305 trace.log("unknown !");
306 trace.log(name);
308 return 0;
312 bool FinishSync(OSyncContext *ctx, BarryEnvironment *env, DatabaseSyncState *pSync)
314 Trace trace("FinishSync()");
316 if( !pSync->m_Sync ) {
317 // this mode is disabled in config, skip
318 return true;
321 // we reconnect to the device here, since dirty flags
322 // for records we've just touched do not show up until
323 // a disconnect... as far as I can tell.
324 env->ReconnectForDirtyFlags();
326 // get the state table again, so we can update
327 // the cache properly
328 Barry::Mode::Desktop &desktop = *env->GetDesktop();
329 desktop.GetRecordStateTable(pSync->m_dbId, pSync->m_Table);
331 // clear all dirty flags in device
332 env->ClearDirtyFlags(pSync->m_Table, pSync->m_dbName);
333 return true;
337 static bool barry_contact_initialize(BarryEnvironment *env, OSyncPluginInfo *info, OSyncError **error)
339 Trace trace("contact initialize");
341 OSyncObjTypeSink *sink = osync_plugin_info_find_objtype(info, "contact");
342 if (!sink)
343 return false;
344 osync_bool sinkEnabled = osync_objtype_sink_is_enabled(sink);
345 if (!sinkEnabled)
346 return false;
348 trace.log("contact enabled");
350 osync_objtype_sink_set_connect_func(sink, connect);
351 osync_objtype_sink_set_disconnect_func(sink, disconnect);
352 osync_objtype_sink_set_get_changes_func(sink, contact_get_changes);
353 osync_objtype_sink_set_commit_func(sink, commit_change);
354 osync_objtype_sink_set_sync_done_func(sink, contact_sync_done);
356 OSyncPluginConfig *config = osync_plugin_info_get_config(info);
357 OSyncPluginResource *resource = osync_plugin_config_find_active_resource(config, "contact");
359 AutoOSyncList objformatsinks = osync_plugin_resource_get_objformat_sinks(resource);
361 bool hasObjFormat = false;
363 OSyncList *r;
364 for(r = objformatsinks.Get();r;r = r->next) {
365 OSyncObjFormatSink *objformatsink = (OSyncObjFormatSink *) r->data;
367 if(!strcmp("vcard30", osync_objformat_sink_get_objformat(objformatsink))) {
368 trace.log(_("vcard30 found in barry-sync"));
369 hasObjFormat = true;
370 break;
374 if (!hasObjFormat) {
375 return false;
378 // OSyncFormatEnv *formatenv = osync_plugin_info_get_format_env(info);
380 // env->format = osync_format_env_find_objformat(formatenv, "vcard30");
381 env->m_ContactsSync.sink = sink;
383 osync_objtype_sink_set_userdata(sink, env);
385 osync_objtype_sink_enable_hashtable(sink, TRUE);
387 trace.log(_("contact initialize OK"));
389 return true;
393 static bool barry_calendar_initialize(BarryEnvironment *env, OSyncPluginInfo *info, OSyncError **error)
395 Trace trace("calendar initialize");
397 OSyncObjTypeSink *sink = osync_plugin_info_find_objtype(info, "event");
398 if (!sink)
399 return false;
400 osync_bool sinkEnabled = osync_objtype_sink_is_enabled(sink);
401 if (!sinkEnabled)
402 return false;
404 trace.log("calendar enabled");
406 osync_objtype_sink_set_connect_func(sink, connect);
407 osync_objtype_sink_set_disconnect_func(sink, disconnect);
408 osync_objtype_sink_set_get_changes_func(sink, event_get_changes);
409 osync_objtype_sink_set_commit_func(sink, commit_change);
410 osync_objtype_sink_set_sync_done_func(sink, event_sync_done);
412 OSyncPluginConfig *config = osync_plugin_info_get_config(info);
413 OSyncPluginResource *resource = osync_plugin_config_find_active_resource(config, "event");
415 AutoOSyncList objformatsinks = osync_plugin_resource_get_objformat_sinks(resource);
417 bool hasObjFormat = false;
419 OSyncList *r;
420 for(r = objformatsinks.Get();r;r = r->next) {
421 OSyncObjFormatSink *objformatsink = (OSyncObjFormatSink *) r->data;
423 if(!strcmp("vevent20", osync_objformat_sink_get_objformat(objformatsink))) {
424 hasObjFormat = true;
425 break;
429 if (!hasObjFormat) {
430 return false;
433 // OSyncFormatEnv *formatenv = osync_plugin_info_get_format_env(info);
435 // env->format = osync_format_env_find_objformat(formatenv, "vevent20");
436 env->m_CalendarSync.sink = sink;
438 osync_objtype_sink_set_userdata(sink, env);
440 osync_objtype_sink_enable_hashtable(sink, TRUE);
442 return true;
446 static bool barry_journal_initialize(BarryEnvironment *env, OSyncPluginInfo *info, OSyncError **error)
448 Trace trace("journal initialize");
450 OSyncObjTypeSink *sink = osync_plugin_info_find_objtype(info, "note");
451 if (!sink)
452 return false;
453 osync_bool sinkEnabled = osync_objtype_sink_is_enabled(sink);
454 if (!sinkEnabled)
455 return false;
457 trace.log("journal enabled");
459 osync_objtype_sink_set_connect_func(sink, connect);
460 osync_objtype_sink_set_disconnect_func(sink, disconnect);
461 osync_objtype_sink_set_get_changes_func(sink, journal_get_changes);
462 osync_objtype_sink_set_commit_func(sink, commit_change);
463 osync_objtype_sink_set_sync_done_func(sink, journal_sync_done);
465 OSyncPluginConfig *config = osync_plugin_info_get_config(info);
466 OSyncPluginResource *resource = osync_plugin_config_find_active_resource(config, "note");
468 AutoOSyncList objformatsinks = osync_plugin_resource_get_objformat_sinks(resource);
470 bool hasObjFormat = false;
472 OSyncList *r;
473 for(r = objformatsinks.Get();r;r = r->next) {
474 OSyncObjFormatSink *objformatsink = (OSyncObjFormatSink *) r->data;
476 if(!strcmp("vjournal", osync_objformat_sink_get_objformat(objformatsink))) {
477 hasObjFormat = true;
478 break;
482 if (!hasObjFormat) {
483 return false;
486 // OSyncFormatEnv *formatenv = osync_plugin_info_get_format_env(info);
488 // env->format = osync_format_env_find_objformat(formatenv, "vjournal");
489 env->m_JournalSync.sink = sink;
491 osync_objtype_sink_set_userdata(sink, env);
493 osync_objtype_sink_enable_hashtable(sink, TRUE);
495 return true;
499 static bool barry_todo_initialize(BarryEnvironment *env, OSyncPluginInfo *info, OSyncError **error)
501 Trace trace("todo initialize");
503 OSyncObjTypeSink *sink = osync_plugin_info_find_objtype(info, "todo");
504 if (!sink)
505 return false;
506 osync_bool sinkEnabled = osync_objtype_sink_is_enabled(sink);
507 if (!sinkEnabled)
508 return false;
510 trace.log("todo enabled");
512 osync_objtype_sink_set_connect_func(sink, connect);
513 osync_objtype_sink_set_disconnect_func(sink, disconnect);
514 osync_objtype_sink_set_get_changes_func(sink, todo_get_changes);
515 osync_objtype_sink_set_commit_func(sink, commit_change);
516 osync_objtype_sink_set_sync_done_func(sink, todo_sync_done);
518 OSyncPluginConfig *config = osync_plugin_info_get_config(info);
519 OSyncPluginResource *resource = osync_plugin_config_find_active_resource(config, "todo");
521 AutoOSyncList objformatsinks = osync_plugin_resource_get_objformat_sinks(resource);
523 bool hasObjFormat = false;
525 OSyncList *r;
526 for(r = objformatsinks.Get();r;r = r->next) {
527 OSyncObjFormatSink *objformatsink = (OSyncObjFormatSink *) r->data;
529 if(!strcmp("vtodo20", osync_objformat_sink_get_objformat(objformatsink))) {
530 hasObjFormat = true;
531 break;
535 if (!hasObjFormat) {
536 return false;
539 // OSyncFormatEnv *formatenv = osync_plugin_info_get_format_env(info);
541 // env->format = osync_format_env_find_objformat(formatenv, "vtodo20");
542 env->m_TodoSync.sink = sink;
544 osync_objtype_sink_set_userdata(sink, env);
546 osync_objtype_sink_enable_hashtable(sink, TRUE);
548 return true;
554 //////////////////////////////////////////////////////////////////////////////
556 // OpenSync API
559 static void *initialize(OSyncPlugin *plugin, OSyncPluginInfo *info, OSyncError **error)
561 Trace trace("initialize");
563 BarryEnvironment *env = 0;
565 try {
566 env = new BarryEnvironment(info);
569 * Read plugin config
571 OSyncPluginConfig *config = osync_plugin_info_get_config(info);
573 if (!config) {
574 osync_error_set(error, OSYNC_ERROR_GENERIC, _("Unable to get config."));
576 delete env;
578 return NULL;
582 * Process plugin specific advanced options
584 AutoOSyncList optslist = osync_plugin_config_get_advancedoptions(config);
585 for (OSyncList *o = optslist.Get(); o; o = o->next) {
586 OSyncPluginAdvancedOption *option = (OSyncPluginAdvancedOption *) o->data;
588 const char *val = osync_plugin_advancedoption_get_value(option);
589 const char *name = osync_plugin_advancedoption_get_name(option);
591 if (!strcmp(name, "PinCode")) {
592 env->m_pin = strtol(val, NULL, 16);
594 else if (!strcmp(name, "Debug")) {
595 env->m_DebugMode = (!strcmp(val, "1")) ? true : false;
599 OSyncPluginAuthentication *optauth = osync_plugin_config_get_authentication(config);
601 if (osync_plugin_authentication_option_is_supported(optauth, OSYNC_PLUGIN_AUTHENTICATION_PASSWORD)) {
602 const char *val = osync_plugin_authentication_get_password(optauth);
604 env->SetPassword(val);
608 // FIXME - near the end of release, do a run with
609 // this set to true, and look for USB protocol
610 // inefficiencies.
611 Barry::Init(env->m_DebugMode);
615 * Process Resource options
617 trace.log(_("Process Resource options..."));
619 if (barry_calendar_initialize(env, info, error)) {
620 env->m_CalendarSync.m_Sync = true;
622 else {
623 trace.log(_("No sync Calendar"));
624 env->m_CalendarSync.m_Sync = false;
627 if (barry_contact_initialize(env, info, error)) {
628 env->m_ContactsSync.m_Sync = true;
630 else {
631 trace.log(_("No sync Contact"));
632 env->m_ContactsSync.m_Sync = false;
635 if (barry_journal_initialize(env, info, error)) {
636 env->m_JournalSync.m_Sync = true;
638 else {
639 trace.log(_("No sync Journal"));
640 env->m_JournalSync.m_Sync = false;
643 if (barry_todo_initialize(env, info, error)) {
644 env->m_TodoSync.m_Sync = true;
646 else {
647 trace.log(_("No sync Todo"));
648 env->m_TodoSync.m_Sync = false;
651 return (void *) env;
653 // Don't let exceptions escape to the C modules
654 catch( std::bad_alloc &ba ) {
655 trace.logf(_("Unable to allocate memory for controller: %s"), ba.what());
656 delete env;
657 return NULL;
659 catch( std::exception &e ) {
660 trace.logf(_("exception: %s"), e.what());
661 delete env;
662 return NULL;
667 static osync_bool discover(OSyncPluginInfo *info, void *userdata, OSyncError **error)
669 Trace trace("discover");
671 AutoOSyncList sinks = osync_plugin_info_get_objtype_sinks(info);
672 for (OSyncList *s = sinks.Get(); s; s = s->next) {
673 OSyncObjTypeSink *sink = (OSyncObjTypeSink*) s->data;
675 osync_objtype_sink_set_available(sink, true);
678 OSyncVersion *version = osync_version_new(error);
679 osync_version_set_plugin(version, "Barry");
680 osync_version_set_modelversion(version, "1");
681 //osync_version_set_firmwareversion(version, "firmwareversion");
682 //osync_version_set_softwareversion(version, "softwareversion");
683 //osync_version_set_hardwareversion(version, "hardwareversion");
684 osync_plugin_info_set_version(info, version);
685 osync_version_unref(version);
687 return true;
691 static void connect(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata)
693 Trace trace("connect");
695 trace.logf("%s(%p, %p, %p, %p)\n", __func__, sink, info, ctx, userdata);
697 try {
698 BarryEnvironment *env = (BarryEnvironment *) userdata;
700 // I have to test if the device is already connected.
701 // Indeed, if I sync both contact and event, the connect
702 // function is called two times.
703 if (!env->isConnected()) {
704 // Probe for available devices
705 Barry::Probe probe;
706 int nIndex = probe.FindActive(env->m_pin);
707 if( nIndex == -1 ) {
708 osync_context_report_error(ctx, OSYNC_ERROR_NO_CONNECTION, _("Unable to find PIN %x"), env->m_pin);
709 return;
712 trace.log(_("connecting..."));
714 env->Connect(probe.Get(nIndex));
716 trace.log(_("connected !"));
719 // Success!
720 osync_context_report_success(ctx);
722 trace.log(_("connect success"));
724 // Don't let exceptions escape to the C modules
725 catch( std::bad_alloc &ba ) {
726 osync_context_report_error(ctx, OSYNC_ERROR_NO_CONNECTION,
727 _("Unable to allocate memory for controller: %s"), ba.what());
729 catch( std::exception &e ) {
730 osync_context_report_error(ctx, OSYNC_ERROR_NO_CONNECTION,
731 "%s", e.what());
736 static void contact_get_changes(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, osync_bool slow_sync, void *userdata)
738 Trace trace("contact_get_changeinfo");
740 try {
741 BarryEnvironment *env = (BarryEnvironment *) userdata;
743 GetChanges(sink, info, ctx, env, &env->m_ContactsSync,
744 "Address Book", "contact", "vcard30",
745 &VCardConverter::GetRecordData,
746 slow_sync);
748 // Success!
749 osync_context_report_success(ctx);
752 // don't let exceptions escape to the C modules
753 catch( std::exception &e ) {
754 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
758 static void event_get_changes(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, osync_bool slow_sync, void *userdata)
760 Trace trace("event_get_changeinfo");
762 try {
763 BarryEnvironment *env = (BarryEnvironment *) userdata;
765 GetChanges(sink, info, ctx, env, &env->m_CalendarSync,
766 "Calendar", "event", "vevent20",
767 &VEventConverter::GetRecordData,
768 slow_sync);
770 // Success!
771 osync_context_report_success(ctx);
773 // don't let exceptions escape to the C modules
774 catch( std::exception &e ) {
775 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
780 static void journal_get_changes(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, osync_bool slow_sync, void *userdata)
782 Trace trace("journal_get_changeinfo");
784 try {
785 BarryEnvironment *env = (BarryEnvironment *) userdata;
787 GetChanges(sink, info, ctx, env, &env->m_JournalSync,
788 "Memos", "note", "vjournal",
789 &VJournalConverter::GetRecordData,
790 slow_sync);
792 // Success!
793 osync_context_report_success(ctx);
795 // don't let exceptions escape to the C modules
796 catch( std::exception &e ) {
797 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
802 static void todo_get_changes(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, osync_bool slow_sync, void *userdata)
804 Trace trace("todo_get_changeinfo");
806 try {
807 BarryEnvironment *env = (BarryEnvironment *) userdata;
809 GetChanges(sink, info, ctx, env, &env->m_TodoSync,
810 "Tasks", "todo", "vtodo20",
811 &VTodoConverter::GetRecordData,
812 slow_sync);
814 // Success!
815 osync_context_report_success(ctx);
817 // don't let exceptions escape to the C modules
818 catch( std::exception &e ) {
819 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
824 static void commit_change(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, OSyncChange *change, void *userdata)
826 Trace trace("commit_change");
828 // We can rely on a valid record state table, since get_changeinfo()
829 // will be called first, and will fill the table.
831 try {
833 BarryEnvironment *env = (BarryEnvironment *) userdata;
835 OSyncHashTable *hashtable = osync_objtype_sink_get_hashtable(sink);
837 // find the needed commit function, based on objtype of the change
838 CommitData_t CommitData = GetCommitFunction(change);
839 if( !CommitData ) {
840 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC,
841 _("unable to get commit function pointer"));
842 return;
845 // find the matching cache, state table, and id map for this change
846 DatabaseSyncState *pSync = env->GetSyncObject(change);
847 if( !pSync ) {
848 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC,
849 _("unable to get sync object that matches change type"));
850 return;
853 // is syncing turned on for this type?
854 if( !pSync->m_Sync ) {
855 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC,
856 _("This object type is disabled in the barry-sync config"));
857 return;
860 // make references instead of pointers
861 Barry::RecordStateTable &table = pSync->m_Table;
862 Barry::Mode::Desktop &desktop = *env->GetDesktop();
863 unsigned int dbId = pSync->m_dbId;
866 // either generate or retrieve the record ID, based on type
867 Barry::RecordStateTable::IndexType StateIndex;
868 unsigned long RecordId = 0;
869 if( osync_change_get_changetype(change) == OSYNC_CHANGE_TYPE_ADDED ) {
870 // create new ID for this record
871 RecordId = table.MakeNewRecordId();
873 // tell opensync to save our ID
874 char *puid = g_strdup_printf("%lu", RecordId);
875 osync_change_set_uid(change, puid);
876 g_free(puid);
878 else {
879 // extract RecordId from change's UID,
880 const char *uid = osync_change_get_uid(change);
881 trace.logf(_("uid from change: %s"), uid);
883 // convert existing UID string to RecordId
884 if( strlen(uid) == 0 ||
885 sscanf(uid, "%lu", &RecordId) != 1 ||
886 RecordId == 0)
888 trace.logf(_("Unable to extract a valid record ID from: %s"), uid);
889 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, _("Unable to extract a valid record ID from: %s"), uid);
890 return;
893 // search for the RecordId in the state table, to find the
894 // index... we only need the index if we are deleting or
895 // modifying
896 if( !table.GetIndex(RecordId, &StateIndex) ) {
897 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC,
898 _("unable to get state table index for RecordId: %lu"),
899 RecordId);
900 return;
904 // if we get here, we are about to update the device,
905 // and dirty flags will change in such a way that a
906 // reconnect will be required later... so flag this state
907 env->RequireDirtyReconnect();
910 std::string errmsg;
911 bool status;
913 OSyncData *odata = NULL;
914 char *plain = NULL;
916 switch( osync_change_get_changetype(change) )
918 case OSYNC_CHANGE_TYPE_DELETED:
919 desktop.DeleteRecord(dbId, StateIndex);
920 break;
922 case OSYNC_CHANGE_TYPE_ADDED:
923 odata = osync_change_get_data(change);
924 osync_data_get_data(odata, &plain, NULL);
925 status = (*CommitData)(env, dbId, StateIndex, RecordId,
926 plain, true, errmsg);
927 if( !status ) {
928 trace.logf(_("CommitData() for ADDED state returned false: %s"), errmsg.c_str());
929 osync_context_report_error(ctx, OSYNC_ERROR_PARAMETER, "%s", errmsg.c_str());
930 return;
932 osync_change_set_hash(change, "0");
933 break;
935 case OSYNC_CHANGE_TYPE_MODIFIED:
936 odata = osync_change_get_data(change);
937 osync_data_get_data(odata, &plain, NULL);
938 status = (*CommitData)(env, dbId, StateIndex, RecordId,
939 plain, false, errmsg);
940 if( !status ) {
941 trace.logf(_("CommitData() for MODIFIED state returned false: %s"), errmsg.c_str());
942 osync_context_report_error(ctx, OSYNC_ERROR_PARAMETER, "%s", errmsg.c_str());
943 return;
945 osync_change_set_hash(change, "0");
946 break;
948 default:
949 trace.log(_("Unknown change type"));
950 break;
953 // Update hashtable
954 osync_hashtable_update_change(hashtable, change);
956 // Answer the call
957 osync_context_report_success(ctx);
958 return;
962 catch( std::exception &e ) {
963 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
964 return;
969 static void contact_sync_done(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata)
972 // This function will only be called if the sync was successfull
975 Trace trace("contact_sync_done");
977 try {
979 BarryEnvironment *env = (BarryEnvironment *) userdata;
981 // do cleanup for each database
982 if( FinishSync(ctx, env, &env->m_ContactsSync) )
984 // Success
985 osync_context_report_success(ctx);
989 catch( std::exception &e ) {
990 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
995 static void event_sync_done(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata)
998 // This function will only be called if the sync was successfull
1001 Trace trace("event_sync_done");
1003 try {
1005 BarryEnvironment *env = (BarryEnvironment *) userdata;
1007 // do cleanup for each database
1008 if( FinishSync(ctx, env, &env->m_CalendarSync) )
1010 // Success
1011 osync_context_report_success(ctx);
1015 catch( std::exception &e ) {
1016 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
1021 static void journal_sync_done(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata)
1024 // This function will only be called if the sync was successfull
1027 Trace trace("journal_sync_done");
1029 try {
1031 BarryEnvironment *env = (BarryEnvironment *) userdata;
1033 // do cleanup for each database
1034 if( FinishSync(ctx, env, &env->m_JournalSync) )
1036 // Success
1037 osync_context_report_success(ctx);
1041 catch( std::exception &e ) {
1042 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
1047 static void todo_sync_done(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata)
1050 // This function will only be called if the sync was successfull
1053 Trace trace("todo_sync_done");
1055 try {
1057 BarryEnvironment *env = (BarryEnvironment *) userdata;
1059 // do cleanup for each database
1060 if( FinishSync(ctx, env, &env->m_TodoSync) )
1062 // Success
1063 osync_context_report_success(ctx);
1067 catch( std::exception &e ) {
1068 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
1073 static void disconnect(OSyncObjTypeSink *sink, OSyncPluginInfo *info, OSyncContext *ctx, void *userdata)
1075 Trace trace("contact_disconnect");
1077 // Done!
1078 osync_context_report_success(ctx);
1082 static void finalize(void *data)
1084 Trace trace("finalize");
1086 BarryEnvironment *env = (BarryEnvironment *) data;
1088 // Disconnect the controller, which closes our connection
1089 if (env->isConnected())
1090 env->Disconnect();
1092 delete env;
1096 osync_bool get_sync_info(OSyncPluginEnv *env, OSyncError **error)
1098 Trace trace("get_sync_info");
1100 static bool i18n_initialized = false;
1101 if( !i18n_initialized ) {
1102 // initialize i18n gettext directory
1103 // the rest is done in i18n.h
1104 bindtextdomain(PACKAGE, LOCALEDIR);
1106 i18n_initialized = true;
1109 // Create a new OpenSync plugin
1110 OSyncPlugin *plugin = osync_plugin_new(error);
1111 if( !plugin ) {
1112 trace.log(osync_error_print(error));
1113 return false;
1116 // Describe our plugin
1117 osync_plugin_set_name(plugin, "barry-sync");
1118 osync_plugin_set_longname(plugin, Barry::string_vprintf(_("Barry OpenSync plugin v%s for the Blackberry handheld"), PACKAGE_VERSION).c_str());
1119 osync_plugin_set_description(plugin, _("Plugin to synchronize note, task, calendar and contact entries on USB Blackberry handhelds"));
1121 // Set the callback functions
1122 osync_plugin_set_initialize_func(plugin, initialize);
1123 osync_plugin_set_finalize_func(plugin, finalize);
1124 osync_plugin_set_discover_func(plugin, discover);
1125 osync_plugin_set_start_type(plugin, OSYNC_START_TYPE_PROCESS);
1127 if( !osync_plugin_env_register_plugin(env, plugin, error) ) {
1128 trace.log(osync_error_print(error));
1129 return false;
1132 osync_plugin_unref(plugin);
1134 return true;
1138 int get_version(void)
1140 return 1;