wiki:devel/pluginPortingGuide-0.40
Last modified 4 years ago Last modified on 08/18/10 11:00:49

A Brief HOWTO on Porting Out of Date OpenSync Plugins to the Latest API

This article documents some of the steps I took to update the google-calendar plugin to the latest opensync API.

Disclaimer: I am no opensync expert, and you read this document at your own risk. But hopefully it will give you a head start on doing any porting.

1) Compile with warnings
------------------------

Most useful thing to do, right away, is to setup your cmake properly,
for maximum pain.... I mean, turn on all warnings. :-)

This is the script I run to setup my opensync and plugin builds:

------------------------------------------------------------------------------
#!/bin/sh

mkdir build
cd build
cmake -Wdev \
	-DCMAKE_INSTALL_PREFIX=/home/cdfrey/software/opensync/git/rootdir \
	-DCMAKE_C_COMPILER:STRING=/usr/local/bin/ccache_gcc \
	-DCMAKE_CXX_COMPILER:STRING=/usr/local/bin/ccache_g++ \
	-DCMAKE_C_FLAGS:STRING="-Wall -Werror -O0" \
	-DCMAKE_CXX_FLAGS:STRING="-Wall -Werror -O0" \
	-DCMAKE_C_FLAGS_RELWITHDEBINFO:STRING="-g" \
	-DCMAKE_VERBOSE_MAKEFILE:BOOL=TRUE \
	..

echo "You can now run: cd build ; make ; make install"

------------------------------------------------------------------------------


2) OSyncLists need to be freed
------------------------------

The latest API makes a copy of all OSyncLists that it returns.  This means
that you must call osync_list_free() on all such lists yourself.


3) Sink pointers do not need to be stored anymore
-------------------------------------------------

Older plugins would create the sinks for each objtype they would support,
and then store the pointers of those sinks in their environment data,
or would fetch it through the plugin info pointer passed to the callbacks.

Neither is necessary anymore, since the sink pointer is passed in to the
callbacks.  This will require you to update your callback prototypes,
and change any code that used to call osync_plugin_info_get_sink(info),
which is obsolete.


4) New error handling
---------------------

The latest API makes heavy use of OSyncError, and it is the owner's
responsibility to clean this up.

The first example is if you call a function that uses the error pointer.
This is the logic you will need:

	OSyncError *error = NULL;
	if( !osync_some_error_returning_function(info, &error) ) {
		osync_trace(OSYNC_ERROR, "Can't load! %s",
			osync_error_print(&error));
		osync_error_unref(&error);
		return FALSE;
	}

The second example is if you are coding a function that accepts an OSyncError**
pointer itself.  The main function get_sync_info() is like this.  For example:

	osync_bool get_sync_info(OSyncPluginEnv *env, OSyncError **error)
	{
		OSyncPlugin *plugin = osync_plugin_new(error);
		if( !plugin )
			goto error;

		osync_plugin_set_name(plugin, "google-data");
		osync_plugin_set_longname(plugin, "Google calendar/plugin");
		osync_plugin_set_description(plugin,
					"Google calendar and contacts plugin");

		osync_plugin_set_initialize(plugin, gc_initialize);
		osync_plugin_set_finalize(plugin, gc_finalize);
		osync_plugin_set_discover(plugin, gc_discover);

		if( !osync_plugin_env_register_plugin(env, plugin, error) )
			goto error;
		osync_plugin_unref(plugin);

		osync_trace(TRACE_EXIT, "%s", __func__);
		return TRUE;

	error:
		osync_trace(TRACE_EXIT_ERROR, "Unable to register: %s",
				osync_error_print(error));
		return FALSE;
	}

Note that the error is not freed here, since it is passed back to the
caller of get_sync_info(), who has the responsibility to free it.


5) Authentication has its own API
---------------------------------

Instead of looping through resources, you can fetch the username and
password configuration options through the following APIs:

       OSyncPluginAuthentication *optauth = NULL;
       optauth = osync_plugin_config_get_authentication(config);
       if( osync_plugin_authentication_option_is_supported(optauth,
                               OSYNC_PLUGIN_AUTHENTICATION_USERNAME) ) {
               const char *user =
                       osync_plugin_authentication_get_username(optauth);
               if( !user )
                       goto error_freeplg;

and the same for password.


6) Callback registration: from struct to individual functions
-------------------------------------------------------------

