OSyncMappingEngine *_osync_obj_engine_create_mapping_engine(OSyncObjEngine *engine, OSyncError **error)
{
  /* If there is none, create one */
  OSyncMapping *mapping = osync_mapping_new(error);
  GList *s = NULL;
  OSyncMappingEngine *mapping_engine = NULL;
  if (!mapping)
    goto error;
	
  osync_mapping_set_id(mapping, osync_mapping_table_get_next_id(engine->mapping_table));
  osync_mapping_table_add_mapping(engine->mapping_table, mapping);
	
  for (s = engine->sink_engines; s; s = s->next) {
    OSyncSinkEngine *sink_engine = s->data;
		
    OSyncMember *member = osync_client_proxy_get_member(sink_engine->proxy);
		
    OSyncMappingEntry *mapping_entry = osync_mapping_entry_new(error);
    osync_mapping_entry_set_member_id(mapping_entry, osync_member_get_id(member));
    osync_mapping_add_entry(mapping, mapping_entry);
    osync_mapping_entry_unref(mapping_entry);
  }
	
  mapping_engine = osync_mapping_engine_new(engine, mapping, error);
  if (!mapping_engine)
    goto error_free_mapping;
  osync_mapping_unref(mapping);
	
  return mapping_engine;
	
 error_free_mapping:
  osync_mapping_unref(mapping);
 error:
  return NULL;
}
/** @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_group_find_member(OSyncGroup *group, int id)
{
  GList *m = NULL;
  for (m = group->members; m; m = m->next) {
    OSyncMember *member = m->data;
    if (osync_member_get_id(member) == id)
      return member;
  }
  return NULL;
}
static osync_bool check_mappings(OSyncEngine *engine)
{
	member_info *meminfo = NULL;
	GList *m;
	for (m = members; m; m = m->next) {
		meminfo = m->data;

		if (g_list_length(engine->maptable->mappings) != g_list_length(meminfo->changes)) {
			printf("Number of mappings do not match for member %lli, %i compared to %i\n", osync_member_get_id(meminfo->member), g_list_length(engine->maptable->mappings), g_list_length(meminfo->changes));
			return FALSE;
		}
	}
	
	return TRUE;
}
/** @brief Solves the conflict by duplicating the conflicting entries
 * 
 * @param engine The engine
 * @param dupe_mapping The conflicting mapping to duplicate
 * 
 */
