[RFC][PATCH] OSYNC_START_TYPE_EXTERNAL

View: New views
7 Messages — Rating Filter:   Alert me  

[RFC][PATCH] OSYNC_START_TYPE_EXTERNAL

by Henrik /KaarPoSoft-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Dear all,

To my surprise, I have received only ONE comment (thank you Graham!) to
my recent postings about OSYNC_START_TYPE_EXTERNAL.

Please let me know if anybody objects to me checking in the attached
combined patch for all the issues I have raised about
OSYNC_START_TYPE_EXTERNAL so far.

/Henrik


Index: opensync/plugin/opensync_plugin_private.h
===================================================================
--- opensync/plugin/opensync_plugin_private.h (revision 5882)
+++ opensync/plugin/opensync_plugin_private.h (working copy)
@@ -53,6 +53,8 @@
  char *longname;
  /** A short description what the plugin does */
  char *description;
+ /** A command to be executed for external plugins */
+ char *external_command;
  /** The function to initialize the plugin. */
  initialize_fn initialize;
  /** The function to finalize the plugin. The input will be the output of the initialize function */
Index: opensync/plugin/opensync_plugin.c
===================================================================
--- opensync/plugin/opensync_plugin.c (revision 5882)
+++ opensync/plugin/opensync_plugin.c (working copy)
@@ -66,6 +66,9 @@
  if (plugin->description)
  osync_free(plugin->description);
 
+ if (plugin->external_command)
+ osync_free(plugin->external_command);
+
  osync_free(plugin);
  }
 }
@@ -112,6 +115,20 @@
  plugin->description = osync_strdup(description);
 }
 
