/*
* libopensync - A synchronization framework
* Copyright (C) 2004-2005 Armin Bauer <armin.bauer@opensync.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "opensync.h"
#include "opensync_internals.h"
/**
* @defgroup OSyncMemberPrivateAPI OpenSync Member Internals
* @ingroup OSyncPrivate
* @brief The private part of the OSyncMember
*
*/
/*@{*/
#ifndef DOXYGEN_SHOULD_SKIP_THIS
OSyncMemberFunctions *osync_memberfunctions_new()
{
OSyncMemberFunctions *functions = g_malloc0(sizeof(OSyncMemberFunctions));
return functions;
}
OSyncMemberFunctions *osync_member_get_memberfunctions(OSyncMember *member)
{
return member->memberfunctions;
}
OSyncFormatEnv *osync_member_get_format_env(OSyncMember *member)
{
g_assert(member);
return osync_group_get_format_env(member->group);
}
/** Find the objtype_sink for a member, corresponding to objtypestr
*
* Note: Only call this function after calling osync_member_require_sink_info()
*/
OSyncObjTypeSink *osync_member_find_objtype_sink(OSyncMember *member, const char *objtypestr)
{
GList *o;
for (o = member->objtype_sinks; o; o = o->next) {
OSyncObjTypeSink *sink = o->data;
if (osync_conv_objtype_is_any(sink->objtype->name) || !strcmp(sink->objtype->name, objtypestr))
return sink;
}
return NULL;
}
/** Be sure that the sink information for the member is available
*
* This function should be used on every code that will access the objtype_sinks
* or objformat_sinks members on OSyncMember.
*
* This function will either load the plugin or load the plugin information
* for the member, in order to get the objtype_sink list information for
* the member.
*/
osync_bool osync_member_require_sink_info(OSyncMember *member, OSyncError **error)
{
// Currently, the only way to get the objtype_sink information
// is loading the plugin
if (!osync_member_instance_default_plugin(member, error))
return FALSE;
return TRUE;
}
/** Returns the list of objtype_sinks of a member
*
* @param member The member
* @param list_ptr Pointer to where the list will be returned
* @param error Pointer to error info
*
* @returns TRUE on success, FALSE on error
*/
osync_bool osync_member_get_objtype_sinks(OSyncMember *member, GList **list_ptr, OSyncError **error)
{
if (!osync_member_require_sink_info(member, error))
return FALSE;
*list_ptr = member->objtype_sinks;
return TRUE;
}
/** @brief Reads the configuration data of this member
*
* The config file is read in this order:
* - If there is a configuration in memory that is not yet saved
* this is returned
* - If there is a config file in the member directory this is read
* and returned
*
* The difference to get_config is that this function will never try to read
* the default config file and return an error instead. So this function is supposed
* to be used by the plugins.
*
* @param member The member
* @param data Return location for the data
* @param size Return location for the size of the data
* @param error Pointer to a error
* @returns TRUE if the config was loaded successfully, FALSE otherwise
*
*/
osync_bool osync_member_read_config(OSyncMember *member, char **data, int *size, OSyncError **error)
{
osync_trace(TRACE_ENTRY, "osync_member_read_config(%p, %p, %p, %p)", member, data, size, error);
if (!osync_member_instance_default_plugin(member, error)) {
osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
return FALSE;
}
OSyncPluginFunctions functions = member->plugin->info.functions;
osync_bool ret = FALSE;
if (!member->configdir) {
osync_error_set(error, OSYNC_ERROR_GENERIC, "Member has no config directory set");
osync_trace(TRACE_EXIT_ERROR, "osync_member_read_config: %i", osync_error_print(error));
return FALSE;
}
if (functions.get_config) {
ret = functions.get_config(member->configdir, data, size);
} else {
char *filename = g_strdup_printf("%s/%s.conf", member->configdir, osync_plugin_get_name(member->plugin));
ret = osync_file_read(filename, data, size, error);
g_free(filename);
}
if (ret)
osync_trace(TRACE_EXIT, "osync_member_read_config: TRUE");
else
osync_trace(TRACE_EXIT_ERROR, "osync_member_read_config: %s", osync_error_print(error));
return ret;
}
#endif
/*@}*/
/**
* @defgroup OSyncMemberAPI OpenSync Member
* @ingroup OSyncPublic
* @brief Used to manipulate members, which represent one device or application in a group
*
*/
/*@{*/
/** @brief Creates a new member for a group
*
* @param group The parent group. NULL if none
* @returns A newly allocated member
*
*/
OSyncMember *osync_member_new(OSyncGroup *group)
{
OSyncMember *member = g_malloc0(sizeof(OSyncMember));
if (group) {
osync_group_add_member(group, member);
member->group = group;
}
member->memberfunctions = osync_memberfunctions_new();
member->name = NULL;
return member;
}
/** @brief Frees a member
*
* @param member The member to free
*
*/
void osync_member_free(OSyncMember *member)
{
osync_assert_msg(member, "You must set a member to free");
if (member->group)
osync_group_remove_member(member->group, member);
//Free the plugin if we are not thread-safe
/*if (member->plugin && !member->plugin->info.is_threadsafe) {
osync_plugin_unload(member->plugin);
osync_plugin_free(member->plugin);
}*/
if (member->pluginname)
g_free(member->pluginname);
g_free(member->memberfunctions);
g_free(member);
}
/** @brief Unloads the plugin of a member
*
* @param member The member for which to unload the plugin
*
*/
void osync_member_unload_plugin(OSyncMember *member)
{
g_assert(member);
if (!member->plugin)
return;
/*if (!member->plugin->info.is_threadsafe) {
osync_plugin_unload(member->plugin);
osync_plugin_free(member->plugin);
}*/
g_list_free(member->objtype_sinks);
g_list_free(member->format_sinks);
//Really free the formats!
member->objtype_sinks = NULL;
member->format_sinks = NULL;
member->plugin = NULL;
}
/** @brief Instances a plugin and loads it if necessary
*
* @param member The member
* @param pluginname The name of the plugin that the member should use
* @param error Pointer to a error
* @returns TRUE if the plugin was instanced successfull, FALSE otherwise
*
*/
osync_bool osync_member_instance_plugin(OSyncMember *member, const char *pluginname, OSyncError **error)
{
g_assert(member);
g_assert(member->group);
g_assert(pluginname);
OSyncPlugin *plugin = osync_env_find_plugin(member->group->env, pluginname);
if (!plugin) {
osync_debug("OSPLG", 0, "Couldn't find the plugin %s for member", pluginname);
osync_error_set(error, OSYNC_ERROR_MISCONFIGURATION, "Unable to find the plugin \"%s\"", pluginname);
return FALSE;
}
osync_member_unload_plugin(member);
//For now we disable the threadsafety feature since dlopen doesnt like it
member->plugin = plugin;
/*if (plugin->info.is_threadsafe) {
member->plugin = plugin;
} else {
member->plugin = osync_plugin_load(NULL, plugin->path, error);
if (!member->plugin)
return FALSE;
}*/
member->pluginname = g_strdup(osync_plugin_get_name(member->plugin));
//Prepare the sinks;
GList *o;
for (o = member->plugin->accepted_objtypes; o; o = o->next) {
OSyncObjTypeTemplate *objtemplate = o->data;
OSyncObjTypeSink *objsink = osync_objtype_sink_from_template(member->group, objtemplate);
if (!objsink) {
osync_error_set(error, OSYNC_ERROR_GENERIC, "Could not find object type \"%s\"", objtemplate->name);
return FALSE;
}
member->objtype_sinks = g_list_append(member->objtype_sinks, objsink);
GList *f;
for (f = objtemplate->formats; f; f = f->next) {
OSyncObjFormatTemplate *frmtemplate = f->data;
OSyncObjFormatSink *format_sink = osync_objformat_sink_from_template(member->group, frmtemplate);
if (!format_sink) {
osync_error_set(error, OSYNC_ERROR_GENERIC, "Could not find format \"%s\"", frmtemplate->name);
return FALSE;
}
objsink->formatsinks = g_list_append(objsink->formatsinks, format_sink);
format_sink->objtype_sink = objsink;
member->format_sinks = g_list_append(member->format_sinks, format_sink);
if (frmtemplate->extension_name)
member->extension = g_strdup(frmtemplate->extension_name);
}
}
member->pluginname = g_strdup(pluginname);
return TRUE;
}
/** @brief Tries to instance the default plugin of a member (if set)
*
* @param member The member
* @param error Pointer to a error
* @returns TRUE if the default plugin was instanced successfully, FALSE otherwise
*
*/
osync_bool osync_member_instance_default_plugin(OSyncMember *member, OSyncError **error)
{
if (member->plugin)
return TRUE;
if (!member->pluginname) {
osync_error_set(error, OSYNC_ERROR_GENERIC, "No default plugin set while instancing");
return FALSE;
}
return osync_member_instance_plugin(member, member->pluginname, error);
}
/** @brief Returns the plugin of member
*
* @param member The member
* @returns The plugin of the member
*
*/
OSyncPlugin *osync_member_get_plugin(OSyncMember *member)
{
g_assert(member);
osync_member_instance_default_plugin(member, NULL);
return member->plugin;
}
/** @brief Returns the name of the default plugin of the member
*
* @param member The member
* @returns The name of the plugin
*
*/
const char *osync_member_get_pluginname(OSyncMember *member)
{
g_assert(member);
return member->pluginname;
}
/** @brief Sets the name of the default plugin of a member
*
* @param member The member
* @param pluginname The name of the default plugin
*
*/
void osync_member_set_pluginname(OSyncMember *member, const char *pluginname)
{
g_assert(member);
if (member->pluginname)
g_free(member->pluginname);
member->pluginname = g_strdup(pluginname);
}
/** @brief Returns the custom data set to the OSyncPluginInfo
*
* You can set custom data to the OSyncPluginInfo struct using
* info->plugin_data = something; you can then query this data later
* using this function.
*
* @param member The member
* @returns The plugin data set before, or NULL if none was set
*
*/
void *osync_member_get_plugindata(OSyncMember *member)
{
g_assert(member);
OSyncPlugin *plugin = osync_member_get_plugin(member);
return osync_plugin_get_plugin_data(plugin);
}
/** @brief Returns the configuration directory where this member is stored
*
* @param member The member
* @returns The configuration directory
*
*/
const char *osync_member_get_configdir(OSyncMember *member)
{
g_assert(member);
return member->configdir;
}
/** @brief Sets the directory where a member is supposed to be stored
*
* @param member The member
* @param configdir The name of the directory
*
*/
void osync_member_set_configdir(OSyncMember *member, const char *configdir)
{
g_assert(member);
if (member->configdir)
g_free(member->configdir);
member->configdir = g_strdup(configdir);
}
osync_bool osync_member_need_config(OSyncMember *member, OSyncConfigurationTypes *type, OSyncError **error)
{
osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, member, type, error);
g_assert(member);
g_assert(type);
*type = NO_CONFIGURATION;
if (!osync_member_instance_default_plugin(member, error))
goto error;
*type = member->plugin->info.config_type;
osync_trace(TRACE_EXIT, "%s: %i", __func__, *type);
return TRUE;
error:
osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
return FALSE;
}
/** @brief Gets the configuration data of this member
*
* The config file is read in this order:
* - If there is a configuration in memory that is not yet saved
* this is returned
* - If there is a config file in the member directory this is read
* and returned
* - Otherwise the default config file is loaded from one the opensync
* directories
*
* @param member The member
* @param data Return location for the data
* @param size Return location for the size of the data
* @param error Pointer to a error
* @returns TRUE if the config was loaded successfully, FALSE otherwise
*
*/
osync_bool osync_member_get_config_or_default(OSyncMember *member, char **data, int *size, OSyncError **error)
{
osync_trace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, member, data, size, error);
g_assert(member);
osync_bool ret = TRUE;
if (member->configdata) {
*data = member->configdata;
*size = member->configsize;
osync_trace(TRACE_EXIT, "%s: Configdata already in memory", __func__);
return TRUE;
}
if (!osync_member_read_config(member, data, size, error)) {
if (osync_error_is_set(error)) {
osync_trace(TRACE_INTERNAL, "Read config not successfull: %s", osync_error_print(error));
osync_error_free(error);
}
char *filename = g_strdup_printf(OPENSYNC_CONFIGDIR"/%s", member->pluginname);
osync_debug("OSMEM", 3, "Reading default2 config file for member %lli from %s", member->id, filename);
ret = osync_file_read(filename, data, size, error);
g_free(filename);
}
osync_trace(TRACE_EXIT, "%s: %i", __func__, ret);
return ret;
}
/** @brief Gets the configuration data of this member
*
* The config file is read in this order:
* - If there is a configuration in memory that is not yet saved
* this is returned
* - If there is a config file in the member directory this is read
* and returned
* - Otherwise the default config file is loaded from one the opensync
* directories (but only if the plugin specified that it can use the default
* configuration)
*
* @param member The member
* @param data Return location for the data
* @param size Return location for the size of the data
* @param error Pointer to a error
* @returns TRUE if the config was loaded successfully, FALSE otherwise
*
*/
osync_bool osync_member_get_config(OSyncMember *member, char **data, int *size, OSyncError **error)
{
osync_trace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, member, data, size, error);
g_assert(member);
osync_bool ret = TRUE;
if (!osync_member_instance_default_plugin(member, error)) {
osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
return FALSE;
}
if (member->plugin->info.config_type == NO_CONFIGURATION) {
osync_error_set(error, OSYNC_ERROR_GENERIC, "This member has no configuration options");
osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
return FALSE;
}
if (member->configdata) {
*data = member->configdata;
*size = member->configsize;
osync_trace(TRACE_EXIT, "%s: Configdata already in memory", __func__);
return TRUE;
}
if (!osync_member_read_config(member, data, size, error)) {
if (osync_error_is_set(error)) {
osync_trace(TRACE_INTERNAL, "Read config not successfull: %s", osync_error_print(error));
osync_error_free(error);
}
if (member->plugin->info.config_type == NEEDS_CONFIGURATION) {
//We dont load the default config for these
osync_error_set(error, OSYNC_ERROR_MISCONFIGURATION, "Member has not been configured");
osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
return FALSE;
}
char *filename = g_strdup_printf(OPENSYNC_CONFIGDIR"/%s", member->pluginname);
osync_debug("OSMEM", 3, "Reading default2 config file for member %lli from %s", member->id, filename);
ret = osync_file_read(filename, data, size, error);
g_free(filename);
}
osync_trace(TRACE_EXIT, "%s: %i", __func__, ret);
return ret;
}
/** @brief Sets the config data for a member
*
* Note that this does not save the config data
*
* @param member The member
* @param data The new config data
* @param size The size of the data
*
*/
void osync_member_set_config(OSyncMember *member, const char *data, int size)
{
osync_trace(TRACE_ENTRY, "%s(%p, %p, %i)", __func__, member, data, size);
g_assert(member);
//FIXME free old data
member->configdata = g_strdup(data);
member->configsize = size;
osync_trace(TRACE_EXIT, "%s", __func__);
}
/** @brief Gets the loop in which the member is dispatched
*
* @param member The member
* @returns The loop
*
*/
void *osync_member_get_loop(OSyncMember *member)
{
g_assert(member);
osync_trace(TRACE_INTERNAL, "%s: %p %p", __func__, member, member->loop);
return member->loop;
}
/** @brief Sets the loop in which the member is dispatched
*
* @param member The member
* @param loop The pointer to the loop
*
*/
void osync_member_set_loop(OSyncMember *member, void *loop)
{
g_assert(member);
osync_trace(TRACE_INTERNAL, "%s: %p %p", __func__, member, loop);
member->loop = loop;
}
/** @brief Returns if the member has configuation options
*
* @param member The member
* @return TRUE if member needs to be configured, FALSE otherwise
*
*/
osync_bool osync_member_has_configuration(OSyncMember *member)
{
osync_trace(TRACE_ENTRY, "%s(%p)", __func__, member);
g_assert(member);
osync_bool ret = (member->plugin->info.config_type == NEEDS_CONFIGURATION || member->plugin->info.config_type == OPTIONAL_CONFIGURATION);
osync_trace(TRACE_EXIT, "%s: %i", __func__, ret);
return ret;
}
/** @brief Loads a member from a directory where it has been saved
*
* @param group The group which is the parent
* @param path The path of the member
* @param error Pointer to a error
* @returns A newly allocated member thats stored in the group or NULL if error
*
*/
OSyncMember *osync_member_load(OSyncGroup *group, const char *path, OSyncError **error)
{
osync_trace(TRACE_ENTRY, "%s(%p, %s, %p)", __func__, group, path, error);
xmlDocPtr doc;
xmlNodePtr cur;
char *filename = NULL;
filename = g_strdup_printf ("%s/syncmember.conf", path);
OSyncMember *member = osync_member_new(group);
char *basename = g_path_get_basename(path);
member->id = atoi(basename);
g_free(basename);
member->configdir = g_strdup(path);
if (!_osync_open_xml_file(&doc, &cur, filename, "syncmember", error)) {
osync_member_free(member);
osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
return NULL;
}
while (cur != NULL) {
char *str = (char*)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
if (str) {
if (!xmlStrcmp(cur->name, (const xmlChar *)"pluginname"))
member->pluginname = g_strdup(str);
if (!xmlStrcmp(cur->name, (const xmlChar *)"name"))
member->name = g_strdup(str);
xmlFree(str);
}
cur = cur->next;
}
xmlFreeDoc(doc);
g_free(filename);
osync_trace(TRACE_EXIT, "%s: Loaded member: %p", __func__, member);
return member;
}
/** @brief Saves a member to it config directory
*
* @param member The member to save
* @param error Pointer to a error
* @returns TRUE if the member was saved successfully, FALSE otherwise
*
*/
osync_bool osync_member_save(OSyncMember *member, OSyncError **error)
{
osync_trace(TRACE_ENTRY, "%s(%p:(%lli), %p)", __func__, member, member ? member->id : 0, error);
char *filename = NULL;
if (!osync_member_instance_default_plugin(member, error)) {
osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
return FALSE;
}
if (!member->id) {
member->id = osync_group_create_member_id(member->group);
member->configdir = g_strdup_printf("%s/%lli", member->group->configdir, member->id);
}
g_assert(member->configdir);
if (!g_file_test(member->configdir, G_FILE_TEST_IS_DIR)) {
osync_debug("OSMEM", 3, "Creating config directory: %s for member %i", member->configdir, member->id);
if (mkdir(member->configdir, 0700)) {
osync_error_set(error, OSYNC_ERROR_IO_ERROR, "Unable to create directory for member %li\n", member->id);
osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
return FALSE;
}
}
//Saving the syncmember.conf
filename = g_strdup_printf ("%s/syncmember.conf", member->configdir);
xmlDocPtr doc;
doc = xmlNewDoc((xmlChar*)"1.0");
doc->children = xmlNewDocNode(doc, NULL, (xmlChar*)"syncmember", NULL);
//The plugin name
xmlNewTextChild(doc->children, NULL, (xmlChar*)"pluginname", (xmlChar*)member->pluginname);
//The name
xmlNewTextChild(doc->children, NULL, (xmlChar*)"name", (xmlChar*)member->name);
xmlSaveFile(filename, doc);
xmlFreeDoc(doc);
g_free(filename);
//Saving the config if it exists
osync_bool ret = TRUE;
if (member->configdata) {
OSyncPluginFunctions functions = member->plugin->info.functions;
if (functions.store_config) {
ret = functions.store_config(member->configdir, member->configdata, member->configsize);
} else {
filename = g_strdup_printf("%s/%s.conf", member->configdir, osync_plugin_get_name(member->plugin));
if (!osync_file_write(filename, member->configdata, member->configsize, 0600, error)) {
ret = FALSE;
}
g_free(filename);
}
g_free(member->configdata);
member->configdata = NULL;
member->configsize = 0;
}
osync_trace(TRACE_EXIT, "%s: %s", __func__, osync_error_print(error));
return ret;
}
/** @brief Gets the unique id of a member
*
* @param member The member
* @returns The id of the member thats unique in its group
*
*/
long long int osync_member_get_id(OSyncMember *member)
{
g_assert(member);
return member->id;
}
/** @brief Makes a custom call to the plugin that the member has instanced
*
* A custom function on the plugin must have the form (void *, void *, OSyncError **)
*
* @param member The member
* @param function The name of the function on the plugin to call
* @param data The custom data to pass as the second arg to the function on the plugin
* @param error A pointer to a error
* @returns The return value of the function call
*
*/
void *osync_member_call_plugin(OSyncMember *member, const char *function, void *data, OSyncError **error)
{
if (!osync_member_instance_default_plugin(member, error))
return FALSE;
void *(*plgfunc) (void *, void *, OSyncError **);
if (!(plgfunc = osync_plugin_get_function(member->plugin, function, error)))
return NULL;
return plgfunc(member->plugindata, data, error);
}
/** @brief Sets the slow-sync for a given object type on a member
*
* @param member The member
* @param objtypestr The name of the object type for which to set slow-sync
* @param slow_sync Set to TRUE if you want slow-sync, to FALSE if you want normal fast-sync (or remove slow-sync)
*
*/
void osync_member_set_slow_sync(OSyncMember *member, const char *objtypestr, osync_bool slow_sync)
{
g_assert(member);
OSyncGroup *group = osync_member_get_group(member);
g_assert(group);
osync_group_set_slow_sync(group, objtypestr, slow_sync);
}
/** @brief Returns if slow-sync has been set for a object type
*
* @param member The member
* @param objtypestr The name of the object type to look up
* @returns TRUE if slow-sync is enabled, FALSE otherwise
*
*/
osync_bool osync_member_get_slow_sync(OSyncMember *member, const char *objtypestr)
{
osync_trace(TRACE_ENTRY, "%s(%p, %s)", __func__, member, objtypestr);
g_assert(member);
OSyncGroup *group = osync_member_get_group(member);
g_assert(group);
osync_bool needs_slow_sync = osync_group_get_slow_sync(group, objtypestr);
osync_trace(TRACE_EXIT, "%s: %i", __func__, needs_slow_sync);
return needs_slow_sync;
}
/** @brief Requests synchronization from the sync engine
*
* @param member The member
*
*/
void osync_member_request_synchronization(OSyncMember *member)
{
osync_trace(TRACE_ENTRY, "%s(%p)", __func__, member);
g_assert(member);
if (member->memberfunctions->rf_sync_alert)
member->memberfunctions->rf_sync_alert(member);
else {
osync_trace(TRACE_EXIT_ERROR, "%s: Alert not handled", __func__);
return;
}
osync_trace(TRACE_EXIT, "%s", __func__);
}
/** @brief Makes random data of a object type that could be writen to the given member
*
* @param member The member
* @param change The change that will receive the random data
* @param objtypename The name of the object type for which to create random data
* @returns The sink of the member that could store this data
*
*/
OSyncObjFormatSink *osync_member_make_random_data(OSyncMember *member, OSyncChange *change, const char *objtypename)
{
int retry = 0;
g_assert(member);
OSyncFormatEnv *env = osync_member_get_format_env(member);
OSyncObjFormatSink *format_sink = NULL;
for (retry = 0; retry < 100; retry++) {
if (retry > 20) {
osync_trace(TRACE_INTERNAL, "%s: Giving up", __func__);
return NULL; //Giving up
}
//Select a random objtype
OSyncObjType *objtype = NULL;
int selected = 0;
if (!objtypename) {
selected = g_random_int_range(0, g_list_length(env->objtypes));
objtype = g_list_nth_data(env->objtypes, selected);
} else
objtype = osync_conv_find_objtype(member->group->conv_env, objtypename);
osync_change_set_objtype(change, objtype);
//Select a random objformat
if (!g_list_length(objtype->formats)) {
osync_trace(TRACE_INTERNAL, "%s: Next. No formats", __func__);
continue;
}
OSyncObjFormat *format = NULL;
selected = g_random_int_range(0, g_list_length(objtype->formats));
format = g_list_nth_data(objtype->formats, selected);
if (!format->create_func) {
osync_trace(TRACE_INTERNAL, "%s: Next. Format %s has no create function", __func__, format->name);
continue;
}
//Create the data
format->create_func(change);
osync_change_set_objformat(change, format);
//Convert the data to a format the plugin understands
OSyncObjTypeSink *objtype_sink = osync_member_find_objtype_sink(member, objtype->name);
if (!objtype_sink) {
osync_trace(TRACE_INTERNAL, "%s: Next. No objtype sink for %s", __func__, objtype->name);
continue; //We had a objtype we cannot add
}
selected = g_random_int_range(0, g_list_length(objtype_sink->formatsinks));
format_sink = g_list_nth_data(objtype_sink->formatsinks, selected);
/*FIXME: use multiple sinks, or what? */
OSyncError *error = NULL;
if (!osync_change_convert(env, change, format_sink->format, &error)) {
osync_trace(TRACE_INTERNAL, "%s: Next. Unable to convert: %s", __func__, osync_error_print(&error));
continue; //Unable to convert to selected format
}
break;
}
return format_sink;
}
/** @brief Returns the custom data of a member
*
* @param member The member
* @returns The custom data
*
*/
void *osync_member_get_data(OSyncMember *member)
{
g_assert(member);
return member->enginedata;
}
/** @brief Sets the custom data on a member
*
* @param member The member
* @param data The custom data
*
*/
void osync_member_set_data(OSyncMember *member, void *data)
{
g_assert(member);
member->enginedata = data;
}
/** @brief Gets the group in which the member is stored
*
* @param member The member
* @returns The parental group
*
*/
OSyncGroup *osync_member_get_group(OSyncMember *member)
{
g_assert(member);
return member->group;
}
/** @brief Searches for a member by its id
*
* @param group The group in which to search
* @param id The id of the member
* @returns The member, or NULL if not found
*
*/
OSyncMember *osync_member_from_id(OSyncGroup *group, int id)
{
OSyncMember *member;
int i;
for (i = 0; i < osync_group_num_members(group); i++) {
member = osync_group_nth_member(group, i);
if (member->id == id) {
return member;
}
}
osync_debug("OSPLG", 0, "Couldnt find the member with the id %i", id);
return NULL;
}
/** @brief Returns if a certain object type is enabled on this member
*
* @param member The member
* @param objtype The name of the object type to check
* @returns TRUE if the object type is enabled, FALSE otherwise
*
*/
osync_bool osync_member_objtype_enabled(OSyncMember *member, const char *objtype)
{
g_assert(member);
OSyncObjTypeSink *sink = osync_member_find_objtype_sink(member, objtype);
g_assert(sink);
return sink->enabled;
}
/** @brief Enables or disables a object type on a member
*
* @param member The member
* @param objtypestr The name of the object type to change
* @param enabled Set to TRUE if you want to sync the object type, FALSE otherwise
*
* Note: this function should be called only after sink information for the member
* is available (osync_member_require_sink_info())
*
* @todo Change function interface to not require the plugin to be instanced manually.
* See comments on osync_group_set_objtype_enabled()
*/
void osync_member_set_objtype_enabled(OSyncMember *member, const char *objtypestr, osync_bool enabled)
{
osync_trace(TRACE_ENTRY, "%s(%p, %s, %i)", __func__, member, objtypestr, enabled);
OSyncObjTypeSink *sink = NULL;
g_assert(member);
if (osync_conv_objtype_is_any(objtypestr)) {
GList *o = NULL;
for (o = member->objtype_sinks; o; o = o->next) {
OSyncObjTypeSink *sink = o->data;
sink->enabled = enabled;
}
} else {
GList *o = NULL;
for (o = member->objtype_sinks; o; o = o->next) {
sink = o->data;
if (!strcmp(sink->objtype->name, objtypestr))
break;
sink = NULL;
}
if (!sink) {
osync_trace(TRACE_EXIT_ERROR, "Unable to find sink with name \"%s\"", objtypestr);
return;
}
sink->enabled = enabled;
}
osync_trace(TRACE_EXIT, "%s", __func__);
}
/*@}*/
/**
* @defgroup OSyncMemberFunctions OpenSync Member Functions
* @ingroup OSyncPublic
* @brief The functions that can be used to access the device that a member represents
*
*/
/*@{*/
/** @brief Initialize a member
*
* Calls the initialize function on a plugin
*
* @param member The member
* @param error A pointer to a error
* @returns TRUE if the plugin initialized successfully, FALSE otherwise
*
*/
osync_bool osync_member_initialize(OSyncMember *member, OSyncError **error)
{
osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, member, error);
if (!osync_member_instance_default_plugin(member, error)) {
osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
return FALSE;
}
g_assert(member);
g_assert(member->plugin);
OSyncPluginFunctions functions = member->plugin->info.functions;
g_assert(functions.initialize);
if (!(member->plugindata = functions.initialize(member, error))) {
osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
return FALSE;
}
osync_trace(TRACE_EXIT, "%s", __func__);
return TRUE;
}
/** @brief Finalizes a plugin
*
* Calls the finalize function on a plugin
*
* @param member The member
*
*/
void osync_member_finalize(OSyncMember *member)
{
osync_trace(TRACE_ENTRY, "%s(%p)", __func__, member);
g_assert(member);
g_assert(member->plugin);
OSyncPluginFunctions functions = member->plugin->info.functions;
if (functions.finalize)
functions.finalize(member->plugindata);
osync_trace(TRACE_EXIT, "%s", __func__);
}
/** @brief Queries a plugin for the changed objects since the last sync
*
* Calls the get_changeinfo function on a plugin
*
* @param member The member
* @param function The function that will receive the answer to this call
* @param user_data User data that will be passed on to the callback function
*
*/
void osync_member_get_changeinfo(OSyncMember *member, OSyncEngCallback function, void *user_data)
{
osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, member, function, user_data);
OSyncPluginFunctions functions = member->plugin->info.functions;
OSyncContext *context = osync_context_new(member);
context->callback_function = function;
context->calldata = user_data;
if (!functions.get_changeinfo) {
osync_context_report_error(context, OSYNC_ERROR_GENERIC, "No get_changeinfo function was given");
osync_trace(TRACE_EXIT_ERROR, "%s: No get_changeinfo function was given", __func__);
return;
}
functions.get_changeinfo(context);
osync_trace(TRACE_EXIT, "%s", __func__);
}
/** @brief Reads a single object by its uid
*
* Calls the read_change function on the plugin
*
* @param member The member
* @param change The change to read. The change must have the uid set
* @param function The function that will receive the answer to this call
* @param user_data User data that will be passed on to the callback function
*
*/
void osync_member_read_change(OSyncMember *member, OSyncChange *change, OSyncEngCallback function, void *user_data)
{
osync_trace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, member, change, function, user_data);
g_assert(change);
g_assert(change->uid);
g_assert(osync_change_get_objformat(change));
OSyncContext *context = osync_context_new(member);
context->callback_function = function;
context->calldata = user_data;
//search for the right formatsink
GList *i;
osync_debug("OSYNC", 2, "Searching for sink");
for (i = member->format_sinks; i; i = i->next) {
OSyncObjFormatSink *fmtsink = i->data;
if (fmtsink->format == osync_change_get_objformat(change)) {
//Read the change
g_assert(fmtsink->functions.read != NULL);
fmtsink->functions.read(context, change);
osync_trace(TRACE_EXIT, "%s", __func__);
return;
}
}
osync_context_report_error(context, OSYNC_ERROR_CONVERT, "Unable to send changes");
osync_trace(TRACE_EXIT_ERROR, "%s: Unable to find a sink", __func__);
}
/** @brief Checks if the member has a read method for the given objtype
*
* @param member The member
* @param objtype The objtype for which to check the read methid
* @return TRUE if the member has read function, FALSE otherwise
*
*/
osync_bool osync_member_has_read_function(OSyncMember *member, OSyncObjType *objtype)
{
GList *i;
for (i = member->format_sinks; i; i = i->next) {
OSyncObjFormatSink *fmtsink = i->data;
if (osync_objformat_get_objtype(fmtsink->format) == objtype)
return fmtsink->functions.read ? TRUE : FALSE;
}
return FALSE;
}
/** @brief Gets the "real" data of a object
*
* Calls the get_data function on the plugin
*
* @param member The member
* @param change The change. The must be returned from a call to get_changeinfo
* @param function The function that will receive the answer to this call
* @param user_data User data that will be passed on to the callback function
*
*/
void osync_member_get_change_data(OSyncMember *member, OSyncChange *change, OSyncEngCallback function, void *user_data)
{
osync_trace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, member, change, function, user_data);
OSyncPluginFunctions functions = member->plugin->info.functions;
g_assert(change != NULL);
OSyncContext *context = osync_context_new(member);
context->callback_function = function;
context->calldata = user_data;
functions.get_data(context, change);
osync_trace(TRACE_EXIT, "%s", __func__);
}
/** @brief Connects a member to its device
*
* Calls the connect function on a plugin
*
* @param member The member
* @param function The function that will receive the answer to this call
* @param user_data User data that will be passed on to the callback function
*
*/
void osync_member_connect(OSyncMember *member, OSyncEngCallback function, void *user_data)
{
osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, member, function, user_data);
OSyncPluginFunctions functions = member->plugin->info.functions;
OSyncContext *context = osync_context_new(member);
context->callback_function = function;
context->calldata = user_data;
if (!functions.connect) {
osync_context_report_error(context, OSYNC_ERROR_GENERIC, "No connect function was given");
osync_trace(TRACE_EXIT_ERROR, "%s: No connect function was given", __func__);
return;
}
functions.connect(context);
osync_trace(TRACE_EXIT, "%s", __func__);
}
/** @brief Disconnects a member from its device
*
* Calls the disconnect function on a plugin
*
* @param member The member
* @param function The function that will receive the answer to this call
* @param user_data User data that will be passed on to the callback function
*
*/
void osync_member_disconnect(OSyncMember *member, OSyncEngCallback function, void *user_data)
{
osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, member, function, user_data);
OSyncPluginFunctions functions = member->plugin->info.functions;
OSyncContext *context = osync_context_new(member);
context->callback_function = function;
context->calldata = user_data;
if (!functions.disconnect) {
osync_context_report_error(context, OSYNC_ERROR_GENERIC, "No disconnect function was given");
osync_trace(TRACE_EXIT_ERROR, "%s: No disconnect function was given", __func__);
return;
}
functions.disconnect(context);
osync_trace(TRACE_EXIT, "%s", __func__);
}
/** @brief Tells the plugin that the sync was successfull
*
* Calls the sync_done function on a plugin
*
* @param member The member
* @param function The function that will receive the answer to this call
* @param user_data User data that will be passed on to the callback function
*
*/
void osync_member_sync_done(OSyncMember *member, OSyncEngCallback function, void *user_data)
{
osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, member, function, user_data);
OSyncPluginFunctions functions = member->plugin->info.functions;
OSyncContext *context = osync_context_new(member);
context->callback_function = function;
context->calldata = user_data;
osync_member_set_slow_sync(member, "data", FALSE);
if (functions.sync_done) {
functions.sync_done(context);
} else {
osync_context_report_success(context);
}
osync_trace(TRACE_EXIT, "%s", __func__);
}
/** @brief Commits a change to the device
*
* Calls the commit_change function on a plugin
*
* @param member The member
* @param change The change to write
* @param function The function that will receive the answer to this call
* @param user_data User data that will be passed on to the callback function
*
*/
void osync_member_commit_change(OSyncMember *member, OSyncChange *change, OSyncEngCallback function, void *user_data)
{
osync_trace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, member, change, function, user_data);
g_assert(member);
g_assert(change);
OSyncContext *context = osync_context_new(member);
context->callback_function = function;
context->calldata = user_data;
OSyncObjType *type = osync_change_get_objtype(change);
/* This is just an optmization:
*
* the path search function will avoid doing
* cross-objtype conversions, so
* if we already have a sink for the original objtype,
* and it is disabled, we can drop the change
* without doing detection/conversion first.
*
* If the objtype will change during conversion,
* we check the right objtype sink later,
* anyway
*/
OSyncObjTypeSink *sink = osync_member_find_objtype_sink(member, type->name);
if (sink && !sink->enabled) {
osync_context_report_success(context);
osync_trace(TRACE_EXIT, "%s: Sink not enabled", __func__);
return;
}
//The destobjtype is the objtype of the format to which
//the change was just converted
change->destobjtype = g_strdup(osync_change_get_objtype(change)->name);
//Filter the change.
if (!osync_filter_change_allowed(member, change)) {
osync_context_report_success(context);
osync_trace(TRACE_EXIT, "%s: Change filtered", __func__);
return;
}
//search for the right formatsink, now that
//the change was converted
GList *i;
osync_debug("OSYNC", 2, "Searching for sink");
for (i = member->format_sinks; i; i = i->next) {
OSyncObjFormatSink *fmtsink = i->data;
osync_debug("OSYNC", 2, "Comparing change %s with sink %s", osync_change_get_objformat(change)->name, fmtsink->format ? fmtsink->format->name : "None");
if (fmtsink->format == osync_change_get_objformat(change)) {
if (fmtsink->functions.batch_commit) {
//Append to the stored changes
fmtsink->commit_changes = g_list_append(fmtsink->commit_changes, change);
fmtsink->commit_contexts = g_list_append(fmtsink->commit_contexts, context);
osync_trace(TRACE_EXIT, "%s: Waiting for batch processing", __func__);
return;
} else {
// Send the change
if (!fmtsink->functions.commit_change) {
osync_context_report_error(context, OSYNC_ERROR_GENERIC, "No commit_change function was given");
osync_trace(TRACE_EXIT_ERROR, "%s: No commit_change function was given", __func__);
return;
}
fmtsink->functions.commit_change(context, change);
osync_trace(TRACE_EXIT, "%s", __func__);
return;
}
}
}
osync_context_report_error(context, OSYNC_ERROR_CONVERT, "Unable to send changes");
osync_trace(TRACE_EXIT_ERROR, "%s: Unable to find a sink", __func__);
}
/** @brief Tells the plugin that all changes have been committed
*
* Calls the committed_all function on a plugin or the batch_commit function
* depending on which function the plugin wants to use.
*
* @param member The member
* @param function The callback that will receive the answer
* @param user_data The userdata to pass to the callback
*
*/
void osync_member_committed_all(OSyncMember *member, OSyncEngCallback function, void *user_data)
{
osync_trace(TRACE_ENTRY, "%s(%p)", __func__, member);
OSyncChange **changes = NULL;
OSyncContext **contexts = NULL;
OSyncContext *context = osync_context_new(member);
context->callback_function = function;
context->calldata = user_data;
GList *pendingchanges = NULL;
GList *pendingcontexts = NULL;
GList *f;
for (f = member->format_sinks; f; f = f->next) {
OSyncObjFormatSink *fmtsink = f->data;
osync_debug("OSYNC", 2, "Sending changes to sink %p:%s", fmtsink, fmtsink->format ? fmtsink->format->name : "None");
OSyncFormatFunctions functions = fmtsink->functions;
if (functions.batch_commit) {
pendingchanges = g_list_concat(pendingchanges, fmtsink->commit_changes);
pendingcontexts = g_list_concat(pendingcontexts, fmtsink->commit_contexts);
fmtsink->commit_changes = NULL;
fmtsink->commit_contexts = NULL;
}
}
f = member->format_sinks;
if (f) {
OSyncObjFormatSink *fmtsink = f->data;
osync_debug("OSYNC", 2, "Sending committed all to sink %p:%s", fmtsink, fmtsink->format ? fmtsink->format->name : "None");
OSyncFormatFunctions functions = fmtsink->functions;
if (functions.batch_commit) {
int i = 0;
changes = g_malloc0(sizeof(OSyncChange *) * (g_list_length(pendingchanges) + 1));
contexts = g_malloc0(sizeof(OSyncContext *) * (g_list_length(pendingcontexts) + 1));;
GList *o = pendingcontexts;
GList *c = NULL;
for (c = pendingchanges; c && o; c = c->next) {
OSyncChange *change = c->data;
OSyncContext *context = o->data;
changes[i] = change;
contexts[i] = context;
i++;
o = o->next;
}
g_list_free(pendingchanges);
g_list_free(pendingcontexts);
functions.batch_commit(context, contexts, changes);
g_free(changes);
g_free(contexts);
} else if (functions.committed_all) {
functions.committed_all(context);
} else {
osync_context_report_success(context);
}
}
osync_trace(TRACE_EXIT, "%s", __func__);
}
void osync_member_set_name(OSyncMember *member, const char *name)
{
g_assert(member);
if (member->name)
g_free(member->name);
member->name = g_strdup(name);
}
const char *osync_member_get_name(OSyncMember *member)
{
return member->name;
}
/** @brief Adds random data to a member
*
* Generates random data and writes it to the plugin. The plugin must support the access
* function. This function is mainly used for testing plugins.
*
* @param member The member on which to add random data
* @param objtype The name of the object type to add
* @returns The change that was added or NULL if adding the data was not successfull
*
*/
OSyncChange *osync_member_add_random_data(OSyncMember *member, const char *objtype)
{
osync_trace(TRACE_ENTRY, "%s(%p)", __func__, member);
OSyncContext *context = osync_context_new(member);
OSyncChange *change = osync_change_new();
change->changetype = CHANGE_ADDED;
OSyncObjFormatSink *format_sink;
if (!(format_sink = osync_member_make_random_data(member, change, objtype))) {
osync_trace(TRACE_EXIT_ERROR, "%s: Unable to make random data", __func__);
return NULL;
}
if (!format_sink->functions.access) {
osync_trace(TRACE_EXIT_ERROR, "%s: No access function", __func__);
return NULL;
}
if (!format_sink->functions.access(context, change)) {
osync_trace(TRACE_EXIT_ERROR, "%s: Unable to write change", __func__);
return NULL;
}
osync_trace(TRACE_EXIT, "%s: %p", __func__, change);
return change;
}
/** @brief Modifies random data on a member
*
* The plugin must support the access
* function. This function is mainly used for testing plugins.
*
* @param member The member on which to add random data
* @param change The change that should be modified. It must have the uid set.
* @returns TRUE if the changes was modified successfully, FALSE otherwise
*
*/
osync_bool osync_member_modify_random_data(OSyncMember *member, OSyncChange *change)
{
osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, member, change);
OSyncContext *context = osync_context_new(member);
change->changetype = CHANGE_MODIFIED;
OSyncObjFormatSink *format_sink;
char *uid = g_strdup(osync_change_get_uid(change));
if (!(format_sink = osync_member_make_random_data(member, change, osync_change_get_objtype(change)->name))) {
osync_trace(TRACE_EXIT_ERROR, "%s: Unable to make random data", __func__);
return FALSE;
}
osync_change_set_uid(change, uid);
if (!format_sink->functions.access) {
osync_trace(TRACE_EXIT_ERROR, "%s: No access function", __func__);
return FALSE;
}
if (!format_sink->functions.access(context, change)) {
osync_trace(TRACE_EXIT_ERROR, "%s: Unable to modify change", __func__);
return FALSE;
}
osync_trace(TRACE_EXIT, "%s", __func__);
return TRUE;
}
/** @brief Deletes data from a device
*
* The plugin must support the access function. This is mainly
* used for testing plugins.
*
* @param member The member from which to delete
* @param change The change to delete. The uid must be set
* @returns TRUE if the change was deleted, FALSE otherwise
*
*/
osync_bool osync_member_delete_data(OSyncMember *member, OSyncChange *change)
{
osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, member, change);
OSyncContext *context = osync_context_new(member);
change->changetype = CHANGE_DELETED;
OSyncObjTypeSink *objtype_sink = osync_member_find_objtype_sink(member, osync_change_get_objtype(change)->name);
if (!objtype_sink) {
osync_trace(TRACE_EXIT_ERROR, "%s: Unable to find objsink for %s", __func__, osync_change_get_objtype(change)->name);
return FALSE;
}
OSyncObjFormat *format = osync_change_get_objformat(change);
OSyncObjFormatSink *format_sink = osync_objtype_find_format_sink(objtype_sink, format->name);
if (!format_sink) {
osync_trace(TRACE_EXIT_ERROR, "%s: Unable to find format sink for %s", __func__, format->name);
return FALSE;
}
if (format_sink->functions.batch_commit) {
//Append to the stored changes
format_sink->commit_changes = g_list_append(format_sink->commit_changes, change);
format_sink->commit_contexts = g_list_append(format_sink->commit_contexts, context);
osync_trace(TRACE_EXIT, "%s: Waiting for batch processing", __func__);
return TRUE;
} else {
if (!format_sink->functions.access) {
osync_trace(TRACE_EXIT_ERROR, "%s: No access function", __func__);
return FALSE;
}
if (!format_sink->functions.access(context, change)) {
osync_trace(TRACE_EXIT_ERROR, "%s: Unable to modify change", __func__);
return FALSE;
}
}
osync_trace(TRACE_EXIT, "%s", __func__);
return TRUE;
}
/*@}*/
/** Write the list of objtypes that had slow-sync set to a message */
void osync_member_write_sink_info(OSyncMember *member, OSyncMessage *message)
{
GList *obj = NULL;
for (osync_member_get_objtype_sinks(member, &obj, NULL); obj; obj = obj->next) {
OSyncObjTypeSink *sink = obj->data;
int slow;
slow = osync_member_get_slow_sync(member, sink->objtype->name);
osync_message_write_string(message, sink->objtype->name);
osync_message_write_int(message, sink->read);
osync_message_write_int(message, sink->write);
osync_message_write_int(message, sink->enabled);
osync_message_write_int(message, slow);
}
osync_message_write_string(message, NULL);
}
/** Read sink info for member
*
* @see osync_member_read_sink_info_full(), osync_member_read_sink_info()
*/
int __sync_member_read_sink_info(OSyncMember *member, OSyncMessage *message)
{
char *objtypestr;
int r = 0;
for (;;) {
int read, write, enabled, slow;
osync_message_read_string(message, &objtypestr);
if (!objtypestr)
break;
osync_message_read_int(message, &read);
osync_message_read_int(message, &write);
osync_message_read_int(message, &enabled);
osync_message_read_int(message, &slow);
OSyncObjTypeSink *sink = osync_member_find_objtype_sink(member, objtypestr);
if (!sink)
continue;
sink->read = read;
sink->write = write;
sink->enabled = enabled;
if (slow) {
osync_member_set_slow_sync(member, objtypestr, TRUE);
r++;
}
free(objtypestr);
}
return r;
}
/** Read a list of objtypes that had slow-sync set, replacing old settings
*
* Please notice that this function will reset any old
* slow-sync setting that was set before. So, this should
* be used only for messages that is known to contain the
* complete slow-sync settings, not only for a member.
*
* i.e. this function may be used to read the slow-sync settings
* from the engine to osplugin, but not to read the slow-sync
* settings from osplugin to the engine, because osplugin doesn't
* know about the slow-sync settings of other members in the sync
* group.
*/
void osync_member_read_sink_info_full(OSyncMember *member, OSyncMessage *message)
{
osync_group_reset_slow_sync(member->group, "data");
__sync_member_read_sink_info(member, message);
}
/** Read a list of objtypes that had slow-sync set, keeping old settings
*
* Please notice that this function won't reset
* the list of slow-sync settings, like
* osync_message_read_slow_sync_full_list(), but instead
* it will just set slow-sync for the objtypes received
* in the list.
*
* This function is supposed to be used when handling messages
* from osplugin to the engine, but NOT for messages from the engine
* to osplugin.
*/
void osync_member_read_sink_info(OSyncMember *member, OSyncMessage *message)
{
int set_for_any;
set_for_any = __sync_member_read_sink_info(member, message);
if (set_for_any) {
/* FIXME: We must force slow-sync in "data" when some
* objtype is marked to slow-sync
*
* (See ticket #1011)
*/
osync_member_set_slow_sync(member, "data", TRUE);
}
}
syntax highlighted by Code2HTML, v. 0.9.1