예제 #1
0
osync_bool osync_xml_open_file(xmlDocPtr *doc, xmlNodePtr *cur, const char *path, const char *topentry, OSyncError **error)
{
	if (!g_file_test(path, G_FILE_TEST_EXISTS)) {
		osync_error_set(error, OSYNC_ERROR_IO_ERROR, "File %s does not exist", path);
		return FALSE;
	}
	
	*doc = xmlParseFile(path);
	if (!*doc) {
		osync_error_set(error, OSYNC_ERROR_IO_ERROR, "Could not open: %s", path);
		goto error;
	}

	*cur = xmlDocGetRootElement(*doc);
	if (!*cur) {
		osync_error_set(error, OSYNC_ERROR_IO_ERROR, "%s seems to be empty", path);
		goto error_free_doc;
	}

	if (xmlStrcmp((*cur)->name, (const xmlChar *) topentry)) {
		osync_error_set(error, OSYNC_ERROR_IO_ERROR, "%s seems not to be a valid configfile.\n", path);
		goto error_free_doc;
	}

	*cur = (*cur)->xmlChildrenNode;
	return TRUE;

 error_free_doc:
	osync_xml_free_doc(*doc);
 error:
	osync_trace(TRACE_ERROR, "%s: %s", __func__, osync_error_print(error));
	return FALSE;
}
예제 #2
0
/*! @brief Check if group configuration is up to date. 
 * 
 * @param group The group
 * @returns TRUE if the group configuration is up to date, FALSE otherwise. 
 */