+const char *osync_plugin_get_start_external_command(OSyncPlugin *plugin)
+{
+ osync_assert(plugin);
+ return plugin->external_command;
+}
+
+void osync_plugin_set_start_external_command(OSyncPlugin *plugin, const char *external_command)
+{
+ osync_assert(plugin);
+ if (plugin->external_command)
+ osync_free(plugin->external_command);
+ plugin->external_command = osync_strdup(external_command);
+}
+
 void *osync_plugin_get_data(OSyncPlugin *plugin)
 {
  osync_assert(plugin);
Index: opensync/plugin/opensync_plugin.h
===================================================================
--- opensync/plugin/opensync_plugin.h (revision 5882)
+++ opensync/plugin/opensync_plugin.h (working copy)
@@ -201,7 +201,29 @@
  */
 OSYNC_EXPORT void osync_plugin_set_description(OSyncPlugin *plugin, const char *description);
 
+/** @brief Returns the external command of a plugin
+ *
+ * @param plugin Pointer to the plugin
+ * @returns External command of the plugin
+ *
+ */
+OSYNC_EXPORT const char *osync_plugin_get_start_external_command(OSyncPlugin *plugin);
 
+/** @brief Sets the external command of a plugin
+ *
+ * If the plugin is of type OSYNC_START_TYPE_EXTERNAL, an external command can be executed by OpenSync.
+ * The external_command should be a string in printf format, with one %s.
+ * Before the command is executed, a variant of printf will be called
+ * to replace the %s with the path to the plugin pipe.
+ * The resulting command will be exectued with the glib function
+ * g_spawn_command_line_async.
+ *
+ * @param plugin Pointer to the plugin
+ * @param external_command the external command to set
+ *
+ */
+OSYNC_EXPORT void osync_plugin_set_start_external_command(OSyncPlugin *plugin, const char *external_command);
+
 /** @brief Sets the initialize function for a plugin
  *
  * The initialize function of a plugin sets up sinks for the plugin as well
Index: opensync/engine/opensync_engine.c
===================================================================
--- opensync/engine/opensync_engine.c (revision 5882)
+++ opensync/engine/opensync_engine.c (working copy)
@@ -773,7 +773,11 @@
  osync_client_proxy_set_context(proxy, engine->context);
  osync_client_proxy_set_change_callback(proxy, _osync_engine_receive_change, engine);
 
- if (!osync_client_proxy_spawn(proxy, osync_plugin_get_start_type(plugin), osync_member_get_configdir(member), error))
+ const char *external_command=NULL;
+ if (osync_plugin_get_start_type(plugin)==OSYNC_START_TYPE_EXTERNAL)
+ external_command=osync_plugin_get_start_external_command(plugin);
+
+ if (!osync_client_proxy_spawn(proxy, osync_plugin_get_start_type(plugin), osync_member_get_configdir(member), external_command, error))
  goto error_free_proxy;
 
  engine->busy = TRUE;
Index: opensync/client/opensync_client.h
===================================================================
--- opensync/client/opensync_client.h (revision 5882)
+++ opensync/client/opensync_client.h (working copy)
@@ -25,6 +25,9 @@
 OSYNC_EXPORT OSyncClient *osync_client_ref(OSyncClient *client);
 OSYNC_EXPORT void osync_client_unref(OSyncClient *client);
 
+OSYNC_EXPORT void osync_client_set_overriding_plugin_dir(OSyncClient *client, char *overriding_plugin_dir);
+OSYNC_EXPORT char *osync_client_get_overriding_plugin_dir(OSyncClient *client);
+
 OSYNC_EXPORT osync_bool osync_client_set_incoming_queue(OSyncClient *client, OSyncQueue *incoming, OSyncError **error);
 OSYNC_EXPORT osync_bool osync_client_set_outgoing_queue(OSyncClient *client, OSyncQueue *outgoing, OSyncError **error);
 
Index: opensync/client/opensync_client_proxy_internals.h
===================================================================
--- opensync/client/opensync_client_proxy_internals.h (revision 5882)
+++ opensync/client/opensync_client_proxy_internals.h (working copy)
@@ -44,7 +44,7 @@
 void osync_client_proxy_set_change_callback(OSyncClientProxy *proxy, change_cb cb, void *userdata);
 OSyncMember *osync_client_proxy_get_member(OSyncClientProxy *proxy);
 
-OSYNC_TEST_EXPORT osync_bool osync_client_proxy_spawn(OSyncClientProxy *proxy, OSyncStartType type, const char *path, OSyncError **error);
+OSYNC_TEST_EXPORT osync_bool osync_client_proxy_spawn(OSyncClientProxy *proxy, OSyncStartType type, const char *path, const char* external_command, OSyncError **error);
 OSYNC_TEST_EXPORT osync_bool osync_client_proxy_shutdown(OSyncClientProxy *proxy, OSyncError **error);
 
 OSYNC_TEST_EXPORT osync_bool osync_client_proxy_initialize(OSyncClientProxy *proxy, initialize_cb callback, void *userdata, const char *formatdir, const char *plugindir, const char *plugin, const char *groupname, const char *configdir, OSyncPluginConfig *config, OSyncError **error);
Index: opensync/client/opensync_client_private.h
===================================================================
--- opensync/client/opensync_client_private.h (revision 5882)
+++ opensync/client/opensync_client_private.h (working copy)
@@ -32,6 +32,7 @@
  OSyncPluginInfo *plugin_info;
  OSyncPluginEnv *plugin_env;
  OSyncFormatEnv *format_env;
+ char *overriding_plugin_dir;
  void *plugin_data;
  OSyncThread *thread;
 };
Index: opensync/client/opensync_client.c
===================================================================
--- opensync/client/opensync_client.c (revision 5882)
+++ opensync/client/opensync_client.c (working copy)
@@ -684,7 +684,14 @@
  osync_queue_unref(outgoing);
  osync_trace(TRACE_INTERNAL, "done connecting to engine");
  }
-
+
+ char *overriding_plugin_dir = osync_client_get_overriding_plugin_dir(client);
+ osync_trace(TRACE_INTERNAL, "overriding_plugin_dir=[%s]", overriding_plugin_dir);
+ if (overriding_plugin_dir) {
+ osync_free(plugindir);
+ plugindir=overriding_plugin_dir;
+ }
+
  if (!client->plugin) {
  client->plugin_env = osync_plugin_env_new(error);
  if (!client->plugin_env)
@@ -1699,12 +1706,29 @@
  if (client->thread)
  osync_thread_unref(client->thread);
 
+ if (client->overriding_plugin_dir)
+ osync_free(client->overriding_plugin_dir);
+
  osync_free(client);
 
  osync_trace(TRACE_EXIT, "%s", __func__);
  }
 }
 
+void osync_client_set_overriding_plugin_dir(OSyncClient *client, char *overriding_plugin_dir)
+{
+ osync_assert(client);
+ if (client->overriding_plugin_dir)
+ osync_free(client->overriding_plugin_dir);
+ client->overriding_plugin_dir = osync_strdup(overriding_plugin_dir);
+}
+
+char *osync_client_get_overriding_plugin_dir(OSyncClient *client)
+{
+ osync_assert(client);
+ return client->overriding_plugin_dir;
+}
+
 osync_bool osync_client_set_incoming_queue(OSyncClient *client, OSyncQueue *incoming, OSyncError **error)
 {
  osync_queue_set_message_handler(incoming, _osync_client_message_handler, client);
Index: opensync/client/opensync_client_proxy.c
===================================================================
--- opensync/client/opensync_client_proxy.c (revision 5882)
+++ opensync/client/opensync_client_proxy.c (working copy)
@@ -952,7 +952,7 @@
  return proxy->member;
 }
 
-osync_bool osync_client_proxy_spawn(OSyncClientProxy *proxy, OSyncStartType type, const char *path, OSyncError **error)
+osync_bool osync_client_proxy_spawn(OSyncClientProxy *proxy, OSyncStartType type, const char *path, const char* external_command, OSyncError **error)
 {
  OSyncQueue *read1 = NULL;
  OSyncQueue *read2 = NULL;
@@ -1080,6 +1080,21 @@
  goto error;
  } else {
  name = osync_strdup_printf("%s%cpluginpipe", path, G_DIR_SEPARATOR);
+
+ if (external_command) {
+ char *command = osync_strdup_printf(external_command, name);
+ osync_trace(TRACE_INTERNAL, "g_spawn_command_line_async(%s)", command);
+ GError *gerror = NULL;
+ gboolean f = g_spawn_command_line_async(command, &gerror);
+ osync_free(command);
+ if (!f) {
+ osync_error_set(error, OSYNC_ERROR_GENERIC, "Unable to g_spawn_command_line_async(%s): %s", command, gerror->message);
+ g_error_free (gerror);
+ goto error;
+ }
+ }
+
+
  proxy->outgoing = osync_queue_new(name, error);
  osync_free(name);
  if (!proxy->outgoing)
Index: tests/client-tests/check_proxy.c
===================================================================
--- tests/client-tests/check_proxy.c (revision 5882)
+++ tests/client-tests/check_proxy.c (working copy)
@@ -39,7 +39,7 @@
  fail_unless(proxy != NULL, NULL);
  fail_unless(error == NULL, NULL);
 
- fail_unless(osync_client_proxy_spawn(proxy, OSYNC_START_TYPE_THREAD, NULL, &error), NULL);
+ fail_unless(osync_client_proxy_spawn(proxy, OSYNC_START_TYPE_THREAD, NULL, NULL, &error), NULL);
  fail_unless(error == NULL, NULL);
 
  fail_unless(osync_client_proxy_shutdown(proxy, &error), NULL);
@@ -110,7 +110,7 @@
  fail_unless(proxy != NULL, NULL);
  fail_unless(error == NULL, NULL);
 
- fail_unless(osync_client_proxy_spawn(proxy, OSYNC_START_TYPE_THREAD, NULL, &error), NULL);
+ fail_unless(osync_client_proxy_spawn(proxy, OSYNC_START_TYPE_THREAD, NULL, NULL, &error), NULL);
  fail_unless(error == NULL, NULL);
 
  OSyncPluginConfig *config = simple_plugin_config(NULL, "data1", "mockobjtype1", "mockformat1", NULL);
@@ -159,7 +159,7 @@
  fail_unless(proxy != NULL, NULL);
  fail_unless(error == NULL, NULL);
 
- fail_unless(osync_client_proxy_spawn(proxy, OSYNC_START_TYPE_THREAD, NULL, &error), NULL);
+ fail_unless(osync_client_proxy_spawn(proxy, OSYNC_START_TYPE_THREAD, NULL, NULL, &error), NULL);
  fail_unless(error == NULL, NULL);
 
  OSyncPluginConfig *config = simple_plugin_config(NULL, "data1", "mockobjtype1", "mockformat1", NULL);
@@ -225,7 +225,7 @@
  fail_unless(proxy != NULL, NULL);
  fail_unless(error == NULL, NULL);
 
- fail_unless(osync_client_proxy_spawn(proxy, OSYNC_START_TYPE_THREAD, NULL, &error), NULL);
+ fail_unless(osync_client_proxy_spawn(proxy, OSYNC_START_TYPE_THREAD, NULL, NULL, &error), NULL);
  fail_unless(error == NULL, NULL);
 
  OSyncPluginConfig *config = simple_plugin_config(NULL, "data1", "mockobjtype1", "mockformat1", NULL);

------------------------------------------------------------------------------
Come build with us! The BlackBerry(R) Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay
ahead of the curve. Join us from November 9 - 12, 2009. Register now!
http://p.sf.net/sfu/devconference
_______________________________________________
Opensync-devel mailing list
Opensync-devel@...
https://lists.sourceforge.net/lists/listinfo/opensync-devel

Re: [RFC][PATCH] OSYNC_START_TYPE_EXTERNAL

by Daniel Gollub-3 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Wednesday 28 October 2009 10:04:43 pm Henrik /KaarPoSoft wrote:
> Dear all,
>
> To my surprise, I have received only ONE comment (thank you Graham!) to
> my recent postings about OSYNC_START_TYPE_EXTERNAL.

Sorry, i wrote several tests this week - looking forward to some more spare
time next week. Private things first - you know ;)


>
> Please let me know if anybody objects to me checking in the attached
> combined patch for all the issues I have raised about
> OSYNC_START_TYPE_EXTERNAL so far.

I like the command_start addition .. this is somethign which was really
missing. Could you explain how you use this for mozilla-sync?

Do you start mozilla if it isn't already started? Or do you trigger the sync
via an additional command? Or both?


I'm still not convinced why there is need to overwrite the plugindir ... or
maybe i didn't understand the problem. Unfortuantely exactly for the new API
function to override the path the API documentation is missing why this is
needed ...

+OSYNC_EXPORT void osync_client_set_overriding_plugin_dir(OSyncClient *client,
char *overriding_plugin_dir);
+OSYNC_EXPORT char *osync_client_get_overriding_plugin_dir(OSyncClient
*client);
+

Best Regards,
Daniel

--
Daniel Gollub                        Geschaeftsfuehrer: Ralph Dehner
FOSS Developer                       Unternehmenssitz:  Vohburg
B1 Systems GmbH                      Amtsgericht:       Ingolstadt
Mobil: +49-(0)-160 47 73 970         Handelsregister:   HRB 3537
EMail: gollub@...          http://www.b1-systems.de

Adresse: B1 Systems GmbH, Osterfeldstraße 7, 85088 Vohburg
http://pgpkeys.pca.dfn.de/pks/lookup?op=get&search=0xED14B95C2F8CA78D


------------------------------------------------------------------------------
Come build with us! The BlackBerry(R) Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay
ahead of the curve. Join us from November 9 - 12, 2009. Register now!
http://p.sf.net/sfu/devconference
_______________________________________________
Opensync-devel mailing list
Opensync-devel@...
https://lists.sourceforge.net/lists/listinfo/opensync-devel

signature.asc (204 bytes) Download Attachment

Re: [RFC][PATCH] OSYNC_START_TYPE_EXTERNAL

by Henrik /KaarPoSoft-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Daniel Gollub wrote:

> On Wednesday 28 October 2009 10:04:43 pm Henrik /KaarPoSoft wrote:
>  
>> Please let me know if anybody objects to me checking in the attached
>> combined patch for all the issues I have raised about
>> OSYNC_START_TYPE_EXTERNAL so far.
>>    
>
> I like the command_start addition .. this is somethign which was really
> missing. Could you explain how you use this for mozilla-sync?
>
> Do you start mozilla if it isn't already started? Or do you trigger the sync
> via an additional command? Or both?
>  
If you call the "thunderbird" program, it will start a new process if it
is not already running. I thunderbird is already running, it will simply
hand off the command line parameters to the already running instance.

The opensync client is triggered by the -mozilla-sync command line
option which I added to thunderbird.

The sync itself can be triggered by running the engine from osynctool or
from inside the blueZync plugin to Thunderbird.

> I'm still not convinced why there is need to overwrite the plugindir ... or
> maybe i didn't understand the problem. Unfortuantely exactly for the new API
> function to override the path the API documentation is missing why this is
> needed ...
>
> +OSYNC_EXPORT void osync_client_set_overriding_plugin_dir(OSyncClient *client,
> char *overriding_plugin_dir);
> +OSYNC_EXPORT char *osync_client_get_overriding_plugin_dir(OSyncClient
> *client);
> +
>  
As I wrote in a different thread, I am not sure if my way of
implementing the external process is the intended one. I am simply
running the same kind of client that would have been running inside
osynctool, by calling the same opensync libraries. That means that
without osync_client_get_overriding_plugin_dir the opensync library
would load the slim mozilla-sync plugin which was intended for
osynctool/engine, not the new fat mozilla-sync plugin intended for the
external process.

/Henrik

------------------------------------------------------------------------------
Come build with us! The BlackBerry(R) Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay
ahead of the curve. Join us from November 9 - 12, 2009. Register now!
http://p.sf.net/sfu/devconference
_______________________________________________
Opensync-devel mailing list
Opensync-devel@...
https://lists.sourceforge.net/lists/listinfo/opensync-devel

Re: [RFC][PATCH] OSYNC_START_TYPE_EXTERNAL

by Graham Cobb-4 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Thursday 29 October 2009 15:48:33 Henrik /KaarPoSoft wrote:
> As I wrote in a different thread, I am not sure if my way of
> implementing the external process is the intended one. I am simply
> running the same kind of client that would have been running inside
> osynctool, by calling the same opensync libraries. That means that
> without osync_client_get_overriding_plugin_dir the opensync library
> would load the slim mozilla-sync plugin which was intended for
> osynctool/engine, not the new fat mozilla-sync plugin intended for the
> external process.

There seem to be two possible solutions:

1) The "thin" plugin could just be replaced with a configuration file.  What
does OpenSync need to know about external process plugins?  Presumably pretty
much just the fact that they are external and how to contact them.

Should we add a config file to supply that info?  Then opensync would just not
attempt to load any plugin for which it had already read a config file.

2) Or, the fat plugin could not really be a plugin.  It could be completely up
to the caller of opensync in the external process to have the plugin code
loaded (presumably statically linked into the executable) and opensync, when
invoked to just create a client, would not load **any** plugins.  There would
have to be a function to call to register a plugin by passing in the
get_plugin_info address.

I **think** this second option was how the original external code worked, but
I haven't checked the code so I might be completely wrong.

I am keen to keep the external client code as minimal as possible.  In
particular, if we are every going to make IPC and timeout work properly I
certainly don't want two full IPC instances communicating -- there are enough
queues as it is!

Graham


------------------------------------------------------------------------------
Come build with us! The BlackBerry(R) Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay
ahead of the curve. Join us from November 9 - 12, 2009. Register now!
http://p.sf.net/sfu/devconference
_______________________________________________
Opensync-devel mailing list
Opensync-devel@...
https://lists.sourceforge.net/lists/listinfo/opensync-devel

Re: [RFC][PATCH] OSYNC_START_TYPE_EXTERNAL

by Henrik /KaarPoSoft-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Graham Cobb wrote:

> On Thursday 29 October 2009 15:48:33 Henrik /KaarPoSoft wrote:
>  
>> As I wrote in a different thread, I am not sure if my way of
>> implementing the external process is the intended one. I am simply
>> running the same kind of client that would have been running inside
>> osynctool, by calling the same opensync libraries. That means that
>> without osync_client_get_overriding_plugin_dir the opensync library
>> would load the slim mozilla-sync plugin which was intended for
>> osynctool/engine, not the new fat mozilla-sync plugin intended for the
>> external process.
>>    
>
> There seem to be two possible solutions:
>
> 1) The "thin" plugin could just be replaced with a configuration file.  What
> does OpenSync need to know about external process plugins?  Presumably pretty
> much just the fact that they are external and how to contact them.
>
> Should we add a config file to supply that info?  Then opensync would just not
> attempt to load any plugin for which it had already read a config file.
>  
Good idea. Daniel, your call!

> 2) Or, the fat plugin could not really be a plugin.  It could be completely up
> to the caller of opensync in the external process to have the plugin code
> loaded (presumably statically linked into the executable) and opensync, when
> invoked to just create a client, would not load **any** plugins.  There would
> have to be a function to call to register a plugin by passing in the
> get_plugin_info address.
>
> I **think** this second option was how the original external code worked, but
> I haven't checked the code so I might be completely wrong.
>  
Could you point me to where to find "the original external code"?
I have not been able to find any implementation of an external
process/plugin - except for osplugin.
> I am keen to keep the external client code as minimal as possible.  In
> particular, if we are every going to make IPC and timeout work properly I
> certainly don't want two full IPC instances communicating -- there are enough
> queues as it is!
>  
I don't think this approach introduces any new queues.
I believe there are two bidirectional queues in any case (engine and
plugin), it is just a question of whether they are accessed from
different threads in the same thread of from different processes.

/Henrik

------------------------------------------------------------------------------
Come build with us! The BlackBerry(R) Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay
ahead of the curve. Join us from November 9 - 12, 2009. Register now!
http://p.sf.net/sfu/devconference
_______________________________________________
Opensync-devel mailing list
Opensync-devel@...
https://lists.sourceforge.net/lists/listinfo/opensync-devel

Re: [RFC][PATCH] OSYNC_START_TYPE_EXTERNAL * new patch *

by Henrik /KaarPoSoft-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi all,

I have earlier provided a patch so a plugin of type
OSYNC_START_TYPE_EXTERNAL can set osync_plugin_set_start_external_command.

However, after some thought, I actually think it should be on the
OSyncPluginConfig.

In this way, different members can have different external commands with
the same plugin.

I.e. the config for mozilla-sync plugin could specify thunderbird or
sunbird or even the Debian ice-*.

Please find attached a combined patch.

Comments?

/Henrik




Index: misc/schemas/plugin_config.xsd
===================================================================
--- misc/schemas/plugin_config.xsd (revision 5882)
+++ misc/schemas/plugin_config.xsd (working copy)
@@ -9,6 +9,7 @@
         <xsd:element maxOccurs="1" minOccurs="0" name="Connection" type="Connection"/>
         <xsd:element maxOccurs="1" minOccurs="0" name="Localization" type="Localization"/>
         <xsd:element maxOccurs="1" minOccurs="0" name="Resources" type="Resources"/>
+        <xsd:element maxOccurs="1" minOccurs="0" name="ExternalPlugin" type="ExternalPlugin"/>
       </xsd:sequence>
       <xsd:attribute name="version" type="xsd:string"/>
     </xsd:complexType>
@@ -164,4 +165,10 @@
     </xsd:all>
   </xsd:complexType>
 
+  <xsd:complexType name="ExternalPlugin">
+    <xsd:all>
+      <xsd:element maxOccurs="1" minOccurs="1" name="ExternalCommand" type="xsd:string" />
+    </xsd:all>
+  </xsd:complexType>
+
 </xsd:schema>
Index: opensync/opensync-plugin.h
===================================================================
--- opensync/opensync-plugin.h (revision 5882)
+++ opensync/opensync-plugin.h (working copy)
@@ -33,6 +33,7 @@
 #include "plugin/opensync_plugin_connection.h"
 #include "plugin/opensync_plugin_localization.h"
 #include "plugin/opensync_plugin_resource.h"
+#include "plugin/opensync_plugin_externalplugin.h"
 #include "plugin/opensync_objtype_sink.h"
 
 OPENSYNC_END_DECLS
Index: opensync/group/opensync_member.c
===================================================================
--- opensync/group/opensync_member.c (revision 5882)
+++ opensync/group/opensync_member.c (working copy)
@@ -832,6 +832,18 @@
  return new_list;
 }
 
+const char *osync_member_get_external_command(OSyncMember *member)
+{
+ osync_assert(member);
+ OSyncError *error;
+ OSyncPluginConfig *config = osync_member_get_config_or_default(member, &error);
+ if (config) {
+ OSyncPluginExternalPlugin *externalplugin = osync_plugin_config_get_externalplugin(config);
+ if (externalplugin) return osync_plugin_externalplugin_get_external_command(externalplugin);
+ }
+ return NULL;
+}
+
 osync_bool osync_member_objtype_enabled(OSyncMember *member, const char *objtype)
 {
  OSyncObjTypeSink *sink = NULL;
Index: opensync/group/opensync_member.h
===================================================================
--- opensync/group/opensync_member.h (revision 5882)
+++ opensync/group/opensync_member.h (working copy)
@@ -334,6 +334,25 @@
  */
 OSYNC_EXPORT osync_bool osync_member_plugin_is_uptodate(OSyncMember *member);
 
+/** @brief Returns the external command of a member
+ *
+ * If the plugin is of type OSYNC_START_TYPE_EXTERNAL, an external command can be executed by OpenSync.
+ * The external_command should be a string in printf format, with one %s.
+ * Before the command is executed, a variant of printf will be called
+ * to replace the %s with the path to the plugin pipe.
+ * The resulting command will be exectued with the glib function
+ * g_spawn_command_line_async.
+ *
+ * The external command is specified in the plugin configuration file for the member, e.g.
+ * <ExternalPlugin><ExternalCommand>the command</ExternalCommand></ExternalPlugin>
+ *
+ * @param plugin Pointer to the plugin
+ * @returns External command of the plugin
+ *
+ */
+OSYNC_EXPORT const char *osync_member_get_external_command(OSyncMember *member);
+
+
 /*@}*/
 
 #endif /* _OPENSYNC_MEMBER_H_ */
Index: opensync/plugin/opensync_plugin_config_private.h
===================================================================
--- opensync/plugin/opensync_plugin_config_private.h (revision 5882)
+++ opensync/plugin/opensync_plugin_config_private.h (working copy)
@@ -44,6 +44,8 @@
  OSyncPluginLocalization *localization;
  /** List of resource configurations */
  OSyncList *resources;
+ /** External plugin configuration */
+ OSyncPluginExternalPlugin *externalplugin;
 
  /** Flags to store supported config options */
  OSyncPluginConfigSupportedFlags supported;
Index: opensync/plugin/opensync_plugin_config.c
===================================================================
--- opensync/plugin/opensync_plugin_config.c (revision 5882)
+++ opensync/plugin/opensync_plugin_config.c (working copy)
@@ -33,6 +33,7 @@
 #include "opensync_plugin_connection_internals.h"
 #include "opensync_plugin_localization_private.h" /* FIXME: direct access to private header */
 #include "opensync_plugin_resource_private.h" /* FIXME: direct access to private header */
+#include "opensync_plugin_externalplugin_private.h" /* FIXME: direct access to private header */
 
 #include "opensync_plugin_config_private.h"
 #include "opensync_plugin_config_internals.h"
@@ -209,6 +210,47 @@
  return FALSE;
 }
 
