desktop: added timezone list to CalendarEditDlg
[barry/progweb.git] / opensync-plugin / src / barry_sync.cc
blob2be7391a8b5b807d2f599b0fa237c8c6e3487d2d
1 //
2 // \file barry_sync.cc
3 // Opensync module for the USB Blackberry handheld
4 //
6 /*
7 Copyright (C) 2006-2012, Net Direct Inc. (http://www.netdirect.ca/)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
22 #include <opensync/opensync.h>
23 #include <barry/barry.h>
24 #include <barry/dll.h>
25 #include "barry_sync.h"
26 #include "environment.h"
27 #include "vevent.h"
28 #include "vcard.h"
29 #include "trace.h"
30 #include <string>
31 #include <glib.h>
32 #include <stdint.h>
33 #include <stdlib.h>
34 #include <string.h>
36 // All functions that are callable from outside must look like C
37 extern "C" {
38 static void *initialize(OSyncMember *member, OSyncError **error);
39 static void connect(OSyncContext *ctx);
40 static void get_changeinfo(OSyncContext *ctx);
41 static void sync_done(OSyncContext *ctx);
42 static void disconnect(OSyncContext *ctx);
43 static void finalize(void *data);
44 BXEXPORT void get_info(OSyncEnv *env);
48 //////////////////////////////////////////////////////////////////////////////
50 // Support functions and classes
54 void GetChanges(OSyncContext *ctx, BarryEnvironment *env,
55 DatabaseSyncState *pSync,
56 const char *DBDBName,
57 const char *ObjTypeName, const char *FormatName,
58 GetData_t getdata)
60 Trace trace("GetChanges");
62 // shortcut references
63 using namespace Barry;
64 using Barry::RecordStateTable;
65 Mode::Desktop &desktop = *env->GetDesktop();
67 // find the matching cache, state table, and id map for this change
68 DatabaseSyncState::cache_type &cache = pSync->m_Cache;
69 idmap &map = pSync->m_IdMap;
71 // check if slow sync has been requested, and if so, empty the
72 // cache and id map and start fresh
73 if( osync_member_get_slow_sync(env->member, ObjTypeName) ) {
74 trace.log("GetChanges: slow sync request detected, clearing cache and id map");
75 cache.clear();
76 map.clear();
79 // fetch state table
80 unsigned int dbId = desktop.GetDBID(DBDBName);
81 RecordStateTable &table = pSync->m_Table;
82 desktop.GetRecordStateTable(dbId, table);
84 // cycle through the state table...
85 // - if not in cache, it is added.
86 // - if in cache, check Blackberry's dirty flag
87 RecordStateTable::StateMapType::const_iterator i = table.StateMap.begin();
88 for( ; i != table.StateMap.end(); ++i ) {
90 OSyncChange *change = 0;
91 const RecordStateTable::IndexType &index = i->first;
92 const RecordStateTable::State &state = i->second;
94 // search the idmap for the UID
95 std::string uid = pSync->Map2Uid(state.RecordId);
97 // search the cache
98 DatabaseSyncState::cache_type::const_iterator c = cache.find(state.RecordId);
99 if( c == cache.end() ) {
100 // not in cache, this is a new item
101 trace.log("found an ADDED change");
102 change = osync_change_new();
103 osync_change_set_changetype(change, CHANGE_ADDED);
105 else {
106 // in the cache... dirty?
107 if( state.Dirty ) {
108 // modified
109 trace.log("found a MODIFIED change");
110 change = osync_change_new();
111 osync_change_set_changetype(change, CHANGE_MODIFIED);
113 else {
114 trace.log("no change detected");
118 // finish filling out the change object
119 if( change ) {
120 osync_change_set_member(change, env->member);
121 osync_change_set_objformat_string(change, FormatName);
123 osync_change_set_uid(change, uid.c_str());
124 trace.logf("change record ID: %s", uid.c_str());
126 // Now you can set the data for the object
127 // Set the last argument to FALSE if the real data
128 // should be queried later in a "get_data" function
129 char *data = (*getdata)(env, dbId, index);
130 osync_change_set_data(change, data, strlen(data), TRUE);
132 // just report the change via
133 osync_context_report_change(ctx, change);
135 // map our IDs for later
136 map.Map(uid, state.RecordId);
140 // now cycle through the cache... any objects in the cache
141 // but not found in the state table means that they have been
142 // deleted in the device
143 DatabaseSyncState::cache_type::const_iterator c = cache.begin();
144 for( ; c != cache.end(); ++c ) {
145 uint32_t recordId = c->first;
147 // search the idmap for the UID
148 std::string uid = pSync->Map2Uid(recordId);
150 // search the state table
151 i = table.StateMap.begin();
152 for( ; i != table.StateMap.end(); ++i ) {
154 if( i->second.RecordId == recordId )
155 break; // found
158 // check if not found...
159 if( i == table.StateMap.end() ) {
160 // register a DELETE, no data
161 trace.log("found DELETE change");
163 OSyncChange *change = osync_change_new();
164 osync_change_set_changetype(change, CHANGE_DELETED);
165 osync_change_set_member(change, env->member);
166 osync_change_set_objformat_string(change, FormatName);
168 osync_change_set_uid(change, uid.c_str());
169 trace.log(uid.c_str());
171 // report the change
172 osync_context_report_change(ctx, change);
176 // finally, cycle through the state map again, and overwrite the
177 // cache with the current state table. Memory only... if successful,
178 // it will be written back to disk later on.
180 // start fresh
181 cache.clear();
183 for( i = table.StateMap.begin(); i != table.StateMap.end(); ++i ) {
184 const RecordStateTable::State &state = i->second;
185 cache[state.RecordId] = false;
189 CommitData_t GetCommitFunction(OSyncChange *change)
191 OSyncObjType *type = osync_change_get_objtype(change);
192 const char *name = osync_objtype_get_name(type);
193 if( strcmp(name, "event") == 0 ) {
194 return &VEventConverter::CommitRecordData;
196 else if( strcmp(name, "contact") == 0 ) {
197 return &VCardConverter::CommitRecordData;
199 else {
200 return 0;
204 bool FinishSync(OSyncContext *ctx, BarryEnvironment *env, DatabaseSyncState *pSync)
206 Trace trace("FinishSync()");
208 if( !pSync->m_Sync ) {
209 // this mode is disabled in config, skip
210 return true;
213 Barry::Mode::Desktop &desktop = *env->GetDesktop();
215 // get the state table again, so we can update
216 // the cache properly
217 desktop.GetRecordStateTable(pSync->m_dbId, pSync->m_Table);
219 // update the cache
220 if( !pSync->SaveCache() ) {
221 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR,
222 "Error saving calendar cache");
223 return false;
226 // save the id map
227 pSync->CleanupMap();
228 if( !pSync->SaveMap() ) {
229 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR,
230 "Error saving calendar id map");
231 return false;
234 // clear all dirty flags in device
235 env->ClearDirtyFlags(pSync->m_Table, pSync->m_dbName);
236 return true;
241 //////////////////////////////////////////////////////////////////////////////
243 // OpenSync API
246 static void *initialize(OSyncMember *member, OSyncError **error)
248 Trace trace("initialize");
250 BarryEnvironment *env = 0;
252 // Create the environment struct, including our Barry objects
253 try {
254 env = new BarryEnvironment(member);
256 // Load config file for this plugin
257 char *configdata;
258 int configsize;
259 if (!osync_member_get_config(member, &configdata, &configsize, error)) {
260 osync_error_update(error, "Unable to get config data: %s",
261 osync_error_print(error));
262 delete env;
263 return NULL;
266 // Process the configdata here and set the options on your environment
267 env->ParseConfig(configdata, configsize);
268 free(configdata);
270 // FIXME - near the end of release, do a run with
271 // this set to true, and look for USB protocol
272 // inefficiencies.
273 Barry::Init(env->m_DebugMode);
275 // Load all needed cache files
276 if( env->m_CalendarSync.m_Sync ) {
277 env->m_CalendarSync.LoadCache();
278 env->m_CalendarSync.LoadMap();
281 if( env->m_ContactsSync.m_Sync ) {
282 env->m_ContactsSync.LoadCache();
283 env->m_ContactsSync.LoadMap();
286 return env;
289 // Don't let C++ exceptions escape to the C code
290 catch( std::bad_alloc &ba ) {
291 osync_error_update(error, "Unable to allocate memory for environment: %s", ba.what());
292 delete env;
293 return NULL;
295 catch( std::exception &e ) {
296 osync_error_update(error, "%s", e.what());
297 delete env;
298 return NULL;
302 static void connect(OSyncContext *ctx)
304 Trace trace("connect");
306 try {
308 // Each time you get passed a context (which is used to track
309 // calls to your plugin) you can get the data your returned in
310 // initialize via this call:
311 BarryEnvironment *env = (BarryEnvironment *)osync_context_get_plugin_data(ctx);
313 // Probe for available devices
314 Barry::Probe probe;
315 int nIndex = probe.FindActive(env->m_pin);
316 if( nIndex == -1 ) {
317 osync_context_report_error(ctx, OSYNC_ERROR_NO_CONNECTION, "Unable to find PIN %lx", env->m_pin);
318 return;
321 env->Connect(probe.Get(nIndex));
323 // Success!
324 osync_context_report_success(ctx);
327 // Don't let exceptions escape to the C modules
328 catch( std::bad_alloc &ba ) {
329 osync_context_report_error(ctx, OSYNC_ERROR_INITIALIZATION,
330 "Unable to allocate memory for controller: %s", ba.what());
332 catch( std::exception &e ) {
333 osync_context_report_error(ctx, OSYNC_ERROR_INITIALIZATION,
334 "%s", e.what());
338 static void get_changeinfo(OSyncContext *ctx)
340 Trace trace("get_changeinfo");
342 try {
344 BarryEnvironment *env = (BarryEnvironment *)osync_context_get_plugin_data(ctx);
345 OSyncMember *member = osync_context_get_member(ctx);
347 if( env->m_CalendarSync.m_Sync && osync_member_objtype_enabled(member, "event") ) {
348 GetChanges(ctx, env, &env->m_CalendarSync,
349 "Calendar", "event", "vevent20",
350 &VEventConverter::GetRecordData);
353 if( env->m_ContactsSync.m_Sync && osync_member_objtype_enabled(member, "contact") ) {
354 GetChanges(ctx, env, &env->m_ContactsSync,
355 "Address Book", "contact", "vcard30",
356 &VCardConverter::GetRecordData);
359 // Success!
360 osync_context_report_success(ctx);
362 // don't let exceptions escape to the C modules
363 catch( std::exception &e ) {
364 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
368 static osync_bool commit_change(OSyncContext *ctx, OSyncChange *change)
370 Trace trace("commit_change");
372 // We can rely on a valid record state table, since get_changeinfo()
373 // will be called first, and will fill the table.
375 try {
377 BarryEnvironment *env = (BarryEnvironment *)osync_context_get_plugin_data(ctx);
379 // find the needed commit function, based on objtype of the change
380 CommitData_t CommitData = GetCommitFunction(change);
381 if( !CommitData ) {
382 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC,
383 "unable to get commit function pointer");
384 return false;
387 // find the matching cache, state table, and id map for this change
388 DatabaseSyncState *pSync = env->GetSyncObject(change);
389 if( !pSync ) {
390 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC,
391 "unable to get sync object that matches change type");
392 return false;
395 // is syncing turned on for this type?
396 if( !pSync->m_Sync ) {
397 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC,
398 "This object type is disabled in the barry-sync config");
399 return false;
402 // make references instead of pointers
403 DatabaseSyncState::cache_type &cache = pSync->m_Cache;
404 Barry::RecordStateTable &table = pSync->m_Table;
405 idmap &map = pSync->m_IdMap;
406 Barry::Mode::Desktop &desktop = *env->GetDesktop();
407 unsigned int dbId = pSync->m_dbId;
410 // extract RecordId from change's UID,
411 // and update the ID map if necessary
412 const char *uid = osync_change_get_uid(change);
413 trace.logf("uid from change: %s", uid);
414 if( strlen(uid) == 0 ) {
415 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC,
416 "uid from change object is blank!");
418 unsigned long RecordId = pSync->GetMappedRecordId(uid);
420 // search for the RecordId in the state table, to find the
421 // index... we only need the index if we are deleting or
422 // modifying
423 Barry::RecordStateTable::IndexType StateIndex;
424 if( osync_change_get_changetype(change) != CHANGE_ADDED ) {
425 if( !table.GetIndex(RecordId, &StateIndex) ) {
426 osync_context_report_error(ctx, OSYNC_ERROR_GENERIC,
427 "unable to get state table index for RecordId: %lu",
428 RecordId);
429 return false;
433 std::string errmsg;
434 bool status;
436 switch( osync_change_get_changetype(change) )
438 case CHANGE_DELETED:
439 desktop.DeleteRecord(dbId, StateIndex);
440 cache.erase(RecordId);
441 map.UnmapUid(uid);
442 break;
444 case CHANGE_ADDED:
445 status = (*CommitData)(env, dbId, StateIndex, RecordId,
446 osync_change_get_data(change), true, errmsg);
447 if( !status ) {
448 trace.logf("CommitData() for ADDED state returned false: %s", errmsg.c_str());
449 osync_context_report_error(ctx, OSYNC_ERROR_PARAMETER, "%s", errmsg.c_str());
450 map.UnmapUid(uid);
451 return false;
453 cache[RecordId] = false;
454 break;
456 case CHANGE_MODIFIED:
457 status = (*CommitData)(env, dbId, StateIndex, RecordId,
458 osync_change_get_data(change), false, errmsg);
459 if( !status ) {
460 trace.logf("CommitData() for MODIFIED state returned false: %s", errmsg.c_str());
461 osync_context_report_error(ctx, OSYNC_ERROR_PARAMETER, "%s", errmsg.c_str());
462 map.UnmapUid(uid);
463 return false;
465 break;
467 default:
468 trace.log("Unknown change type");
469 osync_debug("barry-sync", 0, "Unknown change type");
470 break;
473 // Answer the call
474 osync_context_report_success(ctx);
475 return true;
479 catch( std::exception &e ) {
480 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
482 // we don't worry about unmapping ids here, as there
483 // is still a possibility that the record was added...
484 // plus, the map might not get written out to disk anyway
485 // in a plugin error state
487 return false;
491 static void sync_done(OSyncContext *ctx)
494 // This function will only be called if the sync was successfull
497 Trace trace("sync_done");
499 try {
501 BarryEnvironment *env = (BarryEnvironment *)osync_context_get_plugin_data(ctx);
503 // we reconnect to the device here, since dirty flags
504 // for records we've just touched do not show up until
505 // a disconnect... as far as I can tell.
506 env->Reconnect();
508 // do cleanup for each database
509 if( FinishSync(ctx, env, &env->m_CalendarSync) &&
510 FinishSync(ctx, env, &env->m_ContactsSync) )
512 // Success
513 osync_context_report_success(ctx);
517 catch( std::exception &e ) {
518 osync_context_report_error(ctx, OSYNC_ERROR_IO_ERROR, "%s", e.what());
522 static void disconnect(OSyncContext *ctx)
524 Trace trace("disconnect");
526 // Disconnect the controller, which closes our connection
527 BarryEnvironment *env = (BarryEnvironment *)osync_context_get_plugin_data(ctx);
528 env->Disconnect();
530 // Done!
531 osync_context_report_success(ctx);
534 static void finalize(void *data)
536 Trace trace("finalize");
538 BarryEnvironment *env = (BarryEnvironment *)data;
539 delete env;
542 void get_info(OSyncEnv *env)
544 Trace trace("get_info");
546 // Create first plugin
547 OSyncPluginInfo *info = osync_plugin_new_info(env);
549 info->name = "barry-sync";
550 info->longname = "Barry OpenSync plugin v0.18.0 for the Blackberry handheld";
551 info->description = "Plugin to synchronize calendar and contact entries on USB Blackberry handhelds";
552 info->version = 1; // API version (opensync api?)
553 info->is_threadsafe = TRUE;
555 info->functions.initialize = initialize;
556 info->functions.connect = connect;
557 info->functions.sync_done = sync_done;
558 info->functions.disconnect = disconnect;
559 info->functions.finalize = finalize;
560 info->functions.get_changeinfo = get_changeinfo;
562 // If you like, you can overwrite the default timeouts of your plugin
563 // The default is set to 60 sec. Note that this MUST NOT be used to
564 // wait for expected timeouts (Lets say while waiting for a webserver).
565 // you should wait for the normal timeout and return a error.
566 // info->timeouts.connect_timeout = 5;
567 // There are more timeouts for the other functions
570 // Register each supported feature
573 // Calendar entries, using batch commit
574 osync_plugin_accept_objtype(info, "event");
575 osync_plugin_accept_objformat(info, "event", "vevent20", NULL);
576 osync_plugin_set_commit_objformat(info, "event", "vevent20",
577 commit_change);
579 // Address Book entries
580 osync_plugin_accept_objtype(info, "contact");
581 osync_plugin_accept_objformat(info, "contact", "vcard30", NULL);
582 osync_plugin_set_commit_objformat(info, "contact", "vcard30",
583 commit_change);