osync_bool osync_group_is_uptodate(OSyncGroup *group)
{
  xmlDocPtr doc = NULL;
  xmlNodePtr cur = NULL;
  OSyncError *error = NULL;
  unsigned int version_major;
  unsigned int version_minor;
  xmlChar *version_str = NULL;
  osync_bool uptodate = FALSE;
  char *config = NULL; 
  const char *configdir = NULL;

  osync_assert(group);
  osync_trace(TRACE_ENTRY, "%s(%p)", __func__, group);
        
  configdir = osync_group_get_configdir(group);
  if (!configdir){
    osync_trace(TRACE_EXIT, "%s(%p) - No configdir set", __func__, group);
    return FALSE;
  }

  config = g_strdup_printf("%s%c%s",
                           configdir,
                           G_DIR_SEPARATOR, "syncgroup.conf");
	
  /* If syncgroup isn't present, we assume that update is required. */
  if (!osync_xml_open_file(&doc, &cur, config, "syncgroup", &error)) {
    osync_trace(TRACE_ERROR, "%s: %s", __func__, osync_error_print(&error));
    osync_error_unref(&error);
    goto end;
  }

  version_str = xmlGetProp(cur->parent, (const xmlChar *)"version");

  /* No version node, means very outdated version. */
  if (!version_str)
    goto end;

  sscanf((const char *) version_str, "%u.%u", &version_major, &version_minor);

  osync_trace(TRACE_INTERNAL, "Version: %s (current %u.%u required %u.%u)",
              version_str, version_major, version_minor, 
              OSYNC_GROUP_MAJOR_VERSION, OSYNC_GROUP_MINOR_VERSION ); 

  if (OSYNC_GROUP_MAJOR_VERSION == version_major 
      && OSYNC_GROUP_MINOR_VERSION == version_minor)
    uptodate = TRUE;

  osync_xml_free(version_str);
 end:
  g_free(config);

  if (doc)
    osync_xml_free_doc(doc);

  osync_trace(TRACE_EXIT, "%s(%p)", __func__, group);
  return uptodate;
}
osync_bool osync_capabilities_assemble(OSyncCapabilities *capabilities, char **buffer, unsigned int *size, OSyncError **error)
{
	xmlDocPtr doc = NULL;
	xmlNodePtr root;
	char *version_str;
	const char *capsformat;
	OSyncList *l;
	osync_assert(capabilities);

	capsformat = osync_capabilities_get_format(capabilities);

	if (!capsformat) {
		osync_error_set(error, OSYNC_ERROR_GENERIC, "Can't assamble capabilities: Capabilities Format not set.");
		goto error;
	}

	if (capabilities->doc)
		osync_xml_free_doc(capabilities->doc);

	capabilities->doc = doc = xmlNewDoc(BAD_CAST "1.0");
	capabilities->doc->children = xmlNewDocNode(capabilities->doc, NULL, (xmlChar *)"Caps", NULL);
	capabilities->doc->_private = capabilities;

        /* Set version for capabilities configuration */
        version_str = osync_strdup_printf("%u.%u", OSYNC_CAPS_MAJOR_VERSION, OSYNC_CAPS_MINOR_VERSION);
        xmlSetProp(doc->children, (const xmlChar*)"Version", (const xmlChar *)version_str);
        osync_free(version_str);

	/* Set CapsFormat Name */
        xmlSetProp(doc->children, (const xmlChar*)"CapsFormat", (const xmlChar *)capsformat);

	root = doc->children;

	for (l = capabilities->objtypes; l; l = l->next) {
		OSyncCapabilitiesObjType *capobjtype;
		capobjtype = (OSyncCapabilitiesObjType *) l->data;
		if (!osync_capabilities_objtype_assemble(capobjtype, root, error))
			goto error;
	}

	/* XXX Ugly cast, but we try to fit here the opensync API pattern of unsigned size/length types */
	xmlDocDumpFormatMemoryEnc(doc, (xmlChar **) buffer, (int *) size, NULL, 1);
	
	return TRUE;

/*
error_oom:
	osync_error_set(error, OSYNC_ERROR_GENERIC, "Couldn't allocate memory to assemble capabilities file.");
*/	
error:
	osync_trace(TRACE_EXIT_ERROR, "%s: %s" , __func__, osync_error_print(error));
	return FALSE;
}
void osync_capabilities_unref(OSyncCapabilities *caps)
{
	osync_assert(caps);
			
	if (g_atomic_int_dec_and_test(&(caps->ref_count))) {
		while (caps->objtypes) {
			osync_capabilities_objtype_unref(caps->objtypes->data);
			caps->objtypes = osync_list_remove(caps->objtypes, caps->objtypes->data);
		}

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

		osync_free(caps);
	}
}
void osync_xmlformat_unref(OSyncXMLFormat *xmlformat)
{
  osync_assert(xmlformat);
	
  if (g_atomic_int_dec_and_test(&(xmlformat->ref_count))) {
    OSyncXMLField *cur, *tmp;
    cur = xmlformat->first_child;
    while(cur != NULL)
      {
        tmp = osync_xmlfield_get_next(cur);
        osync_xmlfield_delete(cur);
        cur = tmp;
      }
    osync_xml_free_doc(xmlformat->doc);
    g_free(xmlformat);
  }
}
예제 #6
0
/*! @brief Loads a group from a directory
 * 
 * Loads a group from a directory
 * 
 * @param group The group object to load into
 * @param path The path to the config directory of the group
 * @param error Pointer to an error struct
 * @returns TRUE on success, FALSE otherwise
 * 
 */