+static osync_bool _osync_plugin_config_parse_externalplugin(OSyncPluginConfig *config, xmlNode *cur, OSyncError **error)
+{
+ OSyncPluginExternalPlugin *externalplugin = NULL;
+ osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, config, cur, error);
+
+ if (cur == NULL) { // don't set externalplugin if ExternalPlugin tag is empty
+ osync_trace(TRACE_EXIT, "%s", __func__);
+ return TRUE;
+ }
+
+ externalplugin = osync_plugin_externalplugin_new(error);
+ if (!externalplugin)
+ goto error;
+
+ for (; cur != NULL; cur = cur->next) {
+ char *str = NULL;
+ if (cur->type != XML_ELEMENT_NODE)
+ continue;
+
+ str = (char*)xmlNodeGetContent(cur);
+ if (!str)
+ continue;
+
+ if (!xmlStrcmp(cur->name, (const xmlChar *)"ExternalCommand")) {
+ osync_plugin_externalplugin_set_external_command(externalplugin, str);
+ }
+
+ osync_xml_free(str);
+ }
+
+ osync_plugin_config_set_externalplugin(config, externalplugin);
+ osync_plugin_externalplugin_unref(externalplugin);
+
+ osync_trace(TRACE_EXIT, "%s", __func__);
+ return TRUE;
+
+ error:
+ osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
+ return FALSE;
+}
+
 static osync_bool _osync_plugin_config_parse_connection_bluetooth(OSyncPluginConnection *conn, xmlNode *cur, OSyncError **error) {
 
  osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, conn, cur, error);