The old style of registering your plugin sink callbacks was through
the setup of a struct bundle, like so:

	OSyncObjTypeSinkFunctions functions_gcont;
	memset(&functions_gcont, 0, sizeof(functions_gcont));
	functions_gcont.connect = gc_connect;
	functions_gcont.get_changes = gc_get_changes_contact;
	functions_gcont.commit = gc_commit_change_contact;
	functions_gcont.disconnect = gc_disconnect;
	functions_gcont.sync_done = gc_sync_done;

	sync_objtype_sink_set_functions(sink, functions_gcont, envdata);
	osync_plugin_info_add_objtype(info, sink);


This is now done with individual function calls:

	osync_objtype_sink_set_connect_func(sink, gc_connect_contact);
	osync_objtype_sink_set_disconnect_func(sink, gc_disconnect);
	osync_objtype_sink_set_get_changes_func(sink, gc_get_changes_contact);
	osync_objtype_sink_set_commit_func(sink, gc_commit_change_contact);
	osync_objtype_sink_set_sync_done_func(sink, gc_sync_done);

	osync_objtype_sink_set_userdata(sink, plgdata);

Adding the objtype to the plugin info via osync_plugin_info_add_objtype()
is not required anymore.

Note that you probably want to make all this registration conditional on
whether the sink is enabled or not in your configuration, like this:

	OSyncObjTypeSink *sink = osync_plugin_info_find_objtype(info,"contact");
	if( sink && osync_objtype_sink_is_enabled(sink) ) {
		// do registration
	}



7) New discover() step
----------------------

There is a new step in the latest API called discover, which gives the
plugin a chance to report what it supports.  I don't fully understand
the potential of this stage, but I do know that it is mixture of
plugin configuration and plugin abilities.

The easiest way to code this is to enable all sinks that your plugin
supports.  These sinks are the ones you just registered above.

       OSyncList *sinks = osync_plugin_info_get_objtype_sinks(info);
       OSyncList *s = sinks;
       for( ; s; s = s->next ) {
               OSyncObjTypeSink *sink = (OSyncObjTypeSink*) s->data;
               osync_objtype_sink_set_available(sink, TRUE);
       }


8) Anchor API has been renamed to state_db
------------------------------------------

The old anchor API has been renamed to state_db, and mostly this is a
simple straight renaming of:

	osync_objtype_sink_get_anchor()
	osync_anchor_retrieve()
	osync_anchor_update()
	osync_objtype_sink_enable_anchor()

to (respectively):

	osync_objtype_sink_get_state_db()
	osync_sink_state_get()
	osync_sink_state_set()
	osync_objtype_sink_enable_state_db()

The only difference is that the get() and set() functions now take a
key name.  This can be anything, but it must be consistent.  Think of it
like a Berkeley DB get/set, and you need to use the same key you use
for saving when you retrieve again.

I just use hard coded strings here, like "contact_timestamp", etc.


9) Slow sync is now an argument, instead of a function call
-----------------------------------------------------------

Instead of:

	if (osync_objtype_sink_get_slowsync(plgdata->gcal_sink)) {

use the slow_sync bool variable passed into the new callback prototype:

	if (slow_sync) {


10) List looping is more standard
---------------------------------

In the old API, you would get functions that would return the total
number of items in a list, and a means to get the nth item.

In the new API, it is more encouraged to access these things via
OSyncLists, which are standard ways of looping through lists:

	OSyncList *s = NULL, *list = osync_plugin_info_get_objtype_sinks(info);
	for( s = list; s; s = s->next ) {
		OSyncObjTypeSink *sink = (OSyncObjTypeSink*) s->data;
		// do work...
	}

This replaces functions such as osync_plugin_info_num_objtypes() and
osync_plugin_info_nth_objtype() which are obsolete.


11) Some headers may have changed
---------------------------------

Most of these errors you will find during the compilation, but
headers such as opensync-context.h are obsolete.  Search through
the opensync include directory for the latest headers.



If all else fails, post a plea for help to the opensync-devel mailing list.

You will find the old porting guide from 0.2 to 0.3 here: pluginPortingGuide-0.30