void osync_plugin_info_unref(OSyncPluginInfo *info)
{
	osync_assert(info);
	
	if (g_atomic_int_dec_and_test(&(info->ref_count))) {
		if (info->config)
			osync_plugin_config_unref(info->config);
		
		if (info->configdir)
			osync_free(info->configdir);
		
		if (info->groupname)
			osync_free(info->groupname);
		
		while (info->objtype_sinks) {
			OSyncObjTypeSink *sink = info->objtype_sinks->data;
			osync_objtype_sink_unref(sink);
			info->objtype_sinks = osync_list_remove(info->objtype_sinks, sink);
		}
		
		if (info->main_sink)
			osync_objtype_sink_unref(info->main_sink);
			
		if (info->version)
			osync_version_unref(info->version);
			
		if (info->capabilities)
			osync_capabilities_unref(info->capabilities);

		if (info->formatenv)
			osync_format_env_unref(info->formatenv);
		
		osync_free(info);
	}
}
static void free_env(plugin_environment *env)
{
	while (env->sink_envs) {
		sink_environment *sinkenv = env->sink_envs->data;

		if (sinkenv->sink)
			osync_objtype_sink_unref(sinkenv->sink);

		env->sink_envs = osync_list_remove(env->sink_envs, sinkenv);
	}

	osync_free(env);
}
void osync_sink_engine_unref(OSyncSinkEngine *engine)
{
    osync_assert(engine);

    if (g_atomic_int_dec_and_test(&(engine->ref_count))) {
        while (engine->unmapped) {
            OSyncChange *change = engine->unmapped->data;
            osync_change_unref(change);

            engine->unmapped = osync_list_remove(engine->unmapped, engine->unmapped->data);
        }

        while (engine->entries) {
            OSyncMappingEntryEngine *entry = engine->entries->data;
            osync_entry_engine_unref(entry);

            engine->entries = osync_list_remove(engine->entries, engine->entries->data);
        }

        osync_obj_engine_unref(engine->engine);

        osync_free(engine);
    }
}
void osync_capabilities_objtype_unref(OSyncCapabilitiesObjType *capsobjtype)
{
	osync_assert(capsobjtype);
			
	if (g_atomic_int_dec_and_test(&(capsobjtype->ref_count))) {

		while (capsobjtype->capabilities) {
			osync_capability_unref(capsobjtype->capabilities->data);
			capsobjtype->capabilities = osync_list_remove(capsobjtype->capabilities, capsobjtype->capabilities->data);
		}

		osync_free(capsobjtype->name);

		osync_free(capsobjtype);
	}
}
void osync_capabilities_unref(OSyncCapabilities *caps)
{
	osync_assert(caps);
			
	if (g_atomic_int_dec_and_test(&(caps->ref_count))) {
		while (caps->objtypes) {
			osync_capabilities_objtype_unref(caps->objtypes->data);
			caps->objtypes = osync_list_remove(caps->objtypes, caps->objtypes->data);
		}

		osync_free(caps->format);
		osync_xml_free_doc(caps->doc);

		osync_free(caps);
	}
}
/** Report files on a directory
 *
 * NOTE: If 'dir' is non-empty it MUST start it a slash. This is just
 * to make easier concatenation of the paths, and we can just concatenate
 * fsinfo->path and subdir to get the complete path.
 *
 * @param dir The fsinfo->path subdirectory that should be reported. Use
 *            an empty string to report files on fsinfo->path. Should
 *            start with a slash. See note above.
 *
 */
static void mock_report_dir(MockDir *directory, const char *subdir, OSyncContext *ctx, OSyncPluginInfo *info, OSyncObjTypeSink *sink)
{
	GError *gerror = NULL;
	const char *de = NULL;
	char *path = NULL;
	GDir *dir = NULL;
	OSyncError *error = NULL;
	OSyncList *sorted_dir_list = NULL;

	osync_trace(TRACE_ENTRY, "%s(%p, %s, %p, %p)", __func__, directory, subdir, ctx, sink);

	OSyncHashTable *hashtable = osync_objtype_sink_get_hashtable(sink);
	OSyncFormatEnv *formatenv = osync_plugin_info_get_format_env(info);
	osync_assert(formatenv);

	path = g_build_filename(directory->path, subdir, NULL);
	osync_trace(TRACE_INTERNAL, "path %s", path);

	dir = g_dir_open(path, 0, &gerror);
	osync_assert(dir);

	while((de = g_dir_read_name(dir))) {
		sorted_dir_list = osync_list_insert_sorted(sorted_dir_list,
			g_strdup(de), (OSyncCompareFunc)strcmp);
	}

	g_dir_close(dir);

	while(sorted_dir_list) {
		de = sorted_dir_list->data;
		char *filename = g_build_filename(path, de, NULL);
		char *relative_filename = NULL;
		if (!subdir)
			relative_filename = g_strdup(de);
		else
			relative_filename = g_build_filename(subdir, de, NULL);
		g_free(sorted_dir_list->data);
		sorted_dir_list = osync_list_remove(sorted_dir_list, sorted_dir_list->data);

		osync_trace(TRACE_INTERNAL, "path2 %s %s", filename, relative_filename);

		if (g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {

			struct stat buf;
			stat(filename, &buf);
			char *hash = mock_generate_hash(&buf);

			/* Report normal files */
			OSyncChange *change = osync_change_new(&error);
			osync_assert(change);

			osync_change_set_uid(change, relative_filename);

			osync_change_set_hash(change, hash);
			g_free(hash);

			OSyncChangeType type = osync_hashtable_get_changetype(hashtable, change);

			osync_change_set_changetype(change, type);
			osync_hashtable_update_change(hashtable, change);

			if (type == OSYNC_CHANGE_TYPE_UNMODIFIED) {
				g_free(filename);
				g_free(relative_filename);
				osync_change_unref(change);
				continue;
			}

			OSyncFileFormat *file = osync_try_malloc0(sizeof(OSyncFileFormat), &error);
			osync_assert(file);

			file->path = g_strdup(relative_filename);

			OSyncData *odata = NULL;

			if (!mock_get_error(info->memberid, "ONLY_INFO")) {
				osync_assert(osync_file_read(filename, &(file->data), &(file->size), &error));

				if (mock_get_error(info->memberid, "SLOW_REPORT"))
					g_usleep(1*G_USEC_PER_SEC);

				odata = osync_data_new((char *)file, sizeof(OSyncFileFormat), directory->objformat, &error);
				osync_assert(odata);


				osync_change_set_data(change, odata);

			}

			osync_data_set_objtype(odata, osync_objtype_sink_get_name(sink));
			osync_data_unref(odata);

			osync_context_report_change(ctx, change);

			osync_change_unref(change);
		}

		g_free(filename);
		g_free(relative_filename);

	}

	g_free(path);
	osync_trace(TRACE_EXIT, "%s", __func__);
}