@@ -728,6 +770,9 @@
  } else if (!xmlStrcmp(cur->name, (const xmlChar *)"Resources")) {
  config->supported |= OPENSYNC_PLUGIN_CONFIG_RESOURCES;
  ret = _osync_plugin_config_parse_resources(config, cur->xmlChildrenNode, error);
+ } else if (!xmlStrcmp(cur->name, (const xmlChar *)"ExternalPlugin")) {
+ config->supported |= OPENSYNC_PLUGIN_CONFIG_EXTERNALPLUGIN;
+ ret = _osync_plugin_config_parse_externalplugin(config, cur->xmlChildrenNode, error);
  } else {
  osync_error_set(error, OSYNC_ERROR_MISCONFIGURATION, "Unknown configuration field \"%s\"", cur->name);
  goto error;
@@ -824,6 +869,28 @@
  return FALSE;
 }
 
+static osync_bool _osync_plugin_config_assemble_externalplugin(xmlNode *cur, OSyncPluginExternalPlugin *externalplugin, OSyncError **error)
+{
+ const char *external_command;
+ xmlNode *node = NULL;
+ osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, cur, externalplugin, error);
+
+ node = xmlNewChild(cur, NULL, (xmlChar*)"ExternalPlugin", NULL);
+ if (!node) {
+ osync_error_set(error, OSYNC_ERROR_GENERIC, "No memory left to assemble configuration.");
+ goto error;
+ }
+
+ if ((external_command = osync_plugin_externalplugin_get_external_command(externalplugin)))
+ xmlNewChild(node, NULL, (xmlChar*)"ExternalCommand", (xmlChar*)external_command);
+
+ osync_trace(TRACE_EXIT, "%s", __func__);
+ return TRUE;
+ error:
+ osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
+ return FALSE;
+}
+
 static osync_bool _osync_plugin_config_assemble_connection(xmlNode *cur, OSyncPluginConnection *conn, OSyncError **error)
 {
  char *str;
@@ -1308,6 +1375,9 @@
  config->resources = osync_list_remove(config->resources, res);
  }
 
