/* * libopensync - A synchronization framework * Copyright (C) 2004-2005 Armin Bauer * * 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" #include #include extern int errno; /* * On Solaris no flock function exists, * we must implenet it here */ #ifdef SOLARIS #define LOCK_SH 1 #define LOCK_EX 2 #define LOCK_NB 4 #define LOCK_UN 8 static int flock(int fd, int operation) { struct flock flock; switch (operation & ~LOCK_NB) { case LOCK_SH: flock.l_type = F_RDLCK; break; case LOCK_EX: flock.l_type = F_WRLCK; break; case LOCK_UN: flock.l_type = F_UNLCK; break; default: errno = EINVAL; return -1; } flock.l_whence = 0; flock.l_start = 0; flock.l_len = 0; return fcntl(fd, (operation & LOCK_NB) ? F_SETLK : F_SETLKW, &flock); } #endif /** * @defgroup OSyncGroupPrivateAPI OpenSync Group Internals * @ingroup OSyncPrivate * @brief The private API of opensync * * This gives you an insight in the private API of opensync. * */ /*@{*/ /*! @brief Returns the environment in which a group is registered * * @param group The group * @returns The environment * */ OSyncEnv *osync_group_get_env(OSyncGroup *group) { return group->env; } /*! @brief Gets the custom data of a group * * @param group The group * @returns The custom data of this group * */ void *osync_group_get_data(OSyncGroup *group) { return group->data; } /*! @brief Sets the custom data of a group * * @param group The group * @param data The custom data * */ void osync_group_set_data(OSyncGroup *group, void *data) { group->data = data; } /*! @brief Creates a new unique member if in this group * * @param group The group * @returns A new unique member id * */ long long int osync_group_create_member_id(OSyncGroup *group) { char *filename = NULL; long long int i = 0; do { i++; if (filename) g_free(filename); filename = g_strdup_printf("%s/%lli", group->configdir, i); } while (g_file_test(filename, G_FILE_TEST_EXISTS)); g_free(filename); return i; } /*! @brief Returns the format environment of a group * * @param group The group * @returns The format environment * */ OSyncFormatEnv *osync_group_get_format_env(OSyncGroup *group) { g_assert(group); return group->conv_env; } /*! @brief Loads all members of a group * * Loads all members of a group * * @param group The group * @param path The path from which to load the members * @param error Pointer to a error * @returns True if the members were loaded successfully, FALSE otherwise * */ osync_bool osync_group_load_members(OSyncGroup *group, const char *path, OSyncError **error) { GDir *dir = NULL; GError *gerror = NULL; char *filename = NULL; dir = g_dir_open(path, 0, &gerror); if (!dir) { osync_debug("OSGRP", 3, "Unable to open group configdir %s", gerror->message); osync_error_set(error, OSYNC_ERROR_IO_ERROR, "Unable to open group configdir %s", gerror->message); g_error_free (gerror); return FALSE; } const gchar *de = NULL; while ((de = g_dir_read_name(dir))) { filename = g_strdup_printf ("%s/%s", osync_group_get_configdir(group), de); if (!g_file_test(filename, G_FILE_TEST_IS_DIR) || g_file_test(filename, G_FILE_TEST_IS_SYMLINK) || g_pattern_match_simple(".*", de) || !strcmp("db", de)) { g_free(filename); continue; } if (!osync_member_load(group, filename, error)) { osync_debug("OSGRP", 0, "Unable to load one of the members"); g_free(filename); g_dir_close(dir); return FALSE; } g_free(filename); } g_dir_close(dir); return TRUE; } /*@}*/ /** * @defgroup OSyncGroupAPI OpenSync Groups * @ingroup OSyncPublic * @brief A groups represent several device or application that should be synchronized * */ /*@{*/ /*! @brief Creates a new group for the given environment * * Creates a newly allocated group * * @param env The environment for which to create the group. Might be NULL if you which to not add the group at the point of creation * @returns Pointer to a new group * */ OSyncGroup *osync_group_new(OSyncEnv *env) { OSyncGroup *group = g_malloc0(sizeof(OSyncGroup)); group->conv_env = osync_conv_env_new(env); if (env) { osync_env_append_group(env, group); group->env = env; } return group; } /*! @brief Frees the given group * * Frees the given group * * @param group The group * */ void osync_group_free(OSyncGroup *group) { g_assert(group); if (group->conv_env) osync_conv_env_free(group->conv_env); if (group->lock_fd) osync_group_unlock(group, FALSE); while (osync_group_nth_member(group, 0)) osync_member_free(osync_group_nth_member(group, 0)); if (group->env) osync_env_remove_group(group->env, group); if (group->name) g_free(group->name); if (group->configdir) g_free(group->configdir); g_free(group); } /*! @brief Locks a group * * Tries to acquire a lock for the given group. * * If the lock was successfully acquired, OSYNC_LOCK_OK will * be returned. * * If the lock was acquired, but a old lock file was detected, * OSYNC_LOCK_STALE will be returned. Use this to detect if the * last sync of this group was successfull, or if this something crashed. * If you get this answer you should perform a slow-sync * * If the group is locked, OSYNC_LOCKED is returned * * @param group The group * @returns if the lockfile was acquired * */ OSyncLockState osync_group_lock(OSyncGroup *group) { osync_trace(TRACE_ENTRY, "osync_group_lock(%p)", group); g_assert(group); g_assert(group->configdir); osync_bool exists = FALSE; osync_bool locked = FALSE; if (group->lock_fd) { osync_trace(TRACE_EXIT, "osync_group_lock: OSYNC_LOCKED, lock_fd existed"); return OSYNC_LOCKED; } char *lockfile = g_strdup_printf("%s/lock", group->configdir); osync_debug("GRP", 4, "locking file %s", lockfile); if (g_file_test(lockfile, G_FILE_TEST_EXISTS)) { osync_debug("GRP", 4, "locking group: file exists"); exists = TRUE; } if ((group->lock_fd = open(lockfile, O_CREAT | O_WRONLY, 00700)) == -1) { group->lock_fd = 0; osync_debug("GRP", 1, "error opening file: %s", strerror(errno)); g_free(lockfile); osync_trace(TRACE_EXIT_ERROR, "osync_group_lock: %s", strerror(errno)); return OSYNC_LOCK_STALE; } else { /* Set FD_CLOEXEC flags for the lock file descriptor. We don't want the * subprocesses created by plugins or the engine to keep holding the lock */ int oldflags = fcntl(group->lock_fd, F_GETFD); if (oldflags == -1) { osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, "Unable to get fd flags"); return OSYNC_LOCK_STALE; } if (fcntl(group->lock_fd, F_SETFD, oldflags|FD_CLOEXEC) == -1) { osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, "Unable to set fd flags"); return OSYNC_LOCK_STALE; } if (flock(group->lock_fd, LOCK_EX | LOCK_NB) == -1) { if (errno == EWOULDBLOCK) { osync_debug("GRP", 4, "locking group: is locked2"); locked = TRUE; close(group->lock_fd); group->lock_fd = 0; } else osync_debug("GRP", 1, "error setting lock: %s", strerror(errno)); } else osync_debug("GRP", 4, "Successfully locked"); } g_free(lockfile); if (!exists) { osync_trace(TRACE_EXIT, "osync_group_lock: OSYNC_LOCK_OK"); return OSYNC_LOCK_OK; } else { if (locked) { osync_trace(TRACE_EXIT, "osync_group_lock: OSYNC_LOCKED"); return OSYNC_LOCKED; } else { osync_trace(TRACE_EXIT, "osync_group_lock: OSYNC_LOCK_STALE"); return OSYNC_LOCK_STALE; } } } /*! @brief Unlocks a group * * if you set remove = FALSE, the lock file will not be removed * and the next call to osync_lock_group() for this group will * return OSYNC_LOCK_STALE. * * @param group The group * @param remove If the lockfile should be removed * */ void osync_group_unlock(OSyncGroup *group, osync_bool remove) { g_assert(group); g_assert(group->configdir); osync_debug("GRP", 4, "unlocking group %s", group->name); if (!group->lock_fd) { osync_debug("GRP", 1, "You have to lock the group before unlocking"); return; } if (flock(group->lock_fd, LOCK_UN) == -1) { osync_debug("GRP", 1, "error releasing lock: %s", strerror(errno)); return; } fsync(group->lock_fd); close(group->lock_fd); group->lock_fd = 0; if (remove) { char *lockfile = g_strdup_printf("%s/lock", group->configdir); unlink(lockfile); g_free(lockfile); } } /*! @brief Sets the name for the group * * Sets the name for a group * * @param group The group * @param name The name to set * */ void osync_group_set_name(OSyncGroup *group, const char *name) { g_assert(group); if (group->name) g_free(group->name); group->name = g_strdup(name); } /*! @brief Returns the name of a group * * Returns the name of a group * * @param group The group * @returns Name of the group * */ const char *osync_group_get_name(OSyncGroup *group) { g_assert(group); return group->name; } /*! @brief Saves the group to disc * * Saves the group to disc possibly creating the configdirectory * * @param group The group * @param error Pointer to a error struct * @returns TRUE on success, FALSE otherwise * */ osync_bool osync_group_save(OSyncGroup *group, OSyncError **error) { osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, group, error); g_assert(group); osync_assert_msg(group->env, "You must specify a Environment prior to saving the group"); if (!group->configdir) { group->id = _osync_env_create_group_id(group->env); group->configdir = g_strdup_printf("%s/group%lli", group->env->groupsdir, group->id); } char *filename = NULL; osync_debug("OSGRP", 3, "Trying to open configdirectory %s to save group %s", group->configdir, group->name); int i; if (!g_file_test(group->configdir, G_FILE_TEST_IS_DIR)) { osync_debug("OSGRP", 3, "Creating group configdirectory %s", group->configdir); if (mkdir(group->configdir, 0700)) { osync_error_set(error, OSYNC_ERROR_IO_ERROR, "Unable to create directory for group %s\n", group->name); goto error; } } filename = g_strdup_printf ("%s/syncgroup.conf", group->configdir); osync_debug("OSGRP", 3, "Saving group to file %s", filename); xmlDocPtr doc; doc = xmlNewDoc((xmlChar*)"1.0"); doc->children = xmlNewDocNode(doc, NULL, (xmlChar*)"syncgroup", NULL); //The filters GList *f; for (f = group->filters; f; f = f->next) { OSyncFilter *filter = f->data; xmlNodePtr child = xmlNewTextChild(doc->children, NULL, (xmlChar*)"filter", NULL); if (filter->sourcememberid) { char *sourcememberid = g_strdup_printf("%lli", filter->sourcememberid); xmlNewTextChild(child, NULL, (xmlChar*)"sourcemember", (xmlChar*)sourcememberid); g_free(sourcememberid); } if (filter->destmemberid) { char *destmemberid = g_strdup_printf("%lli", filter->destmemberid); xmlNewTextChild(child, NULL, (xmlChar*)"destmember", (xmlChar*)destmemberid); g_free(destmemberid); } if (filter->sourceobjtype) xmlNewTextChild(child, NULL, (xmlChar*)"sourceobjtype", (xmlChar*)filter->sourceobjtype); if (filter->destobjtype) xmlNewTextChild(child, NULL, (xmlChar*)"destobjtype", (xmlChar*)filter->destobjtype); if (filter->detectobjtype) xmlNewTextChild(child, NULL, (xmlChar*)"detectobjtype", (xmlChar*)filter->detectobjtype); if (filter->action) { char *action = g_strdup_printf("%i", filter->action); xmlNewTextChild(child, NULL, (xmlChar*)"action", (xmlChar*)action); g_free(action); } if (filter->function_name) xmlNewTextChild(child, NULL, (xmlChar*)"function_name", (xmlChar*)filter->function_name); if (filter->config) xmlNewTextChild(child, NULL, (xmlChar*)"config", (xmlChar*)filter->config); } xmlNewTextChild(doc->children, NULL, (xmlChar*)"groupname", (xmlChar*)group->name); char *tmstr = g_strdup_printf("%i", (int)group->last_sync); xmlNewTextChild(doc->children, NULL, (xmlChar*)"last_sync", (xmlChar*)tmstr); g_free(tmstr); xmlSaveFile(filename, doc); xmlFreeDoc(doc); g_free(filename); for (i = 0; i < osync_group_num_members(group); i++) { OSyncMember *member = osync_group_nth_member(group, i); if (!osync_member_save(member, error)) goto error; } osync_trace(TRACE_EXIT, "%s", __func__); return TRUE; error: osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); return FALSE; } /*! @brief Deletes a group from disc * * Deletes to group directories and removes it from its environment * * @param group The group * @param error Pointer to a error struct * @returns TRUE on success, FALSE otherwise * */ osync_bool osync_group_delete(OSyncGroup *group, OSyncError **error) { g_assert(group); char *delcmd = g_strdup_printf("rm -rf %s", group->configdir); if (system(delcmd)) { osync_error_set(error, OSYNC_ERROR_GENERIC, "Failed to delete group. command %s failed", delcmd); g_free(delcmd); return FALSE; } g_free(delcmd); osync_group_free(group); return TRUE; } /*! @brief Loads a group from a directory * * Loads a group from a directory * * @param env The environment in which to create the group. Can be NULL * @param path The path to the config directory of the group * @param error Pointer to a error struct * @returns Pointer to the loaded group * */ OSyncGroup *osync_group_load(OSyncEnv *env, const char *path, OSyncError **error) { g_assert(env); char *filename = NULL; char *real_path = NULL; osync_trace(TRACE_ENTRY, "osync_group_load(%p, %s, %p)", env, path, error); osync_debug("OSGRP", 3, "Trying to load group from directory %s", path); if (!g_path_is_absolute(path)) { real_path = g_strdup_printf("%s/%s", g_get_current_dir(), path); } else { real_path = g_strdup(path); } filename = g_strdup_printf("%s/syncgroup.conf", real_path); OSyncGroup *group = osync_group_new(env); group->configdir = real_path; xmlDocPtr doc; xmlNodePtr cur; xmlNodePtr filternode; if (!_osync_open_xml_file(&doc, &cur, filename, "syncgroup", error)) { osync_group_free(group); g_free(filename); osync_trace(TRACE_EXIT_ERROR, "osync_group_load"); return NULL; } while (cur != NULL) { if (!xmlStrcmp(cur->name, (const xmlChar *)"groupname")) group->name = (char*)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if (!xmlStrcmp(cur->name, (const xmlChar *)"last_sync")) group->last_sync = (time_t)atoi((char*)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1)); if (!xmlStrcmp(cur->name, (const xmlChar *)"filter")) { filternode = cur->xmlChildrenNode; OSyncFilter *filter = osync_filter_new(); filter->group = group; while (filternode != NULL) { if (!xmlStrcmp(filternode->name, (const xmlChar *)"sourceobjtype")) filter->sourceobjtype = (char*)xmlNodeListGetString(doc, filternode->xmlChildrenNode, 1); if (!xmlStrcmp(filternode->name, (const xmlChar *)"destobjtype")) filter->destobjtype = (char*)xmlNodeListGetString(doc, filternode->xmlChildrenNode, 1); if (!xmlStrcmp(filternode->name, (const xmlChar *)"detectobjtype")) filter->detectobjtype = (char*)xmlNodeListGetString(doc, filternode->xmlChildrenNode, 1); if (!xmlStrcmp(filternode->name, (const xmlChar *)"config")) filter->config = (char*)xmlNodeListGetString(doc, filternode->xmlChildrenNode, 1); if (!xmlStrcmp(filternode->name, (const xmlChar *)"function_name")) { char *str = (char*)xmlNodeListGetString(doc, filternode->xmlChildrenNode, 1); if (!str) { filternode = filternode->next; continue; } osync_filter_update_hook(filter, group, str); xmlFree(str); } if (!xmlStrcmp(filternode->name, (const xmlChar *)"sourcemember")) { char *str = (char*)xmlNodeListGetString(doc, filternode->xmlChildrenNode, 1); if (!str) { filternode = filternode->next; continue; } filter->sourcememberid = atoll(str); xmlFree(str); } if (!xmlStrcmp(filternode->name, (const xmlChar *)"destmember")) { char *str = (char*)xmlNodeListGetString(doc, filternode->xmlChildrenNode, 1); if (!str) { filternode = filternode->next; continue; } filter->destmemberid = atoll(str); xmlFree(str); } if (!xmlStrcmp(filternode->name, (const xmlChar *)"action")) { char *str = (char*)xmlNodeListGetString(doc, filternode->xmlChildrenNode, 1); if (!str) { filternode = filternode->next; continue; } filter->action = atoi(str); xmlFree(str); } filternode = filternode->next; } osync_filter_register(group, filter); } cur = cur->next; } xmlFreeDoc(doc); g_free(filename); //Check for sanity if (!group->name) { osync_error_set(error, OSYNC_ERROR_MISCONFIGURATION, "Loaded a group without a name"); osync_debug("OSGRP", 0, "Loaded a group without a name"); osync_group_free(group); osync_trace(TRACE_EXIT_ERROR, "osync_group_load"); return NULL; } if (!osync_group_load_members(group, real_path, error)) { osync_group_free(group); osync_trace(TRACE_EXIT_ERROR, "osync_group_load"); return NULL; } osync_trace(TRACE_EXIT, "osync_group_load"); return group; } /*! @brief Resets all databases of a group * * This will reset all databases of a group. So all anchors, mappings * hashtables etc will be forgotten (as if the group was never synced) * * @param group The group to reset * */ void osync_group_reset(OSyncGroup *group) { OSyncError *error = NULL; osync_db_reset_group(group, &error); GList *m = NULL; for (m = group->members; m; m = m->next) { OSyncMember *member = m->data; osync_db_reset_member(member, &error); } } /*! @brief Appends a member to the group * * Appends a member to the group * * @param group The group to which to append * @param member The member to append * */ void osync_group_add_member(OSyncGroup *group, OSyncMember *member) { g_assert(group); group->members = g_list_append(group->members, member); } /*! @brief Removes a member from the group * * @param group The group from which to remove * @param member The member to remove * */ void osync_group_remove_member(OSyncGroup *group, OSyncMember *member) { g_assert(group); group->members = g_list_remove(group->members, member); } /*! @brief Returns the nth member of the group * * Returns a pointer to the nth member of the group * * @param group The group * @param nth Which member to return * @returns Pointer to the member * */ OSyncMember *osync_group_nth_member(OSyncGroup *group, int nth) { g_assert(group); return (OSyncMember *)g_list_nth_data(group->members, nth); } /*! @brief Counts the members of the group * * Returns the number of members in the group * * @param group The group * @returns Number of members * */ int osync_group_num_members(OSyncGroup *group) { g_assert(group); return g_list_length(group->members); } /*! @brief Returns the configdir for the group * * Returns the configdir for the group * * @param group The group * @returns String with the path of the config directory * */ const char *osync_group_get_configdir(OSyncGroup *group) { g_assert(group); return group->configdir; } /*! @brief Sets the configdir of the group * * @param group The group * @param directory The new configdir * @returns String with the path of the config directory * */ void osync_group_set_configdir(OSyncGroup *group, const char *directory) { g_assert(group); if (group->configdir) g_free(group->configdir); group->configdir = g_strdup(directory); } /*! @brief Sets if the group requires slow-sync for the given object type * * Sets if the group requires slow-sync for the given object type. This will be * reset once the group performs a successfull slow-sync. * * @param group The group * @param objtypestr The name of the object type * @param slow_sync Set to TRUE if you want to perform a slow-sync, FALSE otherwise * */ void osync_group_set_slow_sync(OSyncGroup *group, const char *objtypestr, osync_bool slow_sync) { osync_trace(TRACE_ENTRY, "%s(%p, %s, %i)", __func__, group, objtypestr, slow_sync); g_assert(group); OSyncFormatEnv *conv_env = group->conv_env; //FIXME Remove the slow_sync bool since you are not allowed to reset //the slow-sync manually anyways. //FIXME Race Condition!!! if (!osync_group_get_slow_sync(group, objtypestr)) { if (osync_conv_objtype_is_any(objtypestr)) { GList *element; for (element = conv_env->objtypes; element; element = element->next) { OSyncObjType *objtype = element->data; objtype->needs_slow_sync = slow_sync; } } else { OSyncObjType *objtype = osync_conv_find_objtype(conv_env, objtypestr); g_assert(objtype); objtype->needs_slow_sync = slow_sync; } } osync_trace(TRACE_EXIT, "%s", __func__); } /** @brief Reset slow-sync for this group * * You can use this function to reset the slow-sync status for the given group. This is normally * done if a synchronization succeeds. * * @param group The group to reset slow-sync on * @param objtypestr The name of the object type */ void osync_group_reset_slow_sync(OSyncGroup *group, const char *objtypestr) { osync_trace(TRACE_ENTRY, "%s(%p, %s)", __func__, group, objtypestr); g_assert(group); OSyncFormatEnv *conv_env = group->conv_env; if (osync_conv_objtype_is_any(objtypestr)) { GList *element; for (element = conv_env->objtypes; element; element = element->next) { OSyncObjType *objtype = element->data; objtype->needs_slow_sync = FALSE; } } else { OSyncObjType *objtype = osync_conv_find_objtype(conv_env, objtypestr); g_assert(objtype); objtype->needs_slow_sync = FALSE; } osync_trace(TRACE_EXIT, "%s", __func__); } /*! @brief Returns if the group will perform a slow-sync for the object type * * Returns if the group will perform a slow-sync for the object type * * @param group The group * @param objtype The name of the object type * @returns TRUE if a slow-sync will be performed, FALSE otherwise * */ osync_bool osync_group_get_slow_sync(OSyncGroup *group, const char *objtype) { osync_trace(TRACE_ENTRY, "%s(%p, %s)", __func__, group, objtype); g_assert(group); OSyncFormatEnv *env = group->conv_env; g_assert(env); OSyncObjType *osync_objtype = osync_conv_find_objtype(env, "data"); if (osync_objtype && osync_objtype->needs_slow_sync) { osync_trace(TRACE_EXIT, "%s: Data objtype needs slow-sync", __func__); return TRUE; } osync_objtype = osync_conv_find_objtype(env, objtype); g_assert(osync_objtype); osync_trace(TRACE_EXIT, "%s: %i", __func__, osync_objtype->needs_slow_sync); return osync_objtype->needs_slow_sync; } /*! @brief Returns if the object type is enabled for the group * * Returns TRUE if the object type is enabled for the group. Note that this * information is saved on a per member basis. If one of the members has this object type enabled * this function will return TRUE * * @param group The group * @param objtype The name of the object type * @returns TRUE if the object type is enabled for at least one member. FALSE if for none * */ osync_bool osync_group_objtype_enabled(OSyncGroup *group, const char *objtype) { //FIXME We should actually return a 3-state here. //0 if none is enabled //"0.5" if some are enabled, some are not //1 if all are enabled g_assert(group); GList *m; for (m = group->members; m; m = m->next) { OSyncMember *member = m->data; if (osync_member_objtype_enabled(member, objtype)) return TRUE; } return FALSE; } /*! @brief Sets if the object type is accepted for ALL members * * BUG We loose information if only some members are enabled * * @param group The group * @param objtypestr The name of the object type * @param enabled What do you want to set today? * * Note: the plugin needs to be instanced for this function to be called * * @todo Change interface to remove requirement to instance the plugin manually. * It needs to be able to return error in order to load the plugin */ void osync_group_set_objtype_enabled(OSyncGroup *group, const char *objtypestr, osync_bool enabled) { g_assert(group); GList *m; for (m = group->members; m; m = m->next) { OSyncMember *member = m->data; /*TODO: What this function should do if we don't have * any objtype sink information? * It can't return error currently. We should either * require that the plugin is instanced, or change the function * interface. As changing the function interface require more * care, currently the function is marked as requiring the plugin to be instanced */ if (!osync_member_require_sink_info(member, NULL)) { osync_debug("OSGRP", 0, "%s: No sink information, can't load plugin, and I can't return error"); continue; } osync_member_set_objtype_enabled(member, objtypestr, enabled); } } /*! @brief Returns the number of filters registered in a group * * @param group The group * @returns The number of filters * */ int osync_group_num_filters(OSyncGroup *group) { g_assert(group); return g_list_length(group->filters); } /*! @brief Gets the nth filter of a group * * Note that you should not add or delete filters while * iterating over them * * @param group The group * @param nth Which filter to return * @returns The filter or NULL if not found * */ OSyncFilter *osync_group_nth_filter(OSyncGroup *group, int nth) { g_assert(group); return g_list_nth_data(group->filters, nth); } /*! @brief Flushes the list of filters for a group * * Clean the list of filters on the group */ void osync_group_flush_filters(OSyncGroup *group) { g_assert(group); while (group->filters) { OSyncFilter *f = g_list_nth_data(group->filters, 0); osync_filter_free(f); /* Delete the first item */ group->filters = g_list_delete_link(group->filters, group->filters); } } /*! @brief Can be used to load all items from the changelog. Loaded items will be removed * * @param group The group for which to load the log * @param uids Place to return an array with the saved uids * @param objtype Place to return an array with the saved objtypes * @param memberids Place to return an array with the saved memberids * @param changetypes Place to return an array with the saved changetypes. Same size as uids * @param error Place to return the error * @returns TRUE if successfull, FALSE otherwise */ osync_bool osync_group_open_changelog(OSyncGroup *group, char ***uids, char ***objtype, long long int **memberids, int **changetypes, OSyncError **error) { return osync_db_open_changelog(group, uids, objtype, memberids, changetypes, error); } /*! @brief Saves a change to the changelog. * * @param group The group in which to save * @param change The change to save * @param error Place to return the error * @returns TRUE if successfull, FALSE otherwise */ osync_bool osync_group_save_changelog(OSyncGroup *group, OSyncChange *change, OSyncError **error) { return osync_db_save_changelog(group, change, error); } /*! @brief Removes a change from the changelog. * * @param group The group in which to save * @param change The change to remove * @param error Place to return the error * @returns TRUE if successfull, FALSE otherwise */ osync_bool osync_group_remove_changelog(OSyncGroup *group, OSyncChange *change, OSyncError **error) { return osync_db_remove_changelog(group, change, error); } /*! @brief Sets the last synchronization date of this group * * The information will be stored on disc after osync_group_save() * * @param group The group in which to save * @param tm The time info to set */ void osync_group_set_last_synchronization(OSyncGroup *group, time_t last_sync) { osync_trace(TRACE_ENTRY, "%s(%p, not shown)", __func__, last_sync); osync_assert_msg(group, "Group missing"); group->last_sync = last_sync; osync_trace(TRACE_EXIT, "%s", __func__); } /*! @brief Gets the last synchronization date from this group * * The information will available on the group after osync_group_load() * * @param group The group * @return The synchronization info */ time_t osync_group_get_last_synchronization(OSyncGroup *group) { osync_assert_msg(group, "Group missing"); return group->last_sync; } /*@}*/