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_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; }