+ if (config->externalplugin)
+ osync_plugin_externalplugin_unref(config->externalplugin);
+
  if (config->schemadir)
  osync_free(config->schemadir);
 
@@ -1330,6 +1400,7 @@
  OSyncPluginConnection *conn;
  OSyncPluginAuthentication *auth;
  OSyncPluginLocalization *local;
+ OSyncPluginExternalPlugin *externalplugin;
  OSyncList *resources;
  OSyncList *options;
  char *version_str = NULL;
@@ -1382,6 +1453,11 @@
  if (!_osync_plugin_config_assemble_resources(doc->children, resources, error))
  goto error_and_free;
 
+ /* ExternalPlugin */
+ if ((externalplugin = osync_plugin_config_get_externalplugin(config)))
+ if (!_osync_plugin_config_assemble_externalplugin(doc->children, externalplugin, error))
+ goto error_and_free;
+
  xmlSaveFormatFile(path, doc, 1);
 
  osync_xml_free_doc(doc);
@@ -1567,7 +1643,27 @@
  return NULL;
 }
 
+/* External Plugin */
+OSyncPluginExternalPlugin *osync_plugin_config_get_externalplugin(OSyncPluginConfig *config)
+{
+ osync_assert(config);
+ return config->externalplugin;
+}
 
