Пример #1
0
void check_mapping(OSyncMappingTable *maptable, int memberid, int mappingid, unsigned int numentries, const char *uid)
{
	unsigned int i = 0;
	osync_trace(TRACE_ENTRY, "%s(%p, %i, %i, %i, %s)", __func__, maptable, memberid, mappingid, numentries, uid);
	
	for (i = 0; i < osync_mapping_table_num_mappings(maptable); i++) {
		OSyncMapping *mapping = osync_mapping_table_nth_mapping(maptable, i);
		OSyncMappingEntry *testentry = osync_mapping_find_entry_by_member_id(mapping, memberid);
		if (testentry) {
			if ((mappingid != -1 && osync_mapping_get_id(mapping) == mappingid) || (mappingid == -1 && !strcmp(osync_mapping_entry_get_uid(testentry), uid))) {
				unsigned int n = 0;
				fail_unless(osync_mapping_num_entries(mapping) == numentries);
				for (n = 0; n < osync_mapping_num_entries(mapping); n++) {
					OSyncMappingEntry *entry = osync_mapping_nth_entry(mapping, n);
					if (osync_mapping_entry_get_member_id(entry) == memberid) {
						fail_unless(!strcmp(osync_mapping_entry_get_uid(entry), uid), NULL);
						goto out;
					}
				}
				fail(NULL);
			}
		}
	}
	fail(NULL);

out:
	osync_trace(TRACE_EXIT, "%s", __func__);
}
osync_bool osync_mapping_engine_ignore(OSyncMappingEngine *engine, OSyncError **error)
{
  OSyncObjEngine *objengine = NULL;
  OSyncArchive *archive = NULL;
  char *objtype = NULL;
  long long int id = 0;
  GList *c = NULL;
  osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, engine, error);
	
  engine->conflict = FALSE;
  engine->synced = TRUE;

  objengine = engine->parent;
  archive = objengine->archive;
  objtype = objengine->objtype;
  id = osync_mapping_get_id(engine->mapping);

  for (c = engine->entries; c; c = c->next) {
    OSyncMappingEntryEngine *entry = c->data;
    osync_archive_save_ignored_conflict(archive, objtype, id, osync_change_get_changetype(entry->change), error);
  }

  osync_status_update_mapping(engine->parent->parent, engine, OSYNC_MAPPING_EVENT_SOLVED, NULL);
  engine->parent->conflicts = g_list_remove(engine->parent->conflicts, engine);
	
  if (osync_engine_check_get_changes(engine->parent->parent) && osync_bitcount(engine->parent->sink_errors | engine->parent->sink_get_changes) == g_list_length(engine->parent->sink_engines)) {
    if (!osync_obj_engine_command(engine->parent, 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:
  osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
  return FALSE;
}
osync_bool osync_mapping_engine_multiply(OSyncMappingEngine *engine, OSyncError **error)
{
  GList *e = NULL;
  osync_assert(engine);
  osync_assert(engine->mapping);
	
  osync_trace(TRACE_ENTRY, "%s(%p(%lli), %p)", __func__, engine, osync_mapping_get_id(engine->mapping), error);
		
  if (engine->synced) {
    osync_trace(TRACE_EXIT, "%s: No need to multiply. Already synced", __func__);
    return TRUE;
  }

  if (!engine->master) {
    osync_error_set(error, OSYNC_ERROR_GENERIC, "No master set");
    goto error;
  }

  for (e = engine->entries; e; e = e->next) {
    OSyncChange *existChange = NULL, *masterChange = NULL;
    OSyncData *masterData = NULL, *newData = NULL;
    OSyncMappingEntryEngine *entry_engine = e->data;
    OSyncChangeType existChangeType = 0, newChangeType = 0;
    if (entry_engine == engine->master)
      continue;
		
    osync_trace(TRACE_INTERNAL, "Propagating change %s to %p from %p", osync_mapping_entry_get_uid(entry_engine->entry), entry_engine, engine->master);
		
    /* Input is:
     * masterChange -> change that solved the mapping
     * masterData -> data of masterChange
     * existChange -> change that will be overwritten (if any) */
		
    existChange = entry_engine->change;
    masterChange = osync_entry_engine_get_change(engine->master);
    masterData = osync_change_get_data(masterChange);
		
    /* Clone the masterData. This has to be done since the data
     * might get changed (converted) and we dont want to touch the 
     * original data */
    newData = osync_data_clone(masterData, error);
    if (!newData)
      goto error;
		
    if (!existChange) {
      existChange = osync_change_new(error);
      if (!existChange)
        goto error;
			
      osync_change_set_changetype(existChange, OSYNC_CHANGE_TYPE_UNKNOWN);
    } else {
      /* Ref the change so that we can unref it later */
      osync_change_ref(existChange);
    }
		
    /* Save the changetypes, so that we can calculate the correct changetype later */
    existChangeType = osync_change_get_changetype(existChange);
    newChangeType = osync_change_get_changetype(masterChange);
		
    osync_trace(TRACE_INTERNAL, "Orig change type: %i New change type: %i", existChangeType, newChangeType);

    /* Now update the entry with the change */
    osync_entry_engine_update(entry_engine, existChange);
		
    /* We have to use the uid of the entry, so that the member
     * can correctly identify the entry 
     *
     * prahal: added a check if the entry has a uid to send the existing uid
     * to the plugins in case we have a slow-sync (both are added and have a uid) 
     * This to avoid creating duplicates by sending the plugins a different uid 
     * with the same or merged data 
     *
     * dgollub: enhanced the check if the entry has a uid to send the existing uid
     * to the plugins in case we have a slow-sync - both have changetype ADDED - and
     * for odd plugins/protocolls which mark new entries as MODIFIED all the time.
     * Changetype MODIFIED of new entries has atleast the IrMC plugin and likely some
     * SE SyncML implementation...
     * 
     * ^^irmc hacks in the irmc plugin ;-)
     *
     * dgollub: Set masterChange UID for existChange if entry_engine->entry doesn't have 
     * mapping uid, even if the newChangeType is UNKOWN. Bug: #571  
     *
     * prahal : rely on the fact that there are no mapping nor the entry existed to detect new change
     *	    Also avoid changing the id of the change if one existed and there where no mapping :
     *	    this way we send the id known to the member in case of a "modify". Fixing syncml plugin
     *	    freezing the phone and mozilla sync receiving an id it cannot do anything with for modify
     *	    in slow sync (no existing mapping).
     *
     */
    if ((!osync_mapping_entry_get_uid(entry_engine->entry) && !osync_change_get_uid(existChange))  ) 
      osync_change_set_uid(existChange, osync_change_get_uid(masterChange));
    else if(osync_mapping_entry_get_uid(entry_engine->entry)) 
      osync_change_set_uid(existChange, osync_mapping_entry_get_uid(entry_engine->entry));

    osync_change_set_data(existChange, newData);
    osync_change_set_changetype(existChange, osync_change_get_changetype(masterChange));
		
    /* We also have to update the changetype of the new change */
    if (newChangeType == OSYNC_CHANGE_TYPE_ADDED && (existChangeType != OSYNC_CHANGE_TYPE_DELETED && existChangeType != OSYNC_CHANGE_TYPE_UNKNOWN)) {
      osync_trace(TRACE_INTERNAL, "Updating change type to MODIFIED");
      osync_change_set_changetype(existChange, OSYNC_CHANGE_TYPE_MODIFIED);
      /* Only adapt the change to ADDED if the existing Change got deleted. Don't update it to ADDED if existChangeType is UNKOWN.
         The exitChangeType is at least also UNKOWN if the file-sync has only one modified entry. */
    } else if (newChangeType == OSYNC_CHANGE_TYPE_MODIFIED && (existChangeType == OSYNC_CHANGE_TYPE_DELETED)) {
      osync_trace(TRACE_INTERNAL, "Updating change type to ADDED");
      osync_change_set_changetype(existChange, OSYNC_CHANGE_TYPE_ADDED);
    }
		
    osync_change_unref(existChange);
    /* Also unref newData. Otherwise this cannot be freed when it is written. */
    osync_data_unref(newData);
			
    osync_entry_engine_set_dirty(entry_engine, TRUE);
  }
	
  osync_trace(TRACE_EXIT, "%s", __func__);
  return TRUE;

 error:
  osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
  return FALSE;
}
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;
}