osync_bool osync_mapping_engine_duplicate(OSyncMappingEngine *existingMapping, OSyncError **error)
{
  int elevation = 0;
  OSyncObjEngine *objengine = NULL;
  GList *entries = NULL, *e = NULL, *mappings = NULL;

  osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, existingMapping, error);
  g_assert(existingMapping);
	
  objengine = existingMapping->parent;
	
  /* Remove all deleted items first and copy the changes to a list */
  e = existingMapping->entries;
  for (; e; e = e->next) {
    OSyncMappingEntryEngine *entry = e->data;
    if (entry->change) {
      if (osync_change_get_changetype(entry->change) == OSYNC_CHANGE_TYPE_MODIFIED || osync_change_get_changetype(entry->change) == OSYNC_CHANGE_TYPE_ADDED) {
        osync_trace(TRACE_INTERNAL, "Appending entry %s, changetype %i from member %lli", osync_change_get_uid(entry->change), osync_change_get_changetype(entry->change), osync_member_get_id(osync_client_proxy_get_member(entry->sink_engine->proxy)));
		
        entries = g_list_append(entries, entry);
      } else {
        osync_trace(TRACE_INTERNAL, "Removing entry %s, changetype %i from member %lli", osync_change_get_uid(entry->change), osync_change_get_changetype(entry->change), osync_member_get_id(osync_client_proxy_get_member(entry->sink_engine->proxy)));
        osync_entry_engine_update(entry, NULL);
      }
    } else {
      osync_trace(TRACE_INTERNAL, "member %lli does not have a entry", osync_member_get_id(osync_client_proxy_get_member(entry->sink_engine->proxy)));
    }
  }
	
  /* Create a list with mappings. In the beginning, only the exisiting mapping is in the list */
  mappings = g_list_append(NULL, existingMapping);
  osync_mapping_engine_ref(existingMapping);
	
  while (entries) {
    OSyncMappingEntryEngine *existingEntry = entries->data;
		
    /* Now lets see which mapping is the correct one for the entry */
    GList *m = NULL;
    OSyncMappingEngine *mapping = NULL;
    OSyncChange *existingChange = NULL;
    osync_bool dirty = FALSE;
    OSyncMappingEntryEngine *newEntry = NULL;

    elevation = 0;
    for (m = mappings; m; m = m->next) {
      GList *e = NULL;
      OSyncChange *change = NULL;
      OSyncMappingEntryEngine *entry = NULL;
      mapping = m->data;
			
      /* Get the first change of the mapping to test. Compare the given change with this change.
       * If they are not the same, we have found a new mapping */
      for (e = mapping->entries; e; e = e->next) {
        entry = e->data;
        change = entry->change;
        if (change)
          break;
      }
			
      if (!change || osync_change_compare(existingEntry->change, change) == OSYNC_CONV_DATA_SAME){
        existingChange = existingEntry->change;
        osync_change_ref(existingChange);
        osync_assert(osync_change_get_uid(existingChange));
        break;
      }


      mapping = NULL;
      elevation++;
		
      existingChange = osync_change_clone(existingEntry->change, error);
      osync_assert(osync_change_get_uid(existingChange));
    }
		
		
    if (!mapping) {
      /* Unable to find a mapping. We have to create a new one */
      mapping = _osync_obj_engine_create_mapping_engine(objengine, error);
      if (!mapping)
        goto error;
      mappings = g_list_append(mappings, mapping);
      objengine->mapping_engines = g_list_append(objengine->mapping_engines, mapping);
      osync_mapping_engine_ref(mapping);
    }
		
    /* update the uid and the content to suit the new level */
    if (!_osync_change_elevate(existingChange, elevation, &dirty, error))
      goto error;

    /* Lets add the entry to the mapping */
    newEntry = osync_mapping_engine_get_entry(mapping, existingEntry->sink_engine);
    osync_assert(newEntry);
    osync_entry_engine_update(newEntry, existingChange);
    osync_mapping_entry_set_uid(newEntry->entry, osync_change_get_uid(existingChange));
    osync_change_unref(existingChange);
		
    /* Set the last entry as the master */
    osync_mapping_engine_set_master(mapping, newEntry);
		
    /* Update the dirty status. If the duplicate function said
     * that the returned item needs to be written, we will set
     * this information here */
    newEntry->dirty = dirty;
		
    entries = g_list_remove(entries, existingEntry);
  }
	
	
  while (mappings) {
    OSyncMappingEngine *mapping = mappings->data;
    osync_mapping_engine_unref(mapping);
    mappings = g_list_remove(mappings, mapping);
  }
	
  objengine->conflicts = g_list_remove(objengine->conflicts, existingMapping);
  osync_status_update_mapping(objengine->parent, existingMapping, OSYNC_MAPPING_EVENT_SOLVED, NULL);
	
  if (osync_engine_check_get_changes(objengine->parent) && osync_bitcount(objengine->sink_errors | objengine->sink_get_changes) == g_list_length(objengine->sink_engines)) {
    if (!osync_obj_engine_command(objengine, OSYNC_ENGINE_COMMAND_WRITE, error))
      goto error;
  } else
    osync_trace(TRACE_INTERNAL, "Not triggering write. didnt receive all reads yet");

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

 error:
  while (mappings) {
    OSyncMappingEngine *mapping = mappings->data;
    osync_mapping_engine_unref(mapping);
    mappings = g_list_remove(mappings, mapping);
  }
  osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
  return FALSE;
}
OSyncMappingEngine *osync_mapping_engine_new(OSyncObjEngine *parent, OSyncMapping *mapping, OSyncError **error)
{
  OSyncMappingEngine *engine = NULL;
  GList *s = NULL;
  osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parent, mapping, error);

  osync_assert(parent);
  osync_assert(mapping);
	
  engine = osync_try_malloc0(sizeof(OSyncMappingEngine), error);
  if (!engine)
    goto error;
  engine->ref_count = 1;
	
  engine->mapping = mapping;
  osync_mapping_ref(mapping);
	
  engine->parent = parent;
  engine->synced = TRUE;
	
  for (s = parent->sink_engines; s; s = s->next) {
    OSyncSinkEngine *sink_engine = s->data;
    OSyncMappingEntryEngine *entry_engine  = NULL;
		
    OSyncMember *member = osync_client_proxy_get_member(sink_engine->proxy);
    OSyncMappingEntry *mapping_entry = osync_mapping_find_entry_by_member_id(mapping, osync_member_get_id(member));
    osync_assert(mapping_entry);
		
    entry_engine = osync_entry_engine_new(mapping_entry, engine, sink_engine, parent, error);
    if (!entry_engine)
      goto error_free_engine;

    engine->entries = g_list_append(engine->entries, entry_engine);
  }
	
  osync_trace(TRACE_EXIT, "%s: %p", __func__, engine);
  return engine;

 error_free_engine:
  osync_mapping_engine_unref(engine);
 error:
  osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
  return NULL;
}
osync_bool osync_sink_engine_write(OSyncSinkEngine *engine, OSyncArchive *archive, OSyncError **error)
{
    OSyncList *o;
    const char *objtype;
    OSyncMember *member;

    osync_assert(engine);
    osync_assert(archive);

    objtype = osync_obj_engine_get_objtype(engine->engine);
    member = osync_client_proxy_get_member(engine->proxy);

    for (o = engine->entries; o; o = o->next) {
        OSyncMappingEntryEngine *entry_engine = o->data;
        osync_assert(entry_engine);

        if (osync_entry_engine_is_dirty(entry_engine)) {
            OSyncChange *change = osync_entry_engine_get_change(entry_engine);
            osync_assert(change);

            osync_trace(TRACE_INTERNAL, "Writing change %s, changetype %i, format %s , objtype %s from member %i",
                        osync_change_get_uid(change),
                        osync_change_get_changetype(change),
                        osync_objformat_get_name(osync_change_get_objformat(change)),
                        osync_change_get_objtype(change),
                        osync_member_get_id(member));

            if (!osync_client_proxy_commit_change(engine->proxy,
                                                  osync_obj_engine_commit_change_callback,
                                                  entry_engine, change, error))
                goto error;

        } else if (entry_engine->change) {
            OSyncMapping *mapping = entry_engine->mapping_engine->mapping;
            OSyncMappingEntry *entry = entry_engine->entry;

            /* FIXME: Don't mix up in this function objtypes */
            /* osync_assert_msg(!strcmp(objtype, osync_change_get_objtype(entry_engine->change), "Mixed-objtype in final write!")); */

            if (osync_change_get_changetype(entry_engine->change) == OSYNC_CHANGE_TYPE_DELETED) {
                if (!osync_archive_delete_change(archive, osync_mapping_entry_get_id(entry),
                                                 osync_change_get_objtype(entry_engine->change), error))
                    goto error;
            } else {
                if (!osync_archive_save_change(archive,
                                               osync_mapping_entry_get_id(entry),
                                               osync_change_get_uid(entry_engine->change),
                                               osync_change_get_objtype(entry_engine->change),
                                               osync_mapping_get_id(mapping),
                                               osync_member_get_id(member), objtype, error))
                    goto error;
            }
        }
    }

    if (!osync_client_proxy_committed_all(engine->proxy, osync_obj_engine_written_callback, engine, objtype, error))
        goto error;

    return TRUE;

error:
    return FALSE;
}
osync_bool osync_obj_engine_command(OSyncObjEngine *engine, OSyncEngineCmd cmd, OSyncError **error)
{
  GList *p = NULL;
  GList *m = NULL;
  GList *e = NULL;
  OSyncSinkEngine *sinkengine =  NULL;

	
  osync_trace(TRACE_ENTRY, "%s(%p, %i, %p)", __func__, engine, cmd, error);
  osync_assert(engine);
	
  switch (cmd) {
    int write_sinks = 0;
    osync_bool proxy_disconnect = FALSE;
  case OSYNC_ENGINE_COMMAND_CONNECT:
    for (p = engine->sink_engines; p; p = p->next) {
      sinkengine = p->data;

      if (!osync_client_proxy_connect(sinkengine->proxy, _osync_obj_engine_connect_callback, sinkengine, engine->objtype, engine->slowsync, error))
        goto error;
    }
    break;
  case OSYNC_ENGINE_COMMAND_READ:
    for (p = engine->sink_engines; p; p = p->next) {
      sinkengine = p->data;
      for (m = sinkengine->entries; m; m = m->next) {
        OSyncMappingEntryEngine *entry = m->data;
        OSyncChange *change = entry->change;

        if (!change)
          continue;

        if (!osync_client_proxy_read(sinkengine->proxy, _osync_obj_engine_read_ignored_callback, sinkengine, change, error))
          goto error;
      }
    }

    if (engine->archive) {
      /* Flush the changelog - to avoid double entries of ignored entries */
      if (!osync_archive_flush_ignored_conflict(engine->archive, engine->objtype, error))
        goto error;
    }

    write_sinks = _osync_obj_engine_num_write_sinks(engine);

    /* Get change entries since last sync. (get_changes) */
    for (p = engine->sink_engines; p; p = p->next) {
      OSyncMember *member = NULL;
      OSyncObjTypeSink *objtype_sink = NULL;

      sinkengine = p->data;

      member = osync_client_proxy_get_member(sinkengine->proxy);
      objtype_sink = osync_member_find_objtype_sink(member, engine->objtype);

      /* Is there at least one other writeable sink? */
      if (objtype_sink && osync_objtype_sink_get_write(objtype_sink) && write_sinks) {
        _osync_obj_engine_read_callback(sinkengine->proxy, sinkengine, *error);
        osync_trace(TRACE_INTERNAL, "no other writable sinks .... SKIP");
        continue;
      }

      if (!osync_client_proxy_get_changes(sinkengine->proxy, _osync_obj_engine_read_callback, sinkengine, engine->objtype, engine->slowsync, error))
        goto error;
    }

    break;
  case OSYNC_ENGINE_COMMAND_WRITE:
    if (engine->conflicts) {
      osync_trace(TRACE_INTERNAL, "We still have conflict. Delaying write");
      break;
    }
		
    if (engine->written) {
      osync_trace(TRACE_INTERNAL, "Already written");
      break;
    }
				
    engine->written = TRUE;
		
    /* Write the changes. First, we can multiply the winner in the mapping */
    osync_trace(TRACE_INTERNAL, "Preparing write. multiplying %i mappings", g_list_length(engine->mapping_engines));
    for (m = engine->mapping_engines; m; m = m->next) {
      OSyncMappingEngine *mapping_engine = m->data;
      if (!osync_mapping_engine_multiply(mapping_engine, error))
        goto error;
    }
			
    osync_trace(TRACE_INTERNAL, "Starting to write");
    for (p = engine->sink_engines; p; p = p->next) {
      OSyncMember *member = NULL;
      long long int memberid = 0;
      OSyncObjTypeSink *objtype_sink = NULL;
      OSyncFormatConverterPath *path = NULL;

      sinkengine = p->data;
      member = osync_client_proxy_get_member(sinkengine->proxy);
      memberid = osync_member_get_id(member);
      objtype_sink = osync_member_find_objtype_sink(member, engine->objtype);
				
      /* If sink could not be found use "data" sink if available */
      if (!objtype_sink)
        objtype_sink = osync_member_find_objtype_sink(member, "data");
      /* TODO: Review if objtype_sink = NULL is valid at all. */

      for (e = sinkengine->entries; e; e = e->next) {
        OSyncMappingEntryEngine *entry_engine = e->data;
        osync_assert(entry_engine);

        /* Merger - Save the entire xml and demerge */
        /* TODO: is here the right place to save the xml???? */
        if (osync_group_get_merger_enabled(osync_engine_get_group(engine->parent)) &&
            osync_group_get_converter_enabled(osync_engine_get_group(engine->parent)) &&	
            entry_engine->change &&
            (osync_change_get_changetype(entry_engine->change) != OSYNC_CHANGE_TYPE_DELETED) &&
            !strncmp(osync_objformat_get_name(osync_change_get_objformat(entry_engine->change)), "xmlformat-", 10) )
          {
            char *buffer = NULL;
            unsigned int xmlformat_size = 0, size = 0;
            OSyncXMLFormat *xmlformat = NULL;
            const char *objtype = NULL;
            OSyncMapping *mapping = NULL;
            OSyncMerger *merger = NULL; 

            osync_trace(TRACE_INTERNAL, "Entry %s for member %lli: Dirty: %i", osync_change_get_uid(entry_engine->change), memberid, osync_entry_engine_is_dirty(entry_engine));

            osync_trace(TRACE_INTERNAL, "Save the entire XMLFormat and demerge.");
            objtype = osync_change_get_objtype(entry_engine->change);
            mapping = entry_engine->mapping_engine->mapping;
						
            osync_data_get_data(osync_change_get_data(entry_engine->change), (char **) &xmlformat, &xmlformat_size);
            osync_assert(xmlformat_size == osync_xmlformat_size());

            if(!osync_xmlformat_assemble(xmlformat, &buffer, &size)) {
              osync_error_set(error, OSYNC_ERROR_GENERIC, "Could not assamble the xmlformat");
              goto error;	
            }

            if(!osync_archive_save_data(engine->archive, osync_mapping_get_id(mapping), objtype, buffer, size, error)) {
              g_free(buffer);	
              goto error;			
            }
            g_free(buffer);
						
            merger = osync_member_get_merger(osync_client_proxy_get_member(sinkengine->proxy));
            if(merger)
              osync_merger_demerge(merger, xmlformat);
          }


        /* Only commit change if the objtype sink is able/allowed to write. */
        if (objtype_sink && osync_objtype_sink_get_write(objtype_sink) && osync_entry_engine_is_dirty(entry_engine)) {
          OSyncChange *change = entry_engine->change;
          osync_assert(entry_engine->change);

          /* Convert to requested target format if the changetype is not DELETED */
          if (osync_group_get_converter_enabled(osync_engine_get_group(engine->parent)) && (osync_change_get_changetype(change) != OSYNC_CHANGE_TYPE_DELETED)) {

            char *objtype = NULL;
            OSyncList *format_sinks = NULL;
            unsigned int length = 0;
            OSyncFormatConverter *converter = NULL;

            osync_trace(TRACE_INTERNAL, "Starting to convert from objtype %s and format %s", osync_change_get_objtype(entry_engine->change), osync_objformat_get_name(osync_change_get_objformat(entry_engine->change)));
            /* We have to save the objtype of the change so that it does not get
             * overwritten by the conversion */
            objtype = g_strdup(osync_change_get_objtype(change));
							
            /* Now we have to convert to one of the formats
             * that the client can understand */
            format_sinks = osync_objtype_sink_get_objformat_sinks(objtype_sink);
            if (!format_sinks) {
              osync_error_set(error, OSYNC_ERROR_GENERIC, "There are no available format sinks.");
              goto error;
            }
						
            /* We cache the converter path for each sink/member couple */
            if (!path) {
              path = osync_format_env_find_path_formats_with_detectors(engine->formatenv, osync_change_get_data(entry_engine->change), format_sinks, osync_objtype_sink_get_preferred_format(objtype_sink), error);
            }
            if (!path)
              goto error;

            length = osync_converter_path_num_edges(path);
            converter = osync_converter_path_nth_edge(path, length - 1);
            if (converter) {
              OSyncObjFormat *format = osync_converter_get_targetformat(converter);
              OSyncObjFormatSink *formatsink = osync_objtype_sink_find_objformat_sink(objtype_sink, format);
              osync_converter_path_set_config(path, osync_objformat_sink_get_config(formatsink));
            }

            if (!osync_format_env_convert(engine->formatenv, path, osync_change_get_data(entry_engine->change), error)) {
              osync_converter_path_unref(path);
              goto error;
            }
            osync_trace(TRACE_INTERNAL, "converted to format %s", osync_objformat_get_name(osync_change_get_objformat(entry_engine->change)));
							
							
            osync_change_set_objtype(change, objtype);
            g_free(objtype);
          }
						
          osync_trace(TRACE_INTERNAL, "Writing change %s, changetype %i, format %s , objtype %s from member %lli", 
                      osync_change_get_uid(change), 
                      osync_change_get_changetype(change), 
                      osync_objformat_get_name(osync_change_get_objformat(change)), 
                      osync_change_get_objtype(change), 
                      osync_member_get_id(osync_client_proxy_get_member(sinkengine->proxy)));
	
          if (!osync_client_proxy_commit_change(sinkengine->proxy, _osync_obj_engine_commit_change_callback, entry_engine, osync_entry_engine_get_change(entry_engine), error))
            goto error;
        } else if (entry_engine->change) {
          OSyncMapping *mapping = entry_engine->mapping_engine->mapping;
          OSyncMember *member = osync_client_proxy_get_member(sinkengine->proxy);
          OSyncMappingEntry *entry = entry_engine->entry;
          const char *objtype = osync_change_get_objtype(entry_engine->change);
						
          if (engine->archive) {
            if (osync_change_get_changetype(entry_engine->change) == OSYNC_CHANGE_TYPE_DELETED) {
              if (!osync_archive_delete_change(engine->archive, osync_mapping_entry_get_id(entry), objtype, error))
                goto error;
            } else {
              if (!osync_archive_save_change(engine->archive, osync_mapping_entry_get_id(entry), osync_change_get_uid(entry_engine->change), objtype, osync_mapping_get_id(mapping), osync_member_get_id(member), error))
                goto error;
            }
          }
        }
      }
			
      if (path)
        osync_converter_path_unref(path);

      if (!osync_client_proxy_committed_all(sinkengine->proxy, _osync_obj_engine_written_callback, sinkengine, engine->objtype, error))
        goto error;
    }
    break;
  case OSYNC_ENGINE_COMMAND_SYNC_DONE:
    for (p = engine->sink_engines; p; p = p->next) {
      sinkengine = p->data;
      if (!osync_client_proxy_sync_done(sinkengine->proxy, _osync_obj_engine_sync_done_callback, sinkengine, engine->objtype, error))
        goto error;
    }
    break;
  case OSYNC_ENGINE_COMMAND_DISCONNECT:;
    for (p = engine->sink_engines; p; p = p->next) {
      sinkengine = p->data;

      /* Don't call client disconnect functions if the sink is already disconnected.
         This avoids unintended disconnect calls of clients/plugins which might not prepared
         for a disconnect call when their never got connected. (testcases: *_connect_error, *_connect_timeout ..) */
      if (!osync_sink_engine_is_connected(sinkengine))
        continue;

      proxy_disconnect = TRUE;

      if (!osync_client_proxy_disconnect(sinkengine->proxy, _osync_obj_engine_disconnect_callback, sinkengine, engine->objtype, error))
        goto error;
    }
			
    /* If no client needs to be disconnected, we MUST NOT expected any 
       disconnected_callback which generates an OSYNC_ENGINE_EVENT_DISCONNECTED event.
       So we directly generate such event on our own. (testcases: double_connect_*, triple_connnect_*) */ 
    if (!proxy_disconnect)
      _osync_obj_engine_generate_event_disconnected(engine, NULL);

    break;
  case OSYNC_ENGINE_COMMAND_SOLVE:
  case OSYNC_ENGINE_COMMAND_DISCOVER:
  case OSYNC_ENGINE_COMMAND_ABORT:
    break;
  }
	
  osync_trace(TRACE_EXIT, "%s", __func__);
  return TRUE;

 error:
  osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
  return FALSE;
}
static void _osync_obj_engine_commit_change_callback(OSyncClientProxy *proxy, void *userdata, const char *uid, OSyncError *error)
{
  OSyncMappingEntryEngine *entry_engine = userdata;
  OSyncObjEngine *engine = entry_engine->objengine;
  OSyncSinkEngine *sinkengine = entry_engine->sink_engine;
  OSyncError *locerror = NULL;
  OSyncMapping *mapping = NULL;
  OSyncMember *member = NULL;
  OSyncMappingEntry *entry = NULL;
  const char *objtype = NULL;
  long long int id = 0;

	
  osync_trace(TRACE_ENTRY, "%s(%p, %p, %s, %p)", __func__, proxy, userdata, uid, error);
	
  osync_entry_engine_set_dirty(entry_engine, FALSE);
	
  mapping = entry_engine->mapping_engine->mapping;
  member = osync_client_proxy_get_member(proxy);
  entry = entry_engine->entry;
  objtype = osync_change_get_objtype(entry_engine->change);
  id = osync_mapping_entry_get_id(entry);
	
  if (error) {
    /* Error handling (tests: single_commit_error, ...) */

    /* TODO: Review differences between Mapping and Change status events - Are both really needed?! */
    osync_status_update_change(engine->parent, entry_engine->change, osync_client_proxy_get_member(proxy), entry_engine->mapping_engine->mapping, OSYNC_CHANGE_EVENT_ERROR, error);
    osync_status_update_mapping(engine->parent, entry_engine->mapping_engine, OSYNC_MAPPING_EVENT_ERROR, error);

    osync_obj_engine_set_error(engine, error);
    engine->sink_errors = engine->sink_errors | (0x1 << sinkengine->position);
    goto error;
  }
	
  if (uid)
    osync_change_set_uid(entry_engine->change, uid);
	
  if (engine->archive) {
    if (osync_change_get_changetype(entry_engine->change) == OSYNC_CHANGE_TYPE_DELETED) {
      /* TODO error handling */
      osync_archive_delete_change(engine->archive, id, objtype, &locerror);
    } else {

      /* TODO error handling */
      osync_archive_save_change(engine->archive, id, osync_change_get_uid(entry_engine->change), objtype, osync_mapping_get_id(mapping), osync_member_get_id(member), &locerror);
    }
  }

  osync_assert(entry_engine->mapping_engine);
  osync_status_update_change(engine->parent, entry_engine->change, osync_client_proxy_get_member(proxy), entry_engine->mapping_engine->mapping, OSYNC_CHANGE_EVENT_WRITTEN, NULL);
  osync_entry_engine_update(entry_engine, NULL);
	
  osync_trace(TRACE_EXIT, "%s", __func__);
  return;

 error:	
  _osync_obj_engine_generate_written_event(engine, error);
  osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(&error));
}
osync_bool osync_obj_engine_map_changes(OSyncObjEngine *engine, OSyncError **error)
{
  OSyncMappingEngine *mapping_engine = NULL;
  GList *new_mappings = NULL, *v = NULL;
	
  osync_trace(TRACE_ENTRY, "%s(%p)", __func__, engine);
  //osync_trace_disable();

  /* Go through all sink engines that are available */
  for (v = engine->sink_engines; v; v = v->next) {
    OSyncSinkEngine *sinkengine = v->data;
		
    /* We use a temp list to speed things up. We dont have to compare with newly created mappings for
     * the current sinkengine, since there will be only one entry (for the current sinkengine) so there
     * is no need to compare */
    new_mappings = NULL;
		
    /* For each sinkengine, go through all unmapped changes */
    while (sinkengine->unmapped) {
      OSyncChange *change = sinkengine->unmapped->data;
      OSyncConvCmpResult result = 0;
      OSyncMappingEntryEngine *entry_engine = NULL;
			
      osync_trace(TRACE_INTERNAL, "Looking for mapping for change %s, changetype %i from member %lli", osync_change_get_uid(change), osync_change_get_changetype(change), osync_member_get_id(osync_client_proxy_get_member(sinkengine->proxy)));
	
      /* See if there is an exisiting mapping, which fits the unmapped change */
      result = _osync_obj_engine_mapping_find(engine, change, sinkengine, &mapping_engine);
      if (result == OSYNC_CONV_DATA_MISMATCH) {
        /* If there is none, create one */
        mapping_engine = _osync_obj_engine_create_mapping_engine(engine, error);
        if (!mapping_engine)
          goto error;
				
        osync_trace(TRACE_INTERNAL, "Unable to find mapping. Creating new mapping with id %lli", osync_mapping_get_id(mapping_engine->mapping));
				
        new_mappings = g_list_append(new_mappings, mapping_engine);
      } else if (result == OSYNC_CONV_DATA_SIMILAR) {
        mapping_engine->conflict = TRUE;
      }
      /* Update the entry which belongs to our sinkengine with the the change */
      entry_engine = osync_mapping_engine_get_entry(mapping_engine, sinkengine);
      osync_assert(entry_engine);
			
      osync_entry_engine_update(entry_engine, change);
      sinkengine->unmapped = g_list_remove(sinkengine->unmapped, sinkengine->unmapped->data);
      osync_change_unref(change);
    }
		
    engine->mapping_engines = g_list_concat(engine->mapping_engines, new_mappings);
  }
	
  //osync_trace_enable();
  osync_trace(TRACE_EXIT, "%s", __func__);
  return TRUE;

 error:
  osync_trace_enable();
  osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
  return FALSE;
}
static osync_bool check_hashtables(OSyncEngine *engine)
{
	GList *m;
	for (m = members; m; m = m->next) {
		member_info *meminfo = m->data;
		OSyncHashTable *table = osync_hashtable_new();
		osync_hashtable_load(table, meminfo->member, NULL);
		/*if (osync_hashtable_num_entries(table) != g_list_length(meminfo->changes)) {
			printf("Hashtable for member %i has wrong number %i compared to %i\n", osync_member_get_id(meminfo->member), osync_hashtable_num_entries(table), g_list_length(meminfo->changes));
			abort();
		}*/
		printf("hashtable %p\n", table);
		if (osync_hashtable_num_entries(table) && g_list_length(engine->maptable->mappings) != osync_hashtable_num_entries(table)) {
			printf("Number of mappings do not match hastable for member %lli, %i compared to %i\n", osync_member_get_id(meminfo->member), g_list_length(engine->maptable->mappings), osync_hashtable_num_entries(table));
			return FALSE;
		}
		
		osync_hashtable_close(table);
	}
	return TRUE;
}
void change_content(OSyncEngine *engine)
{
	GList *m;
	for (m = members; m; m = m->next) {
		member_info *meminfo = m->data;
		
		busy = TRUE;
		osync_member_connect(meminfo->member, (OSyncEngCallback)stress_message_callback, NULL);
		if (busy)
			g_main_loop_run(loop);
		
		GList *c = NULL;
		for (c = meminfo->changes; c; c = c->next) {
			change_info *chinfo = c->data;
			if (g_random_int_range(0, 3) == 0) {
				switch (g_random_int_range(1, 6)) {
					case 1:
					case 5:
						if (osync_member_modify_random_data(meminfo->member, chinfo->change)) {
							printf("Modifying data %s for member %lli. Objtype: %s Format: %s\n", osync_change_get_uid(chinfo->change), osync_member_get_id(meminfo->member), osync_objtype_get_name(osync_change_get_objtype(chinfo->change)), osync_objformat_get_name(osync_change_get_objformat(chinfo->change)));
							chinfo->type = CHANGE_MODIFIED;
						}
						break;
					case 2:
						if (osync_member_delete_data(meminfo->member, chinfo->change)) {
							printf("Deleting data %s for member %lli. Objtype: %s Format: %s\n", osync_change_get_uid(chinfo->change), osync_member_get_id(meminfo->member), osync_objtype_get_name(osync_change_get_objtype(chinfo->change)), osync_objformat_get_name(osync_change_get_objformat(chinfo->change)));
							if (!osync_group_get_slow_sync(engine->group, osync_objtype_get_name(osync_change_get_objtype(chinfo->change))));
								chinfo->type = CHANGE_DELETED;
						}
						break;
					case 3:
						//printf("Modifying all for %s\n", osync_change_get_uid(change));
						break;
					case 4:
						//printf("Deleting all for %s\n", osync_change_get_uid(change));
						break;
					default:
						printf("error\n");
				}
			}
		}
		
		int num_new = g_random_int_range(0, 8);
		int n = 0;;
		for (n = 0; n < num_new; n++) {
			change_info *chinfo = g_malloc0(sizeof(change_info));
			if ((chinfo->change = osync_member_add_random_data(meminfo->member, NULL))) {
				if (find_change_info(meminfo, chinfo->change))
					continue;
				meminfo->changes = g_list_append(meminfo->changes, chinfo);
				chinfo->type = CHANGE_ADDED;
				chinfo->uid = g_strdup(osync_change_get_uid(chinfo->change));
				printf("Adding new data %s for member %lli. Objtype: %s Format: %s\n", osync_change_get_uid(chinfo->change), osync_member_get_id(meminfo->member), osync_objtype_get_name(osync_change_get_objtype(chinfo->change)), osync_objformat_get_name(osync_change_get_objformat(chinfo->change)));
			}
		}
		
		busy = TRUE;
		osync_member_disconnect(meminfo->member, (OSyncEngCallback)stress_message_callback, NULL);
		if (busy)
			g_main_loop_run(loop);
	}
}
$NetBSD$

Don't continue the if block.

--- tools/osyncstress.c.orig	2009-09-20 20:14:18.000000000 +0000
+++ tools/osyncstress.c
@@ -184,7 +184,7 @@ void change_content(OSyncEngine *engine)
 					case 2:
 						if (osync_member_delete_data(meminfo->member, chinfo->change)) {
 							printf("Deleting data %s for member %lli. Objtype: %s Format: %s\n", osync_change_get_uid(chinfo->change), osync_member_get_id(meminfo->member), osync_objtype_get_name(osync_change_get_objtype(chinfo->change)), osync_objformat_get_name(osync_change_get_objformat(chinfo->change)));
-							if (!osync_group_get_slow_sync(engine->group, osync_objtype_get_name(osync_change_get_objtype(chinfo->change))));
+							if (!osync_group_get_slow_sync(engine->group, osync_objtype_get_name(osync_change_get_objtype(chinfo->change))))
 								chinfo->type = CHANGE_DELETED;
 						}
 						break;