+void osync_plugin_config_set_externalplugin(OSyncPluginConfig *config, OSyncPluginExternalPlugin *externalplugin)
+{
+ osync_assert(config);
+
+ if (config->externalplugin) {
+ osync_plugin_externalplugin_unref(config->externalplugin);
+ config->externalplugin = NULL;
+ }
+
+ if (externalplugin) {
+ config->externalplugin = osync_plugin_externalplugin_ref(externalplugin);
+ }
+}
+
 OSyncPluginConnection *osync_plugin_config_get_connection(OSyncPluginConfig *config)
 {
  osync_assert(config);
Index: opensync/plugin/opensync_plugin_config.h
===================================================================
--- opensync/plugin/opensync_plugin_config.h (revision 5882)
+++ opensync/plugin/opensync_plugin_config.h (working copy)
@@ -47,7 +47,9 @@
  /** Resources */
  OPENSYNC_PLUGIN_CONFIG_RESOURCES = (1 << 3),
  /** Connection options */
- OPENSYNC_PLUGIN_CONFIG_CONNECTION = (1 << 4)
+ OPENSYNC_PLUGIN_CONFIG_CONNECTION = (1 << 4),
+ /** External Plugin */
+ OPENSYNC_PLUGIN_CONFIG_EXTERNALPLUGIN = (1 << 5)
 } OSyncPluginConfigSupportedFlag;
 
 /** @brief Set of OSyncPluginConfigSupportedFlags
@@ -217,6 +219,21 @@
  */
 OSYNC_EXPORT void osync_plugin_config_set_connection(OSyncPluginConfig *config, OSyncPluginConnection *connection);
 
+/* External Plugin */
+/**@brief Get the external plugin settings from a config
+ *
+ * @param config An OSyncPluginConfig
+ * @returns an OSyncPluginExternalPlugin with the details of the external plugin or NULL if no external plugin settings configured
+ */
+OSYNC_EXPORT OSyncPluginExternalPlugin *osync_plugin_config_get_externalplugin(OSyncPluginConfig *config);
+
+/**@brief Set the external plugin configuration
+ *
+ * @param config An OSyncPluginConfig
+ * @param authentication The new external plugin settings as an OSyncPluginExternalPlugin
+ */
+OSYNC_EXPORT void osync_plugin_config_set_externalplugin(OSyncPluginConfig *config, OSyncPluginExternalPlugin *externalplugin);
+
 /*@}*/
 
 #endif /*_OPENSYNC_PLUGIN_CONFIG_H_*/
Index: opensync/CMakeLists.txt
===================================================================
--- opensync/CMakeLists.txt (revision 5882)
+++ opensync/CMakeLists.txt (working copy)
@@ -60,6 +60,7 @@
    plugin/opensync_plugin_info.c
    plugin/opensync_plugin_localization.c
    plugin/opensync_plugin_resource.c
+   plugin/opensync_plugin_externalplugin.c
    plugin/opensync_objtype_sink.c
    version/opensync_version.c
    xmlformat/opensync_xmlfield.c
Index: opensync/opensync.h
===================================================================
--- opensync/opensync.h (revision 5882)
+++ opensync/opensync.h (working copy)
@@ -213,6 +213,7 @@
 typedef struct OSyncPluginConnection OSyncPluginConnection;
 typedef struct OSyncPluginLocalization OSyncPluginLocalization;
 typedef struct OSyncPluginResource OSyncPluginResource;
+typedef struct OSyncPluginExternalPlugin OSyncPluginExternalPlugin;
 
 /* Engine component */
 typedef struct OSyncEngine OSyncEngine;
Index: opensync/engine/opensync_engine.c
===================================================================
--- opensync/engine/opensync_engine.c (revision 5882)
+++ opensync/engine/opensync_engine.c (working copy)
@@ -773,7 +773,11 @@
  osync_client_proxy_set_context(proxy, engine->context);
  osync_client_proxy_set_change_callback(proxy, _osync_engine_receive_change, engine);
 
- if (!osync_client_proxy_spawn(proxy, osync_plugin_get_start_type(plugin), osync_member_get_configdir(member), error))
+ const char *external_command=NULL;
+ if (osync_plugin_get_start_type(plugin)==OSYNC_START_TYPE_EXTERNAL)
+ external_command=osync_member_get_external_command(member);
+
+ if (!osync_client_proxy_spawn(proxy, osync_plugin_get_start_type(plugin), osync_member_get_configdir(member), external_command, error))
  goto error_free_proxy;
 
  engine->busy = TRUE;
