Yes, you can help.
Given the lack of documentation for writing plugins (at least none that I 
could find) I have had to make a few educated guesses about plugin behaviour.

I have taken the time to write up my "findings" as a how-to document. However, 
parts of it could be mistaken. So I'd like the multi-sync dev team to review
my document and make comments. This has two benefits:
(1)  I get assurance my plugin is designed correctly (or not).
(2)  the multisync project gains a peer-reviewed how-to document. 

The document is attached.

Stewart



On Sat, 1 May 2004 06:27 am, Tom Foottit wrote:
> Ok, please let us know if we can help. If you can get things into a
> useable state we can arrange putting the plugin into our CVS tree, etc.
>
> Also a reminder if you are using our CVS then do your development off of
> branch_08X.
>
> Tom
>
> On Tue, 2004-04-27 at 03:04, Stewart Heitmann wrote:
> > OK, I'll keep working on my KDE plugin then.
> > I'll post a follow up to multisync-devel when I get
> > it into a useful state.
> >
> > As for the Gnome framework, I can live with that.
> > I have already had some success talking to the KDE
> > addressbook back-end from within a Multisync plugin.
> > So I think the two should work together ok.
> >
> > As for kitchensync, I tried it once but couldnt get it
> > to compile on my system (FreeBSD) so I gave up.
> > Then I tried multisync,  and it worked fine.
> > Hence my preference for multisync.
> >
> > Stewart
> >
> > On Tue, 27 Apr 2004 10:41 am, Tom Foottit wrote:
> > > Nobody is working on a KDE PIM plugin currently.
> > >
> > > Multisync has a lot of Gnome dependencies in its current incarnation.
> > > Work on a new version (0.90) will break the UI and sync engine
> > > dependencies, but that is some time away. In the meantime you would
> > > have to live in the Gnome framework.
> > >
> > > You should be aware of Kitchensync as well, which is a KDE project
> > > similar to Mulitsync.
> > >
> > > Tom
> > >
> > > On Fri, 2004-04-23 at 02:12, Stewart Heitmann wrote:
> > > > I just discovered multisync recently and was impressed
> > > > by the application, but dismayed by its lack of a KDE plugin.
> > > >
> > > > I really want to use it to sync my KDE addresss book to
> > > > my mobile phone (Siemens ME45).
> > > > So I am considering writing a KDE plugin myself.
> > > > Before I get too far into it I'd like to know if anybody else
> > > > is working on a KDE plugin at the moment?
> > > > I'd prefer to avoid duplication of effort.
Multisync Plugin How-to
-----------------------

The Multisync sync-engine manages the comparison and merging of PIM
objects (VCARDS, VCALENDARS, etc) between pairs of external devices
and/or applications that each contain an internal store of PIM entries.
The internal stores are referred to as "databases", and the external
devices and applications are collectively termed as "devices".
Each pair of devices being synchronized is known as a "sync pair",
and a single device may belong to multiple sync pairs simultaneously.


About plugins
-------------
Every type of "device" requires its own multisync plugin to act as
interface between the sync-engine and the device's internal PIM database.
Note that each plugin may be required to service multiple instances of
identical devices or even multiple instance of the same physical device so
it should not rely on global static data in its implementation.


About UIDs
----------
Each device usually (but not always) assigns a unique identifier (UID)
for each object in its database. The sync-engine uses these UIDs to
match PIM objects during the synchronization process.
If the device does not natively support UIDs then the multisync plugin
should generate a UID for each database object as it deems fit. 

The sync-engine maintains its own internal mappings of the UIDs between
the plugins of each sync pair. It ensures that each plugin is only ever
presented with its own UIDs (not the UIDs of other plugins).
The only exception is for new PIM objects, which the sync-engine presents
to the plugin with a NULL UID. The plugin is expected to assign its own
UID to these objects and report the new UID back to the sync-engine.

In short, the sync-engine isolates the plugin from issues of UID
uniqueness across devices.


Plugin data structure
---------------------
Each plugin defines a struct of the form:

    typedef struct 
        {
        client_connection commondata;

        <private data goes here>

        } myplugin_connection;

This structure is returned by the plugin's sync_connect() function
which is called by the sync-engine whenever the device is added to
a new sync-pair (or whenever a device in a sync-pair is re-connected).
It must be allocated using g_malloc() as the sync-engine will use
g_free() to de-allocate it later.

The sync-engine expects the "client_connection" data member to be the
first element of the struct so be sure not to put anything before it.
The rest of the struct should be used to store all data pertinent
to this instance of the plugin.

The sync-engine will pass a pointer to this structure in all
subsequent callbacks to the plugin. For instance, some of the more
interesting callbacks are:

    void sync_disconnect(myplugin_connection *conn);
    void get_changes(myplugin_connection *conn, sync_object_type newdbs);
    void syncobj_modify_list(myplugin_connection *conn, GList *changes);
    void sync_done(myplugin_connection *conn, gboolean success);

Note the "myplugin_connection *conn" parameters are actually passed by
the sync-engine as "void *" but we pre-cast them in the function
declarations for our own convenience.