osync_bool osync_group_load(OSyncGroup *group, const char *path, OSyncError **error)
{
  char *filename = NULL;
  char *real_path = NULL;
  xmlDocPtr doc;
  xmlNodePtr cur;
  //xmlNodePtr filternode;
	
  osync_assert(group);
  osync_assert(path);
  osync_trace(TRACE_ENTRY, "%s(%p, %s, %p)", __func__, group, path, error);
	
  if (!g_path_is_absolute(path)) {
    char *curdir = g_get_current_dir();
    real_path = g_strdup_printf("%s%c%s", curdir, G_DIR_SEPARATOR, path);
    g_free(curdir);
  } else {
    real_path = g_strdup(path);
  }
	
  osync_group_set_configdir(group, real_path);
  filename = g_strdup_printf("%s%csyncgroup.conf", real_path, G_DIR_SEPARATOR);
  g_free(real_path);
	
  if (!osync_xml_open_file(&doc, &cur, filename, "syncgroup", error)) {
    g_free(filename);
    goto error;
  }
  g_free(filename);
	
  while (cur != NULL) {
    char *str = (char*)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
    if (str) {
      if (!xmlStrcmp(cur->name, (const xmlChar *)"groupname"))
        osync_group_set_name(group, str);
	
      if (!xmlStrcmp(cur->name, (const xmlChar *)"last_sync"))
        group->last_sync = (time_t)atoi(str);
			
      //TODO: remove the next 2 lines later
      if (!xmlStrcmp(cur->name, (const xmlChar *)"enable_merger"))
        group->merger_enabled = (!g_ascii_strcasecmp("true", str)) ? TRUE : FALSE;
      //TODO: remove the next 2 lines later
      if (!xmlStrcmp(cur->name, (const xmlChar *)"enable_converter"))
        group->converter_enabled = (!g_ascii_strcasecmp("true", str)) ? TRUE : FALSE;

      if (!xmlStrcmp(cur->name, (const xmlChar *)"merger_enabled"))
        group->merger_enabled = (!g_ascii_strcasecmp("true", str)) ? TRUE : FALSE;

      if (!xmlStrcmp(cur->name, (const xmlChar *)"converter_enabled"))
        group->converter_enabled = (!g_ascii_strcasecmp("true", str)) ? TRUE : FALSE;

      // TODO: reimplement the filter!
      /*if (!xmlStrcmp(cur->name, (const xmlChar *)"filter")) {
        filternode = cur->xmlChildrenNode;
        OSyncFilter *filter = osync_filter_new();
        filter->group = group;
				
        while (filternode != NULL) {
        if (!xmlStrcmp(filternode->name, (const xmlChar *)"sourceobjtype"))
        filter->sourceobjtype = (char*)xmlNodeListGetString(doc, filternode->xmlChildrenNode, 1);
					
        if (!xmlStrcmp(filternode->name, (const xmlChar *)"destobjtype"))
        filter->destobjtype = (char*)xmlNodeListGetString(doc, filternode->xmlChildrenNode, 1);
					
        if (!xmlStrcmp(filternode->name, (const xmlChar *)"detectobjtype"))
        filter->detectobjtype = (char*)xmlNodeListGetString(doc, filternode->xmlChildrenNode, 1);
					
        if (!xmlStrcmp(filternode->name, (const xmlChar *)"config"))
        filter->config = (char*)xmlNodeListGetString(doc, filternode->xmlChildrenNode, 1);
					
        if (!xmlStrcmp(filternode->name, (const xmlChar *)"function_name")) {
        char *str = (char*)xmlNodeListGetString(doc, filternode->xmlChildrenNode, 1);
        if (!str) {
        filternode = filternode->next;
        continue;
        }
        osync_filter_update_hook(filter, group, str);
        osync_xml_free(str);
        }
					
        if (!xmlStrcmp(filternode->name, (const xmlChar *)"sourcemember")) {
        char *str = (char*)xmlNodeListGetString(doc, filternode->xmlChildrenNode, 1);
        if (!str) {
        filternode = filternode->next;
        continue;
        }
        filter->sourcememberid = atoll(str);
        osync_xml_free(str);
        }
					
        if (!xmlStrcmp(filternode->name, (const xmlChar *)"destmember")) {
        char *str = (char*)xmlNodeListGetString(doc, filternode->xmlChildrenNode, 1);
        if (!str) {
        filternode = filternode->next;
        continue;
        }
        filter->destmemberid = atoll(str);
        osync_xml_free(str);
        }
					
        if (!xmlStrcmp(filternode->name, (const xmlChar *)"action")) {
        char *str = (char*)xmlNodeListGetString(doc, filternode->xmlChildrenNode, 1);
        if (!str) {
        filternode = filternode->next;
        continue;
        }
        filter->action = atoi(str);
        osync_xml_free(str);
        }
        filternode = filternode->next;
        }
        osync_filter_register(group, filter);
        }*/
		
      osync_xml_free(str);
    }
    cur = cur->next;
  }
  osync_xml_free_doc(doc);
	
  /* Check for sanity */
  if (!group->name) {
    osync_error_set(error, OSYNC_ERROR_MISCONFIGURATION, "Loaded a group without a name");
    goto error;
  }
	
  if (!_osync_group_load_members(group, group->configdir, error))
    goto error;
	
  osync_trace(TRACE_EXIT, "%s: %p", __func__, group);
  return TRUE;

 error:
  osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
  return FALSE;
}
예제 #7
0
/*! @brief Saves the group to disc
 * 
 * Saves the group to disc possibly creating the configdirectory
 * 
 * @param group The group
 * @param error Pointer to an error struct
 * @returns TRUE on success, FALSE otherwise
 * 
 */
