/*
* 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"
/** Function used on a path search for a format name array
*
* @see osync_conv_find_path_fn(), osync_change_convert_fmtnames()
*/
static osync_bool target_fn_fmtnames(const void *data, OSyncObjFormat *fmt)
{
const char * const *list = data;
const char * const *i;
for (i = list; *i; i++) {
if (!strcmp(fmt->name, *i))
/* Found */
return TRUE;
}
/* Not found */
return FALSE;
}
/** Function used on path searchs for a single format
*
* @see osync_conv_find_path_fn(), osync_change_convert()
*/
static osync_bool target_fn_simple(const void *data, OSyncObjFormat *fmt)
{
const OSyncObjFormat *target = data;
return target == fmt;
}
/** Function used on path searchs for a single format name
*
* @see osync_conv_find_path_fn(), osync_change_convert_fmtname()
*/
static osync_bool target_fn_fmtname(const void *data, OSyncObjFormat *fmt)
{
const char *name = data;
return !strcmp(name, fmt->name);
}
/** Function used on path searchs for a sink on a member
*
* @see osync_conv_find_path_fn(), osync_change_convert_member_sink()
*/
static osync_bool target_fn_membersink(const void *data, OSyncObjFormat *fmt)
{
const OSyncMember *memb = data;
GList *i;
for (i = memb->format_sinks; i; i = i->next) {
OSyncObjFormatSink *sink = i->data;
if (sink->format == fmt)
return TRUE;
}
/* Not found */
return FALSE;
}
/**
* @defgroup OSyncChangeCmds OpenSync Change Commands
* @ingroup OSyncPublic
* @brief High level functions to manipulate changes
*/
/*@{*/
/*! @brief Returns a string describing a change object
*
* Some formats cannot be printed directly. To be able to print these
* objects they should specify a print function.
*
* @param change The change to get printable
* @returns A string describing the object
*
*/
char *osync_change_get_printable(OSyncChange *change)
{
g_assert(change);
if (!change->has_data)
return NULL;
OSyncObjFormat *format = osync_change_get_objformat(change);
g_assert(format);
if (!format->print_func)
return g_strndup(change->data, change->size);
return format->print_func(change);
}
/*! @brief Returns the revision of the object
*
* @param change The change to get the revision from
* @param error A error struct
* @returns The revision of the object in seconds since the epoch in zulu time
*
*/
time_t osync_change_get_revision(OSyncChange *change, OSyncError **error)
{
osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, change, error);
g_assert(change);
if (!change->has_data) {
osync_error_set(error, OSYNC_ERROR_GENERIC, "No data set when asking for the timestamp");
osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
return -1;
}
OSyncObjFormat *format = osync_change_get_objformat(change);
g_assert(format);
if (!format->revision_func) {
osync_error_set(error, OSYNC_ERROR_GENERIC, "No revision function set");
osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
return -1;
}
time_t time = format->revision_func(change, error);
osync_trace(osync_error_is_set(error) ? TRACE_EXIT_ERROR : TRACE_EXIT, "%s: %s, %i", __func__, osync_error_print(error), time);
return time;
}
/*! @brief Compares the data of 2 changes
*
* Compares the two given changes and returns:
* CONV_DATA_MISMATCH if they are not the same
* CONV_DATA_SIMILAR if the are not the same but look similar
* CONV_DATA_SAME if they are exactly the same
*
* @param leftchange The left change to compare
* @param rightchange The right change to compare
* @returns The result of the comparison
*
*/
OSyncConvCmpResult osync_change_compare_data(OSyncChange *leftchange, OSyncChange *rightchange)
{
osync_trace(TRACE_ENTRY, "osync_change_compare_data(%p, %p)", leftchange, rightchange);
g_assert(rightchange);
g_assert(leftchange);
OSyncError *error = NULL;
if (!osync_change_convert_to_common(leftchange, &error)) {
osync_trace(TRACE_INTERNAL, "osync_change_compare_data: %s", osync_error_print(&error));
osync_error_free(&error);
osync_trace(TRACE_EXIT, "osync_change_compare_data: MISMATCH: Could not convert leftchange to common format");
return CONV_DATA_MISMATCH;
}
if (!osync_change_convert_to_common(rightchange, &error)) {
osync_trace(TRACE_INTERNAL, "osync_change_compare_data: %s", osync_error_print(&error));
osync_error_free(&error);
osync_trace(TRACE_EXIT, "osync_change_compare_data: MISMATCH: Could not convert rightchange to common format");
return CONV_DATA_MISMATCH;
}
if (!(rightchange->data == leftchange->data)) {
if (!(osync_change_get_objtype(leftchange) == osync_change_get_objtype(rightchange))) {
osync_trace(TRACE_EXIT, "osync_change_compare_data: MISMATCH: Objtypes do not match");
return CONV_DATA_MISMATCH;
}
if (leftchange->format != rightchange->format) {
osync_trace(TRACE_EXIT, "osync_change_compare_data: MISMATCH: Objformats do not match");
return CONV_DATA_MISMATCH;
}
if (!rightchange->data || !leftchange->data) {
osync_trace(TRACE_EXIT, "osync_change_compare_data: MISMATCH: One change has no data");
return CONV_DATA_MISMATCH;
}
OSyncObjFormat *format = osync_change_get_objformat(leftchange);
g_assert(format);
OSyncConvCmpResult ret = format->cmp_func(leftchange, rightchange);
osync_trace(TRACE_EXIT, "osync_change_compare_data: %i", ret);
return ret;
} else {
osync_trace(TRACE_EXIT, "osync_change_compare_data: SAME: OK. data point to same memory");
return CONV_DATA_SAME;
}
}
/*! @brief Compares 2 changes
*
* Compares the two given changes and returns:
* CONV_DATA_MISMATCH if they are not the same
* CONV_DATA_SIMILAR if the are not the same but look similar
* CONV_DATA_SAME if they are exactly the same
* This function does also compare changetypes etc unlike
* osync_change_compare_data()
*
* @param leftchange The left change to compare
* @param rightchange The right change to compare
* @returns The result of the comparison
*
*/
OSyncConvCmpResult osync_change_compare(OSyncChange *leftchange, OSyncChange *rightchange)
{
osync_trace(TRACE_ENTRY, "osync_change_compare(%p, %p)", leftchange, rightchange);
g_assert(rightchange);
g_assert(leftchange);
OSyncError *error = NULL;
if (!osync_change_convert_to_common(leftchange, &error)) {
osync_trace(TRACE_INTERNAL, "osync_change_compare: %s", osync_error_print(&error));
osync_error_free(&error);
osync_trace(TRACE_EXIT, "osync_change_compare: MISMATCH: Could not convert leftchange to common format");
return CONV_DATA_MISMATCH;
}
if (!osync_change_convert_to_common(rightchange, &error)) {
osync_trace(TRACE_INTERNAL, "osync_change_compare: %s", osync_error_print(&error));
osync_error_free(&error);
osync_trace(TRACE_EXIT, "osync_change_compare: MISMATCH: Could not convert leftchange to common format");
return CONV_DATA_MISMATCH;
}
if (rightchange->changetype == leftchange->changetype) {
OSyncConvCmpResult ret = osync_change_compare_data(leftchange, rightchange);
osync_trace(TRACE_EXIT, "osync_change_compare: Compare data: %i", ret);
return ret;
} else {
osync_trace(TRACE_EXIT, "osync_change_compare: MISMATCH: Change types do not match");
return CONV_DATA_MISMATCH;
}
}
/*! @brief Copies the data from one change to another change
*
* @param source The change to copy from
* @param target The change to copy to
* @param error A error struct
* @returns TRUE if the copy was successfull
*
*/
osync_bool osync_change_copy_data(OSyncChange *source, OSyncChange *target, OSyncError **error)
{
osync_trace(TRACE_ENTRY, "osync_change_copy_data(%p, %p, %p)", source, target, error);
OSyncObjFormat *format = NULL;
format = source->format;
if (!format)
format = target->format;
if (target->data)
osync_change_free_data(target);
if (!source->data) {
target->data = NULL;
target->size = 0;
osync_trace(TRACE_EXIT, "%s: Source had not data", __func__);
return TRUE;
}
if (!format || !format->copy_func) {
osync_trace(TRACE_INTERNAL, "We cannot copy the change, falling back to memcpy");
target->data = g_malloc0(sizeof(char) * (source->size + 1));
memcpy(target->data, source->data, source->size);
/* Make sure that its null terminated */
target->data[source->size] = 0;
target->size = source->size;
} else {
if (!format->copy_func(source->data, source->size, &(target->data), &(target->size))) {
osync_error_set(error, OSYNC_ERROR_GENERIC, "Something went wrong during copying");
osync_trace(TRACE_EXIT_ERROR, "osync_change_copy_data: %s", osync_error_print(error));
return FALSE;
}
}
osync_trace(TRACE_EXIT, "osync_change_copy_data");
return TRUE;
}
/*! @brief Makes a exact copy of change
*
* @param source The change to copy from
* @param error A error struct
* @returns A new change that is the copy, NULL on error
*
*/
OSyncChange *osync_change_copy(OSyncChange *source, OSyncError **error)
{
osync_trace(TRACE_ENTRY, "osync_change_copy(%p, %p)", source, error);
g_assert(source);
OSyncChange *newchange = osync_change_new();
newchange->uid = g_strdup(source->uid);
newchange->hash = g_strdup(source->hash);
newchange->has_data = source->has_data;
newchange->changetype = source->changetype;
newchange->format = osync_change_get_objformat(source);
newchange->objtype = osync_change_get_objtype(source);
newchange->sourceobjtype = g_strdup(osync_change_get_objtype(source)->name);
newchange->changes_db = source->changes_db;
newchange->member = source->member;
if (!osync_change_copy_data(source, newchange, error)) {
osync_change_free(newchange);
osync_trace(TRACE_EXIT_ERROR, "osync_change_copy: %s", osync_error_print(error));
return NULL;
}
osync_trace(TRACE_EXIT, "osync_change_copy: %p", newchange);
return newchange;
}
/*! @brief Duplicates the uid of the change
*
* This will call the duplicate function of a format.
* This is used if a uid is not unique.
*
* @param change The change to duplicate
* @returns TRUE if the uid was duplicated successfull
*
*/
osync_bool osync_change_duplicate(OSyncChange *change)
{
g_assert(change);
OSyncObjFormat *format = osync_change_get_objformat(change);
osync_debug("OSCONV", 3, "Duplicating change %s with format %s\n", change->uid, format->name);
if (!format || !format->duplicate_func)
return FALSE;
format->duplicate_func(change);
return TRUE;
}
/*! @brief Convert a change to a specific format with a specific extension
*
* This will convert the change with its data to the specified format
* if possible. And this will also convert the data to the specified format
* extension.
*
* @param env The conversion environment to use
* @param change The change to convert
* @param targetformat To which format you want to convert to
* @param extension_name The name of the extension
* @param error The error-return location
* @returns TRUE on success, FALSE otherwise
*
*/
osync_bool osync_change_convert_extension(OSyncFormatEnv *env, OSyncChange *change, OSyncObjFormat *targetformat, const char *extension_name, OSyncError **error)
{
osync_trace(TRACE_ENTRY, "osync_change_convert(%p, %p, %p:%s, %s, %p)", env, change, targetformat, targetformat ? targetformat->name : "NONE", extension_name, error);
if (osync_conv_convert_fn(env, change, target_fn_simple, targetformat, extension_name, error)) {
osync_trace(TRACE_EXIT, "osync_change_convert: TRUE");
return TRUE;
} else {
osync_trace(TRACE_EXIT_ERROR, "osync_change_convert: %s", osync_error_print(error));
return FALSE;
}
}
/*! @brief Convert a change to a specific format
*
* This will convert the change with its data to the specified format
* if possible.
*
* @param env The conversion environment to use
* @param change The change to convert
* @param targetformat To which format you want to convert to
* @param error The error-return location
* @returns TRUE on success, FALSE otherwise
*
*/
osync_bool osync_change_convert(OSyncFormatEnv *env, OSyncChange *change, OSyncObjFormat *targetformat, OSyncError **error)
{
return osync_change_convert_extension(env, change, targetformat, NULL, error);
}
/*! @brief Convert a change to the specified common format
*
* @param change The change to convert
* @param error The error-return location
* @returns TRUE on success, FALSE otherwise
*
*/
osync_bool osync_change_convert_to_common(OSyncChange *change, OSyncError **error)
{
osync_trace(TRACE_ENTRY, "osync_change_convert_to_common(%p, %p)", change, error);
if (!osync_change_get_objtype(change)) {
osync_error_set(error, OSYNC_ERROR_GENERIC, "The change has no objtype");
osync_trace(TRACE_EXIT_ERROR, "osync_change_convert_to_common: %s", osync_error_print(error));
return FALSE;
}
OSyncFormatEnv *env = osync_change_get_objtype(change)->env;
if (!osync_change_get_objtype(change)->common_format) {
osync_trace(TRACE_EXIT, "osync_change_convert_to_common: No common format set");
return TRUE;
}
osync_trace(TRACE_INTERNAL, "Converting from %s to %s", osync_change_get_objformat(change)->name, osync_change_get_objtype(change)->common_format->name);
if (osync_change_convert(env, change, osync_change_get_objtype(change)->common_format, error)) {
osync_trace(TRACE_EXIT, "osync_change_convert_to_common: TRUE");
return TRUE;
} else {
osync_trace(TRACE_EXIT_ERROR, "osync_change_convert_to_common: %s", osync_error_print(error));
return FALSE;
}
}
/*! @brief Convert a change to a specific format with the given name
*
* This will convert the change with its data to the specified format
* if possible.
*
* @param env The conversion environment to use
* @param change The change to convert
* @param targetname To which format you want to convert to
* @param error The error-return location
* @returns TRUE on success, FALSE otherwise
*
*/
osync_bool osync_change_convert_fmtname(OSyncFormatEnv *env, OSyncChange *change, const char *targetname, OSyncError **error)
{
return osync_conv_convert_fn(env, change, target_fn_fmtname, targetname, NULL, error);
}
/*! @brief Convert a change to some formats
*
* This will convert the change with its data to one of the specified formats
* if possible.
*
* @param env The conversion environment to use
* @param change The change to convert
* @param targetnames NULL-Terminated array of formatnames
* @param error The error-return location
* @returns TRUE on success, FALSE otherwise
*
*/
osync_bool osync_change_convert_fmtnames(OSyncFormatEnv *env, OSyncChange *change, const char **targetnames, OSyncError **error)
{
return osync_conv_convert_fn(env, change, target_fn_fmtnames, targetnames, NULL, error);
}
/*! @brief Convert a change to the nearest sink on a member
*
*
* @param env The conversion environment to use
* @param change The change to convert
* @param member The member that will receive the change
* @param error The error-return location
* @returns TRUE on success, FALSE otherwise
*
*/
osync_bool osync_change_convert_member_sink(OSyncFormatEnv *env, OSyncChange *change, OSyncMember *member, OSyncError **error)
{
if (!osync_member_require_sink_info(member, error))
return FALSE;
return osync_conv_convert_fn(env, change, target_fn_membersink, member, member->extension, error);
}
/*! @brief Tries to detect the object type of the given change
*
* This will try to detect the object type of the data on the change
* and return it, but not set it.
*
* @param env The conversion environment to use
* @param change The change to detect
* @param error The error-return location
* @returns The objecttype on success, NULL otherwise
*
*/
OSyncObjType *osync_change_detect_objtype(OSyncFormatEnv *env, OSyncChange *change, OSyncError **error)
{
OSyncObjFormat *format = NULL;
if (!(format = osync_change_detect_objformat(env, change, error)))
return NULL;
return format->objtype;
}
/*! @brief Tries to detect the encapsulated object type of the given change
*
* This will try to detect the encapsulated object type of the data on the change
* and return it, but not set it. This will try to detect the change, deencapsulate
* it, detect again etc until it cannot deencapsulate further.
*
* @param env The conversion environment to use
* @param change The change to detect
* @param error The error-return location
* @returns The objecttype on success, NULL otherwise
*
*/
OSyncObjType *osync_change_detect_objtype_full(OSyncFormatEnv *env, OSyncChange *change, OSyncError **error)
{
OSyncObjFormat *format = NULL;
if (!(format = osync_change_detect_objformat_full(env, change, error)))
return NULL;
return format->objtype;
}
/*! @brief Tries to detect the format of the given change
*
* This will try to detect the format of the data on the change
* and return it, but not set it.
*
* @param env The conversion environment to use
* @param change The change to detect
* @param error The error-return location
* @returns The format on success, NULL otherwise
*
*/
OSyncObjFormat *osync_change_detect_objformat(OSyncFormatEnv *env, OSyncChange *change, OSyncError **error)
{
osync_trace(TRACE_ENTRY, "osync_change_detect_objformat(%p, %p, %p)", env, change, error);
if (!change->has_data) {
osync_error_set(error, OSYNC_ERROR_GENERIC, "The change has no data");
osync_trace(TRACE_EXIT_ERROR, "osync_change_detect_objformat: %s", osync_error_print(error));
return NULL;
}
//Run all datadetectors for our source type
GList *d = NULL;
for (d = env->converters; d; d = d->next) {
OSyncFormatConverter *converter = d->data;
osync_trace(TRACE_INTERNAL, "running detector %s for format %s\n", converter->source_format->name, osync_change_get_objformat(change)->name);
if (!strcmp(converter->source_format->name, osync_change_get_objformat(change)->name)) {
if (converter->detect_func && converter->detect_func(env, change->data, change->size)) {
osync_trace(TRACE_EXIT, "osync_change_detect_objformat: %p:%s", converter->target_format, converter->target_format->name);
return converter->target_format;
}
}
}
osync_error_set(error, OSYNC_ERROR_GENERIC, "None of the detectors was able to recognize this data");
osync_trace(TRACE_EXIT_ERROR, "osync_change_detect_objformat: %s", osync_error_print(error));
return NULL;
}
/*! @brief Tries to detect the encapsulated format of the given change
*
* This will try to detect the encapsulated format of the data on the change
* and return it, but not set it. This will try to detect the change, deencapsulate
* it, detect again etc until it cannot deencapsulate further.
*
* @param env The conversion environment to use
* @param change The change to detect
* @param error The error-return location
* @returns The format on success, NULL otherwise
*
*/
OSyncObjFormat *osync_change_detect_objformat_full(OSyncFormatEnv *env, OSyncChange *change, OSyncError **error)
{
osync_trace(TRACE_ENTRY, "osync_change_detect_objformat_full(%p, %p, %p)", env, change, error);
if (!change->has_data) {
osync_error_set(error, OSYNC_ERROR_GENERIC, "The change has no data");
osync_trace(TRACE_EXIT_ERROR, "osync_change_detect_objformat: %s", osync_error_print(error));
return NULL;
}
OSyncChange *new_change = change;
//Try to decap the change as far as possible
while (1) {
GList *d = NULL;
for (d = env->converters; d; d = d->next) {
OSyncFormatConverter *converter = d->data;
if (!strcmp(converter->source_format->name, osync_change_get_objformat(change)->name) && converter->type == CONVERTER_DECAP) {
osync_bool free_output = FALSE;
if (!(new_change = osync_converter_invoke_decap(converter, new_change, &free_output))) {
osync_error_set(error, OSYNC_ERROR_GENERIC, "Unable to decap the change");
osync_trace(TRACE_EXIT_ERROR, "osync_change_detect_objformat_full: %s", osync_error_print(error));
return NULL;
}
continue;
}
}
break;
}
OSyncObjFormat *ret = osync_change_detect_objformat(env, new_change, error);
if (!ret)
osync_trace(TRACE_EXIT_ERROR, "osync_change_detect_objformat_full: %s", osync_error_print(error));
else
osync_trace(TRACE_EXIT, "osync_change_detect_objformat_full: %p:%s", ret, ret->name);
return ret;
}
/*@}*/
syntax highlighted by Code2HTML, v. 0.9.1