Index: opensync/client/opensync_client.h
===================================================================
--- opensync/client/opensync_client.h (revision 5882)
+++ opensync/client/opensync_client.h (working copy)
@@ -25,6 +25,29 @@
 OSYNC_EXPORT OSyncClient *osync_client_ref(OSyncClient *client);
 OSYNC_EXPORT void osync_client_unref(OSyncClient *client);
 
+/** @brief Set the directory from which the client shall load plugins
+ *
+ * In general, the "overriding_plugin_dir" does not need to be set.
+ * However, if a plugin has start type OSYNC_START_TYPE_EXTERNAL,
+ * and wishes to load a different plugin library in the external process,
+ * but still use the OSyncClient code, then osync_client_set_overriding_plugin_dir
+ * can be called to instruct OpenSync to look for the plugin library
+ * in an alternative location.
+ *
+ * @param plugin Pointer to the client
+ * @param overriding_plugin_dir The directory from which the client shall load plugins
+ *
+ */
+OSYNC_EXPORT void osync_client_set_overriding_plugin_dir(OSyncClient *client, char *overriding_plugin_dir);
+
+/** @brief Get the directory from which the client will load plugins
+ *
+ * @param client Pointer to the client
+ * @returns The directory from which the client will load plugins
+ *
+ */
+OSYNC_EXPORT char *osync_client_get_overriding_plugin_dir(OSyncClient *client);
+
 OSYNC_EXPORT osync_bool osync_client_set_incoming_queue(OSyncClient *client, OSyncQueue *incoming, OSyncError **error);
 OSYNC_EXPORT osync_bool osync_client_set_outgoing_queue(OSyncClient *client, OSyncQueue *outgoing, OSyncError **error);
 
Index: opensync/client/opensync_client_proxy_internals.h
===================================================================
--- opensync/client/opensync_client_proxy_internals.h (revision 5882)
+++ opensync/client/opensync_client_proxy_internals.h (working copy)
@@ -44,7 +44,7 @@
 void osync_client_proxy_set_change_callback(OSyncClientProxy *proxy, change_cb cb, void *userdata);
 OSyncMember *osync_client_proxy_get_member(OSyncClientProxy *proxy);
 
-OSYNC_TEST_EXPORT osync_bool osync_client_proxy_spawn(OSyncClientProxy *proxy, OSyncStartType type, const char *path, OSyncError **error);
+OSYNC_TEST_EXPORT osync_bool osync_client_proxy_spawn(OSyncClientProxy *proxy, OSyncStartType type, const char *path, const char* external_command, OSyncError **error);
 OSYNC_TEST_EXPORT osync_bool osync_client_proxy_shutdown(OSyncClientProxy *proxy, OSyncError **error);
 
 OSYNC_TEST_EXPORT osync_bool osync_client_proxy_initialize(OSyncClientProxy *proxy, initialize_cb callback, void *userdata, const char *formatdir, const char *plugindir, const char *plugin, const char *groupname, const char *configdir, OSyncPluginConfig *config, OSyncError **error);
Index: opensync/client/opensync_client_private.h
===================================================================
--- opensync/client/opensync_client_private.h (revision 5882)
+++ opensync/client/opensync_client_private.h (working copy)
@@ -32,6 +32,7 @@
  OSyncPluginInfo *plugin_info;
  OSyncPluginEnv *plugin_env;
  OSyncFormatEnv *format_env;
+ char *overriding_plugin_dir;
  void *plugin_data;
  OSyncThread *thread;
 };
Index: opensync/client/opensync_client.c
===================================================================
--- opensync/client/opensync_client.c (revision 5882)
+++ opensync/client/opensync_client.c (working copy)
@@ -684,7 +684,14 @@
  osync_queue_unref(outgoing);
  osync_trace(TRACE_INTERNAL, "done connecting to engine");
  }
-
+
+ char *overriding_plugin_dir = osync_client_get_overriding_plugin_dir(client);
+ osync_trace(TRACE_INTERNAL, "overriding_plugin_dir=[%s]", overriding_plugin_dir);
+ if (overriding_plugin_dir) {
+ osync_free(plugindir);
+ plugindir=osync_strdup(overriding_plugin_dir);
+ }
+
  if (!client->plugin) {
  client->plugin_env = osync_plugin_env_new(error);
  if (!client->plugin_env)
@@ -1699,12 +1706,29 @@
  if (client->thread)
  osync_thread_unref(client->thread);
 
+ if (client->overriding_plugin_dir)
+ osync_free(client->overriding_plugin_dir);
+
  osync_free(client);
 
  osync_trace(TRACE_EXIT, "%s", __func__);
  }
 }
 
+void osync_client_set_overriding_plugin_dir(OSyncClient *client, char *overriding_plugin_dir)
+{
+ osync_assert(client);
+ if (client->overriding_plugin_dir)
+ osync_free(client->overriding_plugin_dir);
+ client->overriding_plugin_dir = osync_strdup(overriding_plugin_dir);
+}
+
+char *osync_client_get_overriding_plugin_dir(OSyncClient *client)
+{
+ osync_assert(client);
+ return client->overriding_plugin_dir;
+}
+
 osync_bool osync_client_set_incoming_queue(OSyncClient *client, OSyncQueue *incoming, OSyncError **error)
 {
  osync_queue_set_message_handler(incoming, _osync_client_message_handler, client);
Index: opensync/client/opensync_client_proxy.c
===================================================================
--- opensync/client/opensync_client_proxy.c (revision 5882)
+++ opensync/client/opensync_client_proxy.c (working copy)
@@ -952,7 +952,7 @@
  return proxy->member;
 }
 