osync_bool osync_group_save(OSyncGroup *group, OSyncError **error)
{
  char *filename = NULL;
  int i;
  xmlDocPtr doc;
  char *tmstr = NULL;
  char *version_str = NULL;
	
  osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, group, error);
  osync_assert(group);
  osync_assert(group->configdir);
	
  osync_trace(TRACE_INTERNAL, "Trying to open configdirectory %s to save group %s", group->configdir, group->name);
	
  if (!g_file_test(group->configdir, G_FILE_TEST_IS_DIR)) {
    osync_trace(TRACE_INTERNAL, "Creating group configdirectory %s", group->configdir);
    if (g_mkdir(group->configdir, 0700)) {
      osync_error_set(error, OSYNC_ERROR_IO_ERROR, "Unable to create directory for group %s\n", group->name);
      goto error;
    }
  }
	
  filename = g_strdup_printf ("%s%csyncgroup.conf", group->configdir, G_DIR_SEPARATOR);
  osync_trace(TRACE_INTERNAL, "Saving group to file %s", filename);
	
  doc = xmlNewDoc((xmlChar*)"1.0");
  doc->children = xmlNewDocNode(doc, NULL, (xmlChar*)"syncgroup", NULL);

  version_str = g_strdup_printf("%u.%u", OSYNC_GROUP_MAJOR_VERSION, OSYNC_GROUP_MINOR_VERSION);
  xmlSetProp(doc->children, (const xmlChar*)"version", (const xmlChar *)version_str);	
  g_free(version_str);
	
  // TODO: reimplement the filter!
  //The filters
  /*GList *f;
    for (f = group->filters; f; f = f->next) {
    OSyncFilter *filter = f->data;
    xmlNodePtr child = xmlNewChild(doc->children, NULL, (xmlChar*)"filter", NULL);
		
    if (filter->sourcememberid) {
    char *sourcememberid = g_strdup_printf("%lli", filter->sourcememberid);
    xmlNewChild(child, NULL, (xmlChar*)"sourcemember", (xmlChar*)sourcememberid);
    g_free(sourcememberid);
    }
    if (filter->destmemberid) {
    char *destmemberid = g_strdup_printf("%lli", filter->destmemberid);
    xmlNewChild(child, NULL, (xmlChar*)"destmember", (xmlChar*)destmemberid);
    g_free(destmemberid);
    }
    if (filter->sourceobjtype)
    xmlNewChild(child, NULL, (xmlChar*)"sourceobjtype", (xmlChar*)filter->sourceobjtype);
    if (filter->destobjtype)
    xmlNewChild(child, NULL, (xmlChar*)"destobjtype", (xmlChar*)filter->destobjtype);
    if (filter->detectobjtype)
    xmlNewChild(child, NULL, (xmlChar*)"detectobjtype", (xmlChar*)filter->detectobjtype);
    if (filter->action) {
    char *action = g_strdup_printf("%i", filter->action);
    xmlNewChild(child, NULL, (xmlChar*)"action", (xmlChar*)action);
    g_free(action);
    }
    if (filter->function_name)
    xmlNewChild(child, NULL, (xmlChar*)"function_name", (xmlChar*)filter->function_name);
    if (filter->config)
    xmlNewChild(child, NULL, (xmlChar*)"config", (xmlChar*)filter->config);
    }*/

  xmlNewChild(doc->children, NULL, (xmlChar*)"groupname", (xmlChar*)group->name);

  tmstr = g_strdup_printf("%i", (int)group->last_sync);
  xmlNewChild(doc->children, NULL, (xmlChar*)"last_sync", (xmlChar*)tmstr);
  g_free(tmstr);

  xmlNewChild(doc->children, NULL, (xmlChar*)"merger_enabled", (xmlChar*) (group->merger_enabled ? "true" : "false"));
  xmlNewChild(doc->children, NULL, (xmlChar*)"converter_enabled", (xmlChar*) (group->converter_enabled ? "true" : "false"));


  xmlSaveFormatFile(filename, doc, 1);
  osync_xml_free_doc(doc);
  g_free(filename);

  for (i = 0; i < osync_group_num_members(group); i++) {
    OSyncMember *member = osync_group_nth_member(group, i);
    if (!osync_member_save(member, error))
      goto error;
  }
	
  osync_trace(TRACE_EXIT, "%s", __func__);
  return TRUE;

 error:
  osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error));
  return FALSE;
}
예제 #8
0
OSyncConvCmpResult osync_xml_compare(xmlDoc *leftinpdoc, xmlDoc *rightinpdoc, OSyncXMLScore *scores, int default_score, int treshold)
{
	int z = 0, i = 0, n = 0;
	int res_score = 0;
	xmlDoc *leftdoc = NULL;
	xmlDoc *rightdoc = NULL;

	osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, leftinpdoc, rightinpdoc, scores);
	
	leftdoc = xmlCopyDoc(leftinpdoc, TRUE);
	rightdoc = xmlCopyDoc(rightinpdoc, TRUE);
	
	osync_trace(TRACE_INTERNAL, "Comparing given score list");
	while (scores && scores[z].path) {
		OSyncXMLScore *score = &scores[z];
		xmlXPathObject *leftxobj = osync_xml_get_nodeset(leftdoc, score->path);
		xmlXPathObject *rightxobj = osync_xml_get_nodeset(rightdoc, score->path);
		
		xmlNodeSet *lnodes = leftxobj->nodesetval;
		xmlNodeSet *rnodes = rightxobj->nodesetval;
		
		int lsize = (lnodes) ? lnodes->nodeNr : 0;
		int rsize = (rnodes) ? rnodes->nodeNr : 0;
		z++;
		osync_trace(TRACE_INTERNAL, "parsing next path %s", score->path);
		
		if (!score->value) {
			for (i = 0; i < lsize; i++) {
				xmlUnlinkNode(lnodes->nodeTab[i]);
				xmlFreeNode(lnodes->nodeTab[i]);
				lnodes->nodeTab[i] = NULL;
			}
			
			for (n = 0; n < rsize; n++) {
				xmlUnlinkNode(rnodes->nodeTab[n]);
				xmlFreeNode(rnodes->nodeTab[n]);
				rnodes->nodeTab[n] = NULL;
			}
		} else {
			for (i = 0; i < lsize; i++) {
				if (!lnodes->nodeTab[i])
					continue;
				for (n = 0; n < rsize; n++) {
					xmlChar *lcontent = NULL;
					xmlChar *rcontent = NULL;
					if (!rnodes->nodeTab[n])
						continue;
					lcontent = osync_xml_find_node(lnodes->nodeTab[i], "Content");
					rcontent = osync_xml_find_node(rnodes->nodeTab[n], "Content"); 
					osync_trace(TRACE_INTERNAL, "cmp %i:%s (%s), %i:%s (%s)", i, lnodes->nodeTab[i]->name, lcontent, n, rnodes->nodeTab[n]->name, rcontent);
					xmlFree(lcontent);
					xmlFree(rcontent);

					if (osync_xml_compare_node(lnodes->nodeTab[i], rnodes->nodeTab[n])) {
						osync_trace(TRACE_INTERNAL, "Adding %i for %s", score->value, score->path);
						res_score += score->value;
						xmlUnlinkNode(lnodes->nodeTab[i]);
						xmlFreeNode(lnodes->nodeTab[i]);
						lnodes->nodeTab[i] = NULL;
						xmlUnlinkNode(rnodes->nodeTab[n]);
						xmlFreeNode(rnodes->nodeTab[n]);
						rnodes->nodeTab[n] = NULL;
						goto next;
					}
				}
				osync_trace(TRACE_INTERNAL, "Subtracting %i for %s", score->value, score->path);
				res_score -= score->value;
			next:;
			}
			for(i = 0; i < rsize; i++) {
				if (!rnodes->nodeTab[i])
					continue;
				res_score -= score->value;
			}
		}
		
		xmlXPathFreeObject(leftxobj);
		xmlXPathFreeObject(rightxobj);
	}

	{ /* Block for new variables */
		xmlXPathObject *leftxobj = osync_xml_get_nodeset(leftdoc, "/*/*");
		xmlXPathObject *rightxobj = osync_xml_get_nodeset(rightdoc, "/*/*");
	
		xmlNodeSet *lnodes = leftxobj->nodesetval;
		xmlNodeSet *rnodes = rightxobj->nodesetval;
	
		int lsize = (lnodes) ? lnodes->nodeNr : 0;
		int rsize = (rnodes) ? rnodes->nodeNr : 0;
		osync_bool same = TRUE;
		osync_trace(TRACE_INTERNAL, "Comparing remaining list");

		for(i = 0; i < lsize; i++) {		
			for (n = 0; n < rsize; n++) {
				xmlChar *lcontent = NULL;
				xmlChar *rcontent = NULL;

				if (!rnodes->nodeTab[n])
					continue;

				lcontent = osync_xml_find_node(lnodes->nodeTab[i], "Content");
				rcontent = osync_xml_find_node(rnodes->nodeTab[n], "Content"); 

				osync_trace(TRACE_INTERNAL, "cmp %i:%s (%s), %i:%s (%s)", i, lnodes->nodeTab[i]->name, lcontent, n, rnodes->nodeTab[n]->name, rcontent);

				xmlFree(lcontent);
				xmlFree(rcontent);

				if (osync_xml_compare_node(lnodes->nodeTab[i], rnodes->nodeTab[n])) {
					xmlUnlinkNode(lnodes->nodeTab[i]);
					xmlFreeNode(lnodes->nodeTab[i]);
					lnodes->nodeTab[i] = NULL;
					xmlUnlinkNode(rnodes->nodeTab[n]);
					xmlFreeNode(rnodes->nodeTab[n]);
					rnodes->nodeTab[n] = NULL;
					osync_trace(TRACE_INTERNAL, "Adding %i", default_score);
					res_score += default_score;
					goto next2;
				}
			}
			osync_trace(TRACE_INTERNAL, "Subtracting %i", default_score);
			res_score -= default_score;
			same = FALSE;
			//goto out;
		next2:;
		}
	
		for(i = 0; i < lsize; i++) {
			if (!lnodes->nodeTab[i])
				continue;
			osync_trace(TRACE_INTERNAL, "left remaining: %s", lnodes->nodeTab[i]->name);
			same = FALSE;
			goto out;
		}
	
		for(i = 0; i < rsize; i++) {
			if (!rnodes->nodeTab[i])
				continue;
			osync_trace(TRACE_INTERNAL, "right remaining: %s", rnodes->nodeTab[i]->name);
			same = FALSE;
			goto out;
		}
	out:
		xmlXPathFreeObject(leftxobj);
		xmlXPathFreeObject(rightxobj);

		osync_xml_free_doc(leftdoc);
		osync_xml_free_doc(rightdoc);	
				

		osync_trace(TRACE_INTERNAL, "Result is: %i, Treshold is: %i", res_score, treshold);
		if (same) {
			osync_trace(TRACE_EXIT, "%s: SAME", __func__);
			return OSYNC_CONV_DATA_SAME;
		}
	} /* Block for new variables */
	if (res_score >= treshold) {
		osync_trace(TRACE_EXIT, "%s: SIMILAR", __func__);
		return OSYNC_CONV_DATA_SIMILAR;
	}
	osync_trace(TRACE_EXIT, "%s: MISMATCH", __func__);
	return OSYNC_CONV_DATA_MISMATCH;
}
예제 #9
0
OSyncList *osync_version_load_from_descriptions(OSyncError **error, const char *descriptiondir, const char *schemadir)
{
	GDir *dir = NULL;
	GError *gerror = NULL;
	const char *descpath = descriptiondir ? descriptiondir : OPENSYNC_DESCRIPTIONSDIR; 
	const char *schemapath = schemadir ? schemadir : OPENSYNC_SCHEMASDIR; 
	char *filename = NULL;
	const gchar *de = NULL;
	OSyncList *versions = NULL;
	OSyncVersion *version = NULL;
	xmlDocPtr doc;
	xmlNodePtr root;
	xmlNodePtr cur;
	xmlNodePtr child;
	
	osync_trace(TRACE_ENTRY, "%s(%p)", __func__, error);
	
	dir = g_dir_open(descpath, 0, &gerror);
	if (!dir) {
		/* If description directory doesn't exist (e.g. unittests), just ignore this. */
		osync_trace(TRACE_EXIT, "Unable to open directory %s: %s", descpath, gerror->message);
		g_error_free(gerror);
		return NULL;
	}
	
	while ((de = g_dir_read_name(dir))) {
		char *schemafilepath = NULL;
		osync_bool res;

		filename = osync_strdup_printf ("%s%c%s", descpath, G_DIR_SEPARATOR, de);
		
		if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR) || !g_pattern_match_simple("*.xml", filename)) {
			osync_free(filename);
			continue;
		}
		
		doc = xmlReadFile(filename, NULL, XML_PARSE_NOBLANKS);
		if(!doc) {
			osync_free(filename);
			continue;
		}
		
		osync_free(filename);
		
		root = xmlDocGetRootElement(doc);
		if(!root || !xmlStrEqual(root->name, BAD_CAST "versions")) {
			osync_xml_free_doc(doc);
			continue;
		}

		schemafilepath = osync_strdup_printf("%s%c%s", schemapath, G_DIR_SEPARATOR, "descriptions.xsd");
		res = osync_xml_validate_document(doc, schemafilepath);
		osync_free(schemafilepath);

		if(res == FALSE) {
			osync_xml_free_doc(doc);
			continue;
		}
		
		cur = root->children;
		for(; cur != NULL; cur = cur->next) {
		
			version = osync_version_new(error);
			if(!version) {
				OSyncList *cur = NULL;
				osync_xml_free_doc(doc);
				cur = osync_list_first(versions);
				while(cur) {
					osync_version_unref(cur->data);
					cur = cur->next;	
				}
				goto error;
			}
				
			child = cur->children;
			osync_version_set_plugin(version, (const char *)osync_xml_node_get_content(child));
			child = child->next;
			osync_version_set_priority(version, (const char *)osync_xml_node_get_content(child));
			child = child->next;
			osync_version_set_vendor(version, (const char *)osync_xml_node_get_content(child));
			child = child->next;
			osync_version_set_modelversion(version, (const char *)osync_xml_node_get_content(child));
			child = child->next;
			osync_version_set_firmwareversion(version, (const char *)osync_xml_node_get_content(child));
			child = child->next;
			osync_version_set_softwareversion(version, (const char *)osync_xml_node_get_content(child));
			child = child->next;
			osync_version_set_hardwareversion(version, (const char *)osync_xml_node_get_content(child));
			child = child->next;
			osync_version_set_identifier(version, (const char *)osync_xml_node_get_content(child));
			
			versions = osync_list_append(versions, version);
		}
		
		osync_xml_free_doc(doc);
	}
	
	g_dir_close(dir);
	
	osync_trace(TRACE_EXIT, "%s: %p", __func__, versions);
	return versions;

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