osync_bool osync_sink_engine_convert_to_dest(OSyncSinkEngine *engine, OSyncFormatEnv *formatenv, OSyncError **error)
{
    OSyncList *o;
    OSyncMember *member;
    OSyncObjTypeSink *objtype_sink;
    const char *objtype;
    OSyncFormatConverterPath *path = NULL;

    osync_assert(engine);
    osync_assert(formatenv);

    member = osync_client_proxy_get_member(engine->proxy);
    osync_assert(member);

    objtype = osync_obj_engine_get_objtype(engine->engine);
    objtype_sink = osync_member_find_objtype_sink(member, objtype);
    osync_assert(objtype_sink);

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

        if (entry_engine->change == NULL)
            continue;

        /* If change not meant to get written (change shared among multiple "same" mapping
           entry engines), prevents conversions see #1207 */
        if (!osync_entry_engine_is_dirty(entry_engine))
            continue;

        if (osync_change_get_changetype(entry_engine->change) == OSYNC_CHANGE_TYPE_DELETED)
            continue;

        if (!osync_entry_engine_convert(entry_engine, formatenv, objtype_sink, &path, error))
            goto error;
    }

    if (path)
        osync_converter_path_unref(path);


    return TRUE;

error:
    if (path)
        osync_converter_path_unref(path);

    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;
}
예제 #3
0
static OSyncFormatConverterPath *osync_format_env_find_path_fn(OSyncFormatEnv *env, OSyncData *sourcedata, OSyncPathTargetFn target_fn, OSyncTargetLastConverterFn last_converter_fn, const void *fndata, const char * preferred_format, OSyncError **error)
{
  OSyncFormatConverterPath *path = NULL;
  OSyncFormatConverterTree *tree = NULL;
  OSyncFormatConverterPathVertice *begin = NULL;
  OSyncFormatConverterPathVertice *result = NULL;
  OSyncFormatConverterPathVertice *neighbour = NULL;
  GList *e, *v;
  guint vertice_id = 0;
	
  osync_trace(TRACE_ENTRY, "%s(%p, %p, %p, %p, %p)", __func__, env, sourcedata, target_fn, fndata, error);
  osync_assert(env);
  osync_assert(sourcedata);
  osync_assert(target_fn);
	
  //Vertice = Spitze = Format
  //edge = Kante = Converter

  /* Optimization: check if the format is already valid */
  if (target_fn(fndata, osync_data_get_objformat(sourcedata))) {
    path = osync_converter_path_new(error);
    if (!path)
      goto error;
		
    osync_trace(TRACE_EXIT, "%s: Target already valid", __func__);
    return path;
  }

  /* Make a new search tree */
  tree = osync_try_malloc0(sizeof(OSyncFormatConverterTree), error);
  if (!tree)
    goto error;
  tree->unused = g_list_copy(env->converters);
	
  /* We make our starting point (which is the current format of the 
   * change of course */
  begin = osync_format_converter_path_vertice_new(error);
  if (!begin)
    goto error_free_tree;
	
  begin->format = osync_data_get_objformat(sourcedata);
  begin->path = NULL;
  begin->id = vertice_id;
  begin->neighbour_id = 0;
	
  tree->search = g_list_append(NULL, begin);
	
  /* While there are still vertices in our
   * search queue */
  while (g_list_length(tree->search)) {
    /* log current tree search list */
    GString *string = g_string_new("");
    guint size = g_list_length(tree->search);
    guint count = 0;
    guint neighbour_id = 0;
    OSyncFormatConverterPathVertice *current = NULL;
    OSyncFormatConverterPath *path_tmp = NULL;
    for (v = tree->search; v; v = v->next) {
      OSyncFormatConverterPathVertice *vertice = v->data;
      GString *string2 = g_string_new("");
      guint size2 = g_list_length(vertice->path);
      guint count2 = 0;
      count ++;
      for (e = vertice->path; e; e = e->next) {
        OSyncFormatConverter *edge = e->data;
        count2 ++;
        if (count2 == 1) {
          g_string_append(string2, osync_objformat_get_name(osync_converter_get_sourceformat(edge)));
          g_string_append(string2, " -> ");
        }
        g_string_append(string2, osync_objformat_get_name(osync_converter_get_targetformat(edge)));
        if (size2 > 1 && count2 < size2)
          g_string_append(string2, " -> ");
      }
      g_string_append(string, osync_objformat_get_name(vertice->format));
      g_string_append(string, " ( ");
      g_string_append(string, string2->str);
      g_string_append(string, " ) ");

      g_string_free(string2, TRUE);

      if (size > 1 && count < size)
        g_string_append(string, " -> ");
    }
    osync_trace(TRACE_INTERNAL, "Tree : %s", string->str);
    g_string_free(string, TRUE);

    /* Get the first OSyncFormatConverterPathVertice from the search queue
     * and remove it from the queue */
    current = tree->search->data;
    tree->search = g_list_remove(tree->search, current);
		
    /* log current OSyncFormatConverterPathVertice */
    string = g_string_new("");
    size = g_list_length(current->path);
    count = 0;
    for (e = current->path; e; e = e->next) {
      OSyncFormatConverter *edge = e->data;
      count ++;
      if (count == 1) {
        g_string_append(string, osync_objformat_get_name(osync_converter_get_sourceformat(edge)));
        g_string_append(string, " -> ");
      }
      g_string_append(string, osync_objformat_get_name(osync_converter_get_targetformat(edge)));
      if (size > 1 && count < size)
        g_string_append(string, " -> ");
    }
    osync_trace(TRACE_INTERNAL, "Next vertice : %s (%s).", osync_objformat_get_name(current->format), string->str);
    g_string_free(string, TRUE);

    current->neighbour_id = 0;
    vertice_id++; // current OSyncFormatConverterPathVertice id for its neighbours

    /* Check if we have reached a target format */
    if (target_fn(fndata, current->format)) {
      osync_trace(TRACE_INTERNAL, "Target %s found", osync_objformat_get_name(current->format));
      /* Done. return the result */
      result = current;
      break;
    }
		
    /*
     * Optimizations : 
     */
    if (last_converter_fn(fndata, tree)) {
      osync_trace(TRACE_INTERNAL, "Last converter for target format reached: %s.", (result)?osync_objformat_get_name(result->format):"null");
      osync_format_converter_path_vertice_unref(current);
      break;
    }
    /* Check if saved result is equal to current regarding losses, objtype_changes
     * and conversions. If yes, we can skip further searches and break here */
    if (result) {
      if (result->losses <= current->losses && result->objtype_changes <= current->objtype_changes && result->conversions <= current->conversions) {
        osync_trace(TRACE_INTERNAL, "Target %s found in queue", osync_objformat_get_name(result->format));
        tree->search = g_list_remove(tree->search, result);
        break;
      } else {
        result = NULL;
      }
    }


    /*
     * If we dont have reached a target, we look at our neighbours 
     */
    osync_trace(TRACE_INTERNAL, "Looking at %s's neighbours.", osync_objformat_get_name(current->format));

    /* Convert the "current" data to the last edge found in the "current" conversion path  */
    current->data = osync_data_clone(sourcedata, error);
    path_tmp = osync_converter_path_new(error);
    if (!path_tmp)
      goto error;
    for (e = current->path; e; e = e->next) {
      OSyncFormatConverter *edge = e->data;
      osync_converter_path_add_edge(path_tmp, edge);
    }
    if (!(osync_format_env_convert(env, path_tmp, current->data, error))) {
      osync_trace(TRACE_INTERNAL, "osync format env convert on this path failed - skipping the conversion");
      continue;
    }
    osync_converter_path_unref(path_tmp);

    /* Find all the neighboors or "current" at its current conversion point */
    while ((neighbour = osync_format_converter_path_vertice_get_next_vertice_neighbour(env, tree, current, error))) {
      GString *string = g_string_new("");
      guint size = g_list_length(neighbour->path);
      guint count = 0;

      neighbour->id = vertice_id;
      neighbour_id++;
      neighbour->neighbour_id = neighbour_id;

      neighbour->preferred = FALSE;
      if (current->preferred)	  /* preferred is inherited by the neighbours */
        neighbour->preferred = TRUE;
      if(preferred_format && !strcmp(preferred_format, osync_objformat_get_name(neighbour->format)))
        neighbour->preferred = TRUE;

      /* log neighbour to be added to the tree search list */
      for (e = neighbour->path; e; e = e->next) {
        OSyncFormatConverter *edge = e->data;
        count ++;
        if (count == 1) {
          g_string_append(string, osync_objformat_get_name(osync_converter_get_sourceformat(edge)));
          g_string_append(string, " -> ");
        }
        g_string_append(string, osync_objformat_get_name(osync_converter_get_targetformat(edge)));
        if (size > 1 && count < size)
          g_string_append(string, " -> ");
      }
      osync_trace(TRACE_INTERNAL, "%s's neighbour : %s (%s)", osync_objformat_get_name(current->format), osync_objformat_get_name(neighbour->format), string->str);
      g_string_free(string, TRUE);

      /* We found a neighbour and insert it sorted in our search queue 
         If vertices are equals in losses, objtypes and conversions, first registered is inserted before the others 
         in the same OSyncFormatConverterPathVertice group (vertice_id) */
      tree->search = g_list_insert_sorted(tree->search, neighbour, osync_format_converter_path_vertice_compare_distance); 

      /* Optimization:
       * We found a possible target. Save it. */
      if (target_fn(fndata, neighbour->format)) {
        osync_trace(TRACE_INTERNAL, "Possible target found.");
        result = neighbour;
        osync_format_converter_path_vertice_ref(result);
      }
    }

    if (osync_error_is_set(error))
      goto error_free_tree;
		
    /* Done, drop the reference to the OSyncFormatConverterPathVertice */
    osync_format_converter_path_vertice_unref(current);
  }
			
  if (!result) {
    osync_error_set(error, OSYNC_ERROR_GENERIC, "Unable to find conversion path");
    goto error_free_tree;
  }
	
  /* Found it. Create a path object */
  path = osync_converter_path_new(error);
  if (!path)
    goto error;
	
  for (e = result->path; e; e = e->next) {
    OSyncFormatConverter *edge = e->data;
    osync_converter_path_add_edge(path, edge);
  }
	
  /* Drop the reference to the result OSyncFormatConverterPathVertice */
  osync_format_converter_path_vertice_unref(result);
	
  /* Free the tree */
  osync_converter_tree_free(tree);
	
  osync_trace(TRACE_EXIT, "%s: %p", __func__, path);
  return path;

 error_free_tree:
  osync_converter_tree_free(tree);
 error:
  osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
  return NULL;
}