See the plugin-API.c file supplied with multisync for the full list of
plugin callbacks.


The get_changes() callback
--------------------------
When the sync-engine wishes to synchronize the devices of a sync-pair, it
first calls the get_changes() callback of each plugin to ascertain which
objects in the device's database have been altered.

    void get_changes(myplugin_connection *conn, sync_object_type newdbs);

The plugin should respond with a list containing copies of those database
objects that have been modified, added, or deleted since the last time the
database was synchronized. That is, since the last time the sync-engine
called the plugin's sync_done() callback.

Alternatively, the sync-engine will set the appropriate bit
(SYNC_OBJECT_TYPE_CALENDAR, SYNC_OBJECT_TYPE_PHONEBOOK, SYNC_OBJECT_TYPE_TODO)
in the "newdbs" parameter if it wishes the plugin to return all objects in its
database rather than only those that have changed. This will happen when the
other device in the sync-pair has lost all of its data and needs to be
refreshed from scratch.

The list of changes must be accumulated as GList of changed_object structs

    typedef struct
        {
        char *comp;    // The PIM object data in VCARD or VCALENDAR string format.
        char *uid;     // The plugins unique ID for this PIM object.
        char *removepriority;  // Word to sort on for removal when database is full.
        int change_type;       // One of SYNC_OBJ_MODIFIED, SYNC_OBJ_ADDED, etc
        sync_object_type object_type;  //The data type of this object
        } changed_object;

each one allocated with g_malloc(). Furthermore, this list must be encapsulated
in a change_info struct, which incidentally must also be allocated using g_malloc().

    typedef struct
        {
        GList *changes;          // List of changed_object's
        sync_object_type newdbs; // Set the bit for the corresponding type
                                 // if the database is not recognized or has been 
reset.
        } change_info;

Once the change_info structure is complete, it is returned to the sync-engine
using sync_set_requestdata() function.

When constructing changed_object structs for ADDED and MODIFIED database objects, the
"changed_object.comp" data member should point to a newly gmalloc'ed string containing
the data for the object. The sync-engine uses this data when comparing and
merging modified objects from either device in the sync-pair. The string should be
formatted as a VCARD or VCALENDAR string as appropriate to its object type.
For example, a vcard would appear as 
     "BEGIN:VCARD\nEMAIL;TYPE=PREF:[EMAIL PROTECTED]:Foo Bar\nN:Bar;Foo;;;\n
     VERSION:3.0\nEND:VCARD\n"
Objects marked as DELETE however may have "changed_object.comp" set to NULL.

The "changed_object.uid" data member should always point to a newly gmalloc'ed
string containing the UID assigned to that object by the device. This is
regardless of whether the object is marked as ADDED, MODIFIED, or DELETE.

QUESTION TO THE MULTI-SYNC DEVELOPMENT TEAM:
    How can a plugin know the state (MODIFIED,ADDED,DELETED) of each object on
    the device unless the plugin keeps its own persistent record of each object stored
    on the device at the time of the previous synchronisation?
    The device's per-object revision dates (if it supports them) can only assist
    with identifying MODIFIED and ADDED records but they can never identify DELETED
    records because they don't exist anymore!
    The only way is if each plugin maintains its own persistent database of the
    objects stored on the device (or their UIDs at very least).

    Is this true or have I misunderstood?  If true, it seems a rather onerous task
    to expect of each plugin developer. Especially since it is a job which could be
    easily done by the sync engine as it already maintains persistent stores of the
    UIDs in each syncpair's ids file.


The syncobj_modify_list() callback
----------------------------------
Once the sync-engine has determined the differences between the two devices of a sync
pair it calls syncobj_modify_list() with a list of changes for the plugin to replicate
on the device.

    void syncobj_modify_list(kdepim_connection *conn, GList *changes)

Each item in the "changes" list is a "changed_object" struct (as seen above).
As the plugin applies these changes to the device it should accumulate a list of
"syncobj_modify_result" structs in which to record the success of each
change. This list is returned to the sync-engine with the sync_set_requestdata()
function upon completion of all modifications.

    typedef struct
        {
        sync_msg_type result;  // The result of the operation
        char *returnuid;       // Returns UID for a new entry as a gmalloc'ed string.
        } syncobj_modify_result;

Each object in the "changes" list whose "change_type" is SYNC_OBJ_HARDDELETED or
SYNC_OBJ_SOFTDELETED can simply be deleted from the device.

For those objects whose "change_type" is SYNC_OBJ_MODIFIED, the corresponding object
(according to UID) on the device should have their contents replaced with the contents
of the changed_object's "comp" string. 

Similarly, those objects whose "change_type" is SYNC_OBJ_ADDED use the data in the
"comp" string as the new record, however in this case the device will generate a UID
for the new object. The plugin should inform the sync-engine of the new UID by 
returning
a gmalloc'ed copy of it in the changed_object's "returnuid" variable. The "returnuid"
variable should be set to NULL for other types of changes.

Reply via email to