-osync_bool osync_client_proxy_spawn(OSyncClientProxy *proxy, OSyncStartType type, const char *path, OSyncError **error)
+osync_bool osync_client_proxy_spawn(OSyncClientProxy *proxy, OSyncStartType type, const char *path, const char* external_command, OSyncError **error)
 {
  OSyncQueue *read1 = NULL;
  OSyncQueue *read2 = NULL;
@@ -1080,6 +1080,22 @@
  goto error;
  } else {
  name = osync_strdup_printf("%s%cpluginpipe", path, G_DIR_SEPARATOR);
+
+ if (external_command) {
+ char *command = osync_strdup_printf(external_command, name);
+ osync_trace(TRACE_INTERNAL, "g_spawn_command_line_async(%s)", command);
+ GError *gerror = NULL;
+ gboolean f = g_spawn_command_line_async(command, &gerror);
+ if (!f) {
+ osync_error_set(error, OSYNC_ERROR_GENERIC, "Unable to g_spawn_command_line_async(%s): %s", command, gerror->message);
+ g_error_free (gerror);
+ osync_free(command);
+ goto error;
+ }
+ osync_free(command);
+ }
+
+
  proxy->outgoing = osync_queue_new(name, error);
  osync_free(name);
  if (!proxy->outgoing)
Index: tests/client-tests/check_proxy.c
===================================================================
--- tests/client-tests/check_proxy.c (revision 5882)
+++ tests/client-tests/check_proxy.c (working copy)
@@ -39,7 +39,7 @@
  fail_unless(proxy != NULL, NULL);
  fail_unless(error == NULL, NULL);
 
- fail_unless(osync_client_proxy_spawn(proxy, OSYNC_START_TYPE_THREAD, NULL, &error), NULL);
+ fail_unless(osync_client_proxy_spawn(proxy, OSYNC_START_TYPE_THREAD, NULL, NULL, &error), NULL);
  fail_unless(error == NULL, NULL);
 
  fail_unless(osync_client_proxy_shutdown(proxy, &error), NULL);
@@ -110,7 +110,7 @@
  fail_unless(proxy != NULL, NULL);
  fail_unless(error == NULL, NULL);
 
- fail_unless(osync_client_proxy_spawn(proxy, OSYNC_START_TYPE_THREAD, NULL, &error), NULL);
+ fail_unless(osync_client_proxy_spawn(proxy, OSYNC_START_TYPE_THREAD, NULL, NULL, &error), NULL);
  fail_unless(error == NULL, NULL);
 
  OSyncPluginConfig *config = simple_plugin_config(NULL, "data1", "mockobjtype1", "mockformat1", NULL);
@@ -159,7 +159,7 @@
  fail_unless(proxy != NULL, NULL);
  fail_unless(error == NULL, NULL);
 
- fail_unless(osync_client_proxy_spawn(proxy, OSYNC_START_TYPE_THREAD, NULL, &error), NULL);
+ fail_unless(osync_client_proxy_spawn(proxy, OSYNC_START_TYPE_THREAD, NULL, NULL, &error), NULL);
  fail_unless(error == NULL, NULL);
 
  OSyncPluginConfig *config = simple_plugin_config(NULL, "data1", "mockobjtype1", "mockformat1", NULL);
@@ -225,7 +225,7 @@
  fail_unless(proxy != NULL, NULL);
  fail_unless(error == NULL, NULL);
 
- fail_unless(osync_client_proxy_spawn(proxy, OSYNC_START_TYPE_THREAD, NULL, &error), NULL);
+ fail_unless(osync_client_proxy_spawn(proxy, OSYNC_START_TYPE_THREAD, NULL, NULL, &error), NULL);
  fail_unless(error == NULL, NULL);
 
  OSyncPluginConfig *config = simple_plugin_config(NULL, "data1", "mockobjtype1", "mockformat1", NULL);

------------------------------------------------------------------------------
Come build with us! The BlackBerry(R) Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay
ahead of the curve. Join us from November 9 - 12, 2009. Register now!
http://p.sf.net/sfu/devconference
_______________________________________________
Opensync-devel mailing list
Opensync-devel@...
https://lists.sourceforge.net/lists/listinfo/opensync-devel

Re: [RFC][PATCH] OSYNC_START_TYPE_EXTERNAL * new patch *

by Henrik /KaarPoSoft-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Just for reference, the external process basically boils down to this
function:


static int run_plugin(BZOpenSyncPlugin *pbz) {

    osync_trace(TRACE_ENTRY, "%s", __func__);

    OSyncError *error = NULL;
    OSyncQueue *incoming = NULL;
    OSyncQueue *outgoing = NULL;
    OSyncClient *client = NULL;

    client = osync_client_new(&error);
    if (!client)
        goto done;
   
    osync_client_set_overriding_plugin_dir(client, pbz->mySzPathToLib);

    /* Create connection pipes **/
    incoming = osync_queue_new(pbz->mySzPathToPipe, &error);
    if (!incoming)
        goto error;
   
    if (!osync_queue_create(incoming, &error))
        goto error;
   
    /* We now connect to our incoming queue */
    if (!osync_queue_connect(incoming, OSYNC_QUEUE_RECEIVER, &error))
        goto error;
   
    if (!osync_client_set_incoming_queue(client, incoming, &error))
        goto error;

    osync_queue_unref(incoming);


    osync_client_run_and_block(client);
   
    osync_client_unref(client);

    printf("blueZync synchronization client thread complete\n");

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

 error:
    osync_client_unref(client);
   
 done:  
    osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__,
osync_error_print(&error));
    fprintf(stderr, "Unable to initialize environment: %s\n",
osync_error_print(&error));
    osync_error_unref(&error);
    return NS_ERROR_FAILURE;
}


Henrik /KaarPoSoft wrote:

> Hi all,
>
> I have earlier provided a patch so a plugin of type
> OSYNC_START_TYPE_EXTERNAL can set
> osync_plugin_set_start_external_command.
>
> However, after some thought, I actually think it should be on the
> OSyncPluginConfig.
>
> In this way, different members can have different external commands
> with the same plugin.
>
> I.e. the config for mozilla-sync plugin could specify thunderbird or
> sunbird or even the Debian ice-*.
>
> Please find attached a combined patch.
>
> Comments?
>
> /Henrik
>

------------------------------------------------------------------------------
Come build with us! The BlackBerry(R) Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay
ahead of the curve. Join us from November 9 - 12, 2009. Register now!
http://p.sf.net/sfu/devconference
_______________________________________________
Opensync-devel mailing list
Opensync-devel@...
https://lists.sourceforge.net/lists/listinfo/opensync-devel