/*
 * libsyncml - A syncml protocol implementation
 * Copyright (C) 2005  Armin Bauer <armin.bauer@opensync.org>
 * Copyright (C) 2007-2009  Michael Bell <michael.bell@opensync.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
 */

#include <libsyncml/sml_support.h>
#include <libsyncml/sml_elements_internals.h>
#include <libsyncml/sml_command_internals.h>
#include <libsyncml/sml_session_internals.h>
#include "libsyncml/sml_error_internals.h"
#include "libsyncml/data_sync_api/sml_location.h"
#include "libsyncml/data_sync_api/sml_data_sync_change_item_internals.h"

#include "sml_xml_parse_internals.h"
#include <string.h>

#define BUFFER_SIZE 500

static gboolean _smlXmlParserStep(SmlXmlParser *parser)
{
	gboolean ret = FALSE;
	do {
		ret = (xmlTextReaderRead(parser->reader) == 1) ? TRUE : FALSE;
	} while (ret && (xmlTextReaderNodeType(parser->reader) == XML_READER_TYPE_DOCUMENT_TYPE || \
		xmlTextReaderNodeType(parser->reader) == XML_READER_TYPE_WHITESPACE || \
		xmlTextReaderNodeType(parser->reader) == XML_READER_TYPE_SIGNIFICANT_WHITESPACE));
		
	return ret;
}

static gboolean _smlXmlParserStepData(SmlXmlParser *parser)
{
	gboolean ret = FALSE;
	do {
		ret = (xmlTextReaderRead(parser->reader) == 1) ? TRUE : FALSE;
	} while (ret && (xmlTextReaderNodeType(parser->reader) == XML_READER_TYPE_DOCUMENT_TYPE || \
		xmlTextReaderNodeType(parser->reader) == XML_READER_TYPE_WHITESPACE));
		
	return ret;
}

static gboolean _smlXmlSkipNode(SmlXmlParser *parser)
{
	int node_type;

	if (xmlTextReaderNext(parser->reader) != 1)
		return FALSE;

	node_type = xmlTextReaderNodeType(parser->reader);
	while (xmlTextReaderNodeType(parser->reader) == XML_READER_TYPE_DOCUMENT_TYPE ||
	       xmlTextReaderNodeType(parser->reader) == XML_READER_TYPE_WHITESPACE ||
	       xmlTextReaderNodeType(parser->reader) == XML_READER_TYPE_SIGNIFICANT_WHITESPACE)
	{
		if (!_smlXmlParserStep (parser))
			return FALSE;
	}

	return TRUE;
}

static gboolean
_smlXmlParserExpectNode (SmlXmlParser *parser,
                         int type,
                         gboolean empty,
                         const gchar *name,
                         GError **error)
{
	CHECK_ERROR_REF
	if (!_smlXmlParserStep(parser)) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No node at all");
		return FALSE;
	}
	
	if (xmlTextReaderNodeType(parser->reader) != type) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "wrong node type");
		return FALSE;
	}
	
		
	switch (type) {
		case XML_NODE_START:
		case XML_NODE_CLOSE:
			if (name) {
				if (!xmlTextReaderConstName(parser->reader)) {
					g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "no name");
					return FALSE;
				}
			
				if (strcmp((char *)xmlTextReaderConstName(parser->reader), name)) {
					g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Wrong name");
					return FALSE;
				}
			}
			break;
		case XML_NODE_TEXT:
			if (!empty && !xmlTextReaderConstName(parser->reader)) {
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Empty.");
				return FALSE;
			}
			break;
		default:
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unknown node type");
			return FALSE;
	}
	
	return TRUE;
}

static gboolean
_smlXmlParserGetID (SmlXmlParser *parser,
                    guint64 *id,
                    const gchar *name,
                    GError **error)
{
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(id);
	
	if (*id) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Id already set");
		goto error;
	}
	
	if (!_smlXmlParserExpectNode(parser, XML_NODE_TEXT, FALSE, NULL, error))
		goto error;
	
	*id = atoi(g_strstrip((char *)xmlTextReaderConstValue(parser->reader)));
	
	if (!_smlXmlParserExpectNode(parser, XML_NODE_CLOSE, FALSE, name, error))
		goto error;
	
	return TRUE;
error:
	return FALSE;
}

static gboolean
_smlXmlParserGetString (SmlXmlParser *parser,
                        gchar **string,
                        const gchar *name,
                        GError **error)
{
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(string);
	
	if (*string) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "string already set");
		goto error;
	}
	
	/* required for empty elements like <FwV/> */
	if (xmlTextReaderIsEmptyElement(parser->reader)) {
		*string = g_strdup("");
		return TRUE;
	}
	
	if (!_smlXmlParserStep(parser)) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No node at all");
		goto error_clear;
	}
	
	if (xmlTextReaderNodeType(parser->reader) == XML_NODE_TEXT) {
		*string = g_strstrip(g_strdup((char *)xmlTextReaderConstValue(parser->reader)));
		
		if (!_smlXmlParserExpectNode(parser, XML_NODE_CLOSE, FALSE, name, error))
			goto error_clear;
	} else if (xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
		*string = g_strdup("");
	} else {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "wrong node type");
		goto error_clear;
	}
	
	return TRUE;

error_clear:
	*string = NULL;
error:
	return FALSE;
}

static gboolean
_smlXmlParserGetData (SmlXmlParser *parser,
                      gchar **string,
                      gsize *size,
                      const gchar *name,
                      GError **error)
{
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(string);
	int rc = 0;
	
	if (*string) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "string already set");
		return FALSE;
	}
	
	
	xmlBuffer *buffer = xmlBufferCreateSize(BUFFER_SIZE);
	if (!buffer) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unable to create new buffer");
		goto error;
	}
	
	xmlTextWriter *writer = xmlNewTextWriterMemory(buffer, 0);
	if (!writer) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unable to create new writer");
		goto error_free_buffer;
	}

	/* SyncML 1.2 allows empty data elements <Data /> */
	if (xmlTextReaderIsEmptyElement(parser->reader))
		goto out;
		
	while (1) {
		if (!_smlXmlParserStepData(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
			            "There is a missing node during the data parsing of %s.", name);
			goto error_free_writer;
		}
		
		switch (xmlTextReaderNodeType(parser->reader)) {
			case XML_NODE_CLOSE:
				if (!strcmp((char *)xmlTextReaderConstName(parser->reader), name))
					goto out;
					
				if (!xmlTextReaderIsEmptyElement(parser->reader))
					rc = xmlTextWriterEndElement(writer);

				if (rc < 0) {
					g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
					            "Unable to end node");
					goto error_free_writer;
				}
				break;
			case XML_NODE_START:
				if (!xmlTextReaderIsEmptyElement(parser->reader))
					rc = xmlTextWriterStartElementNS(writer, NULL, xmlTextReaderConstName(parser->reader), NULL);
				else
					rc = xmlTextWriterWriteElementNS(writer, NULL, xmlTextReaderConstName(parser->reader), NULL, (const xmlChar*)"");

				if (rc < 0) {
					g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unable to start node");
					goto error_free_writer;
				}
				break;
			case XML_NODE_CDATA:
			case XML_NODE_TEXT:
				rc = xmlTextWriterWriteRaw(writer, xmlTextReaderConstValue(parser->reader));
				if (rc < 0) {
					g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unable to add string");
					goto error_free_writer;
				}
				break;
			case XML_READER_TYPE_SIGNIFICANT_WHITESPACE:
				rc = xmlTextWriterWriteRaw(writer, xmlTextReaderConstValue(parser->reader));
				if (rc < 0) {
					g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unable to add string");
					goto error_free_writer;
				}
				break;
			default:
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unknown node type");
				goto error_free_writer;
		}
	}

out:
	// do not call xmlTextWriterEndDocument here
	// the writer was not started with xmlTextWriterStartDocument
	// the function adds always a newline
	// if you load only a CDATA field then a newline is additional data
	// additional data invalidates size information of the remote peer
	// additional data crashs by this way all length checks if smlItemCheck !!!
	
	xmlFreeTextWriter(writer);
	*string = g_strndup((const char *) xmlBufferContent(buffer), xmlBufferLength(buffer));
	if (size)
		*size = xmlBufferLength(buffer);
	
	xmlBufferFree(buffer);
	
	return TRUE;

error_free_writer:
	xmlFreeTextWriter(writer);
error_free_buffer:
	xmlBufferFree(buffer);
error:
	*string = NULL;
	if (size) *size = 0;
	return FALSE;
}

static gboolean
_smlSyncHeaderParseDTD (SmlProtocolVersion *version,
                        SmlXmlParser *parser,
                        GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, version, parser, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(version);
	
	if (*version) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "dtd already set");
		smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
		return FALSE;
	}
	
	char *dtd = NULL;
	if (!_smlXmlParserGetString(parser, &dtd, SML_ELEMENT_VERDTD, error))
		goto error;
	
	if (!strcmp(dtd, "1.0"))
		*version = SML_VERSION_10;
	else if (!strcmp(dtd, "1.1"))
		*version = SML_VERSION_11;
	else if (!strcmp(dtd, "1.2"))
		*version = SML_VERSION_12;
	else {
		smlSafeCFree(&dtd);
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unknown VerDTD");
		goto error;
	}
	smlSafeCFree(&dtd);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
error:
	*version = SML_VERSION_UNKNOWN;
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}

static gboolean
_smlSyncHeaderParseProto (SmlProtocolType *type,
                          SmlXmlParser *parser,
                          GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, type, parser, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(type);
	
	if (*type) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "protocol already set");
		smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
		return FALSE;
	}
	
	char *proto = NULL;
	if (!_smlXmlParserGetString(parser, &proto, SML_ELEMENT_VERPROTO, error))
		goto error;
	
	if (!strcmp(proto, SML_VERSION_STRING_10) || !strcmp(proto, SML_VERSION_STRING_11) || !strcmp(proto, SML_VERSION_STRING_12))
		*type = SML_PROTOCOL_SYNCML;
	else {
		smlSafeCFree(&proto);
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unknown VerProto");
		goto error;
	}
	smlSafeCFree(&proto);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
error:
	*type = SML_PROTOCOL_UNKNOWN;
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}


static gboolean
_smlLocationParse (SmlLocation **location,
                   SmlXmlParser *parser,
                   GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, location, parser, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(location);
	
	if (*location) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Location already set");
		smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
		return FALSE;
	}
	
	*location = sml_location_new();
	if (!location) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Cannot create new SmlLocation object - out of memory.");
		goto error;
	}
	
	while (1) {
		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TARGET) && \
			xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCE) && \
			xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCE_PARENT) && \
			xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TARGET_PARENT) && \
			xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in locations (Target, Source, SourceParent or TargetParent).",
				xmlTextReaderConstName(parser->reader));
			goto error;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_LOCURI)) {
			gchar *uri = NULL;
			if (!_smlXmlParserGetString(parser, &uri, SML_ELEMENT_LOCURI, error))
				goto error;
			sml_location_set_uri(*location, uri);
			g_free(uri);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_LOCNAME)) {
			gchar *name = NULL;
			if (!_smlXmlParserGetString(parser, &name, SML_ELEMENT_LOCNAME, error))
				goto error;
			sml_location_set_name(*location, name);
			g_free(name);
		} else {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "wrong initial node. expected SyncHdr");
			goto error;
		}
	}
	
	if (!sml_location_get_uri(*location)) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No locURI set");
		goto error;
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
error:
	if (*location)
		g_object_unref(*location);
	*location = NULL;
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}

static gboolean
_smlAnchorParse (SmlAnchor **anchor,
                 SmlXmlParser *parser,
                 GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, anchor, parser, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(anchor);
	
	if (*anchor) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "anchor already set");
		smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
		return FALSE;
	}
	
	*anchor = smlTryMalloc0(sizeof(SmlAnchor), error);
	if (!anchor)
		goto error;
	
	while (1) {
		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error_free_anchor;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_ANCHOR) && \
			xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in Anchor.",
				xmlTextReaderConstName(parser->reader));
			goto error_free_anchor;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_NEXT)) {
			if (!_smlXmlParserGetString(parser, &((*anchor)->next), SML_ELEMENT_NEXT, error))
				goto error_free_anchor;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_LAST)) {
			if (!_smlXmlParserGetString(parser, &((*anchor)->last), SML_ELEMENT_LAST, error))
				goto error_free_anchor;
		} else {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "wrong initial node");
			goto error_free_anchor;
		}
	}
	
	if (!(*anchor)->next) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No next set");
		goto error_free_anchor;
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error_free_anchor:
	smlAnchorFree(*anchor);
error:
	*anchor = NULL;
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}

static gboolean
_smlChalMetaParse (SmlXmlParser *parser,
                   gchar **format,
                   gchar **type,
                   gchar **nonce,
                   GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p, %p, %p, %p)", __func__, parser, format, type, nonce, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	
	while (1) {
		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error;
		}

		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_META) && \
		xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in Chal/Meta.",
				xmlTextReaderConstName(parser->reader));
			goto error;
		}
		
		if (type && !strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TYPE)) {
			if (!_smlXmlParserGetString(parser, type, SML_ELEMENT_TYPE, error))
				goto error;
		} else if (format && !strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_FORMAT)) {
			if (!_smlXmlParserGetString(parser, format, SML_ELEMENT_FORMAT, error))
				goto error;
		} else if (format && !strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_NEXTNONCE)) {
			if (!_smlXmlParserGetString(parser, nonce, SML_ELEMENT_NEXTNONCE, error))
				goto error;
		} else {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "wrong initial node: %s", xmlTextReaderConstName(parser->reader));
			goto error;
		}
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error:
	if (format)
		*format = NULL;
	if (nonce)
		*nonce = NULL;
	if (type)
		*type = NULL;

	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}

static gboolean
_smlCommandMetaParse (SmlXmlParser *parser,
                      gchar **format,
                      gchar **type,
                      SmlAnchor **anchor,
                      gsize *size,
                      gsize *maxobjsize,
                      GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p, %p, %p, %p)", __func__, parser, format, type, anchor, size, maxobjsize, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	
	if (maxobjsize)
		*maxobjsize = 0;

	if (xmlTextReaderIsEmptyElement(parser->reader))
	{
		/* empty meta element <meta/> */
		smlTrace(TRACE_EXIT, "%s - empty meta element", __func__);
		return TRUE;
	}
	
	while (1) {
		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error;
		}

		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MEM)) {
			/* Ignored for now */
			smlTrace(TRACE_INTERNAL, "%s: Skipping mem node");
			if (!_smlXmlSkipNode(parser)) {
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unable to skip mem node");
				goto error;
			}
		}

		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_META) && \
		xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in Command/Meta.",
				xmlTextReaderConstName(parser->reader));
			goto error;
		}
		
		if (type && !strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TYPE)) {
			if (!_smlXmlParserGetString(parser, type, SML_ELEMENT_TYPE, error))
				goto error;
		} else if (anchor && !strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_ANCHOR)) {
			if (!_smlAnchorParse(anchor, parser, error))
				goto error;
		} else if (format && !strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_FORMAT)) {
			if (!_smlXmlParserGetString(parser, format, SML_ELEMENT_FORMAT, error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MARK)) {
			/* Ignore the mark for now */
			char *mark = NULL;
			if (!_smlXmlParserGetString(parser, &mark, SML_ELEMENT_MARK, error))
				goto error;
			smlSafeCFree(&mark);
		} else if (size && !strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SIZE)) {
			guint64 id = 0;
			if (!_smlXmlParserGetID(parser, &id, SML_ELEMENT_SIZE, error))
				goto error;
			*size = id;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MAXOBJSIZE)) {
			/* MaxObjSize must be parsed even if there is no maxobjsize pointer */
			guint64 id = 0;
			if (!_smlXmlParserGetID(parser, &id, SML_ELEMENT_MAXOBJSIZE, error))
				goto error;
			if (maxobjsize)
				*maxobjsize = id;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_VERSION)) {
			/* Ignore the version for now */
			char *version = NULL;
			if (!_smlXmlParserGetString(parser, &version, SML_ELEMENT_VERSION, error))
				goto error;
			smlSafeCFree(&version);
		} else {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "wrong initial node for meta information: %s", xmlTextReaderConstName(parser->reader));
			goto error;
		}
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error:
	if (format)
		*format = NULL;
	if (anchor)
		*anchor = NULL;
	if (type)
		*type = NULL;
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}

static SmlItem*
_smlItemParse (SmlXmlParser *parser,
               SmlCommand *cmd,
               SmlCommandType type,
               GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %i, %p)", __func__, parser, cmd, type, error);
	CHECK_ERROR_REF
	smlAssert(parser);

	SmlItem *item = NULL;

	if (parser->gotMoreData) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Last item already had more data set");
		goto error;
	}
	
	item = smlItemNew(0, error);
	if (!item)
		goto error;
	
	/* If the item is an empty element then return immediately. */
	if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_ITEM) &&
	    xmlTextReaderIsEmptyElement(parser->reader)) {
		smlTrace(TRACE_EXIT, "%s - empty item", __func__);
		return item;
	}

	if (!_smlXmlParserStep(parser)) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "The first element inside of an Item structure cannot be parsed.");
		goto error;
	}

	while (1) {
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_ITEM) && \
		xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in Item.",
				xmlTextReaderConstName(parser->reader));
			goto error;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCE)) {
			SmlLocation *source = NULL;
			if (!_smlLocationParse(&source, parser, error))
				goto error;

			if (smlItemGetSource(item)) {
				/* SourceParent already parsed */
				sml_location_set_parent_uri(source, sml_location_get_parent_uri(smlItemGetSource(item)));
			}
			
			smlItemSetSource(item, source);
			g_object_unref(source);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TARGET)) {
			SmlLocation *target = NULL;
			if (!_smlLocationParse(&target, parser, error))
				goto error;

			if (smlItemGetTarget(item)) {
				/* TargetParent already parsed */
				sml_location_set_parent_uri(target, sml_location_get_parent_uri(smlItemGetTarget(item)));
			}
			
			smlItemSetTarget(item, target);
			g_object_unref(target);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCE_PARENT)) {
			SmlLocation *source = NULL;
			if (!_smlLocationParse(&source, parser, error))
				goto error;
			
			if (smlItemGetSource(item)) {
				/* Source already parsed */
				sml_location_set_parent_uri(smlItemGetSource(item), sml_location_get_uri(source));
			} else {
				/* there is no source object until now */
				smlItemSetSource(item, source);
				/* copy uri to parent_uri property */
				sml_location_set_parent_uri(source, sml_location_get_uri(source));
				/* delete uri property */
				sml_location_set_uri(source, NULL);
			}

			g_object_unref(source);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TARGET_PARENT)) {
			SmlLocation *target = NULL;
			if (!_smlLocationParse(&target, parser, error))
				goto error;
			
			if (smlItemGetTarget(item)) {
				/* Target already parsed */
				sml_location_set_parent_uri(smlItemGetTarget(item), sml_location_get_uri(target));
			} else {
				/* there is no target object until now */
				smlItemSetTarget(item, target);
				/* copy uri to parent_uri property */
				sml_location_set_parent_uri(target, sml_location_get_uri(target));
				/* delete uri property */
				sml_location_set_uri(target, NULL);
			}

			g_object_unref(target);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_META)) {
			switch (type) {
				case SML_COMMAND_TYPE_ALERT:
					if (!cmd) {
						/* This is an item inside a status.
						 * We must parse the meta data
						 * but we ignore the content completely.
						 * FIXME: Can we ignore the content?
						 */
						if (!_smlCommandMetaParse(parser, NULL, NULL, NULL, NULL, NULL, error))
							goto error;
					} else {
						/* Some phones send the maxobjsize in the alert */
						if (!_smlCommandMetaParse(parser, NULL, &(cmd->private.alert.contentType), &cmd->private.alert.anchor, NULL, &(cmd->private.alert.maxObjSize), error))
							goto error;
					}
					break;
				case SML_COMMAND_TYPE_ADD:
				case SML_COMMAND_TYPE_DELETE:	
				case SML_COMMAND_TYPE_REPLACE:
					if (!_smlCommandMetaParse(parser, NULL, &(item->contenttype), NULL, &cmd->size, NULL, error))
						goto error;
					break;
				case SML_COMMAND_TYPE_RESULTS:
				case SML_COMMAND_TYPE_PUT:;
					/* Some phones send the type information for the devinf put
					 * not in the put itself but in the item */
					if (!_smlCommandMetaParse(parser, NULL, &(item->contenttype), NULL, NULL, NULL, error))
						goto error;
						
					if (!item->contenttype)
						goto error;
					break;
				default:
					g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unknown command type");
					goto error;
			}
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DATA)) {
			/* Do nothing if the Data element is empty.
			 * The first emptyness test only covers <Data/>.
			 *
			 * Example: Funambol with WBXML on scheduleworld.com
			 */
			if (!xmlTextReaderIsEmptyElement(parser->reader)) {
				switch (type) {
					case SML_COMMAND_TYPE_ALERT:
						if (!_smlXmlParserStep(parser)) {
							g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "The next element after the starting Data element in an Item is missing.");
							goto error;
						}

						if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DATA) &&
						    xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
							/* Do nothing if the Data element is empty.
							 * This only covers <Data></Data>.
							 *
							 * Example: Funambol with XML on scheduleworld.com
							 */
							smlTrace(TRACE_INTERNAL, "%s: detected empty data element", __func__);
							break;
						}

						SmlAnchor *anchor = NULL;
						if (xmlTextReaderNodeType(parser->reader) == XML_NODE_TEXT) {
							/* Google just copies the element Next from the Anchor
							 * Please see ticket #230 for more details.
							 */
							anchor = smlTryMalloc0(sizeof(SmlAnchor), error);
							if (!anchor)
								goto error;
							anchor->next = g_strdup((char *)xmlTextReaderConstValue(parser->reader));
						} else {
							/* normal behaviour with anchor copy */
							if (!_smlAnchorParse(&anchor, parser, error))
								goto error;
						}
						item->anchor = anchor;

						if (!_smlXmlParserStep(parser)) {
							g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "The closing Data element in an Item is missing.");
							goto error;
						}
						break;
					default:;
						char *data = NULL;
						gsize size = 0;
						if (!_smlXmlParserGetData(parser, &data, &size, SML_ELEMENT_DATA, error))
							goto error;
						
						if (!smlItemAddData(item, data, size, error)) {
							smlSafeCFree(&data);
							goto error;
						}
						
						smlSafeCFree(&data);
				}
			}
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MOREDATA)) {
			if (parser->version == SML_VERSION_10) {
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "SyncML 1.0 does not allow MoreData");
				goto error;
			}
			
			item->moreData = TRUE;
			parser->gotMoreData = TRUE;
			if (!_smlXmlParserStep(parser)) {
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes2");
				goto error;
			}
			
			if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MOREDATA) && \
				xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
				if (!_smlXmlParserStep(parser)) {
					g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes3");
					goto error;
				}
			}
			continue;
		} else {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "The element Item does not support the child element %s.", xmlTextReaderConstName(parser->reader));
			goto error;
		}
		
		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "The next element in an Item structure is missing.");
			goto error;
		}
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return item;

error:
	if (item)
		smlItemUnref(item);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return NULL;
}

static SmlCred*
_smlCredParse (SmlXmlParser *parser,
               GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, parser, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	SmlCred *cred = NULL;
	char *format = NULL;
	char *type = NULL;
	char *data = NULL;
	
	while (1) {
		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CRED) && \
		xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in Cred.",
				xmlTextReaderConstName(parser->reader));
			goto error;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_META)) {
			if (!_smlCommandMetaParse(parser, &format, &type, NULL, NULL, NULL, error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DATA)) {
			if (!_smlXmlParserGetString(parser, &data, SML_ELEMENT_DATA, error))
				goto error;
		} else {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "wrong initial node %s", xmlTextReaderConstName(parser->reader));
			goto error;
		}
	}

	cred = smlCredNewFromString(type, format, data, error);

error:	
	if (data)
		smlSafeCFree(&data);

	if (format)
		smlSafeCFree(&format);

	if (type)
		smlSafeCFree(&type);

	if (!(*error))
	{
		smlTrace(TRACE_EXIT, "%s: %p", __func__, cred);
		return cred;
	} else {
		if (cred)
			smlCredUnref(cred);
		smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
		return NULL;
	}
}

static SmlChal*
_smlChalParse (SmlXmlParser *parser,
               GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, parser, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	char *format = NULL;
	char *type = NULL;
	char *nonce = NULL;
	
	while (1) {
		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CHAL) && \
		xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in Chal.",
				xmlTextReaderConstName(parser->reader));
			goto error;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_META)) {
			if (!_smlChalMetaParse(parser, &format, &type, &nonce, error))
				goto error;
		} else {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "wrong initial node %s", xmlTextReaderConstName(parser->reader));
			goto error;
		}
	}
		
	if (format && strcmp(format, SML_BASE64)) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unknown format");
		goto error;
	}

	SmlAuthType auth;
	if (!type || !strcmp(type, SML_AUTH_BASIC)) {
		auth = SML_AUTH_TYPE_BASIC;
	} else if (!strcmp(type, SML_AUTH_MD5)) {
		auth = SML_AUTH_TYPE_MD5;
	} else {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unknown type");
		goto error;
	}

	if (format)
		smlSafeCFree(&format);
	if (type)
		smlSafeCFree(&type);

	SmlChal *chal = NULL;
	if (nonce || auth == SML_AUTH_TYPE_MD5)
		chal = smlChalNewFromBase64(auth, nonce, error);
	else
		chal = smlChalNew(auth, error);
	if (!chal)
		goto error;

	if (nonce)
		smlSafeCFree(&nonce);

	smlTrace(TRACE_EXIT, "%s: %p", __func__, chal);
	return chal;
error:
	if (format)
		smlSafeCFree(&format);
	if (type)
		smlSafeCFree(&type);
	if (nonce)
		smlSafeCFree(&nonce);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return NULL;
}

static gboolean
_smlChangeParse (SmlXmlParser *parser,
                 SmlCommand **cmd,
                 SmlCommandType type,
                 const gchar *name,
                 GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %i, %s, %p)", __func__, parser, type, VA_STRING(name), error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(name);
	char *contenttype = NULL;
	SmlLocation *source = NULL;
	SmlLocation *target = NULL;
	
	*cmd = smlCommandNew(type, error);
	if (!*cmd)
		goto error;	
	(*cmd)->refCount = 1;
	
	switch (type) {
		case SML_COMMAND_TYPE_ADD:
			(*cmd)->private.change.type = SML_CHANGE_ADD;
			break;
		case SML_COMMAND_TYPE_REPLACE:
			(*cmd)->private.change.type = SML_CHANGE_REPLACE;
			break;
		case SML_COMMAND_TYPE_DELETE:
			(*cmd)->private.change.type = SML_CHANGE_DELETE;
			break;
		default:
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unknown change type set");
			goto error;
	}

	while (1) {
		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), name) && \
		xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in %s.",
				xmlTextReaderConstName(parser->reader), name);
			goto error;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMDID)) {
			guint64 id = 0;
			if (!_smlXmlParserGetID(parser, &id, SML_ELEMENT_CMDID, error))
				goto error;
			(*cmd)->cmdID = id;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_ITEM)) {
			SmlItem *item = _smlItemParse(parser, *cmd, type, error);
			if (!item)
				goto error;

			/* convert SmlItem to SmlDataSyncChangeItem */

			SmlDataSyncChangeItem *citem = sml_data_sync_change_item_new();
			if (!citem) {
				smlItemUnref(item);
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Cannot create new instance of SmlDataSyncChangeItem - out of memory.");
				goto error;
			}
			sml_data_sync_change_item_set_action(citem, (*cmd)->private.change.type);

			/* message from OMA DS client:
			 *     - ADD + source URI                   => automatically mapped item
			 *     - REPLACE/DELETE + source/target URI => mapped item
			 * message from OMA DS server:
			 *     - ADD + source/target URI            => not yet mapped item
			 *     - REPLACE/DELETE + target URI        => mapped item
			 * => first check the target URI for a finally mapped item
			 * => second check the source URI for a not yet mapped item
			 */
			if (smlItemGetTarget(item)) {
				if (!sml_data_sync_change_item_set_location(citem, smlItemGetTarget(item), error)) {
					smlItemUnref(item);
					g_object_unref(citem);
					goto error;
				}
			} else {
				if (!sml_data_sync_change_item_set_location(citem, smlItemGetSource(item), error)) {
					smlItemUnref(item);
					g_object_unref(citem);
					goto error;
				}
			}
			if (smlItemGetContent(item) &&
			    !sml_data_sync_change_item_set_data(citem, smlItemGetContent(item), smlItemGetSize(item), error)) {
				smlItemUnref(item);
				g_object_unref(citem);
				goto error;
			}
			if (smlItemGetContentType(item) &&
			    !sml_data_sync_change_item_set_content_type(citem, smlItemGetContentType(item), error)) {
				smlItemUnref(item);
				g_object_unref(citem);
				goto error;
			}
			if (smlItemGetMoreData(item)) {
				sml_data_sync_change_item_set_missing_data(citem, TRUE);
				sml_data_sync_change_item_set_planned_size(citem, (*cmd)->size);
			}
			if (!source && smlItemGetSource(item)) {
				source = smlItemGetSource(item);
				g_object_ref(source);
			}
			if (!target && smlItemGetTarget(item)) {
				target = smlItemGetTarget(item);
				g_object_ref(target);
			}
			smlItemUnref(item);
			item = NULL;
			
			/* add item to list of items */

			(*cmd)->private.change.items = g_list_append((*cmd)->private.change.items, citem);
			if (!(*cmd)->private.change.items)
			{
				g_object_unref(citem);
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "g_list_append for item list of change command failed.");
				goto error;
			}
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_META)) {
			if (!_smlCommandMetaParse(parser, NULL, &contenttype, NULL, &(*cmd)->size, NULL, error))
				goto error;
		} else {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "wrong initial node");
			goto error;
		}
	}
	
	if (!(*cmd)->cmdID) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No cmdid set");
		goto error;
	}
	
	if (!(*cmd)->private.change.items ||
	    !g_list_length((*cmd)->private.change.items)) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No items set");
		goto error;
	}
	
	/* some clients set the contentype in the item, don't overwrite it with NULL */
	guint i;
	for (i=0; i < g_list_length((*cmd)->private.change.items); i++)
	{
		SmlDataSyncChangeItem *item = g_list_nth_data((*cmd)->private.change.items, i);
		if (sml_data_sync_change_item_get_content_type(item) == NULL &&
		    contenttype &&
		    !sml_data_sync_change_item_set_content_type(item, contenttype, error)) {
			goto error;
		}
	}
	if (contenttype)
		smlSafeCFree(&contenttype);

	//for (i=0; i < g_list_length((*cmd)->private.change.items); i++)
	//{
	//	SmlItem *item = g_list_nth_data((*cmd)->private.change.items, i);
	//	if ((*cmd)->size && !item->size)
	//		item->size = (*cmd)->size;
	//}
	
	if (!(*cmd)->source && source)
	{
		(*cmd)->source = sml_location_clone(source);
		g_object_unref(source);
		source = NULL;
	}

	if (!(*cmd)->target && target)
	{
		(*cmd)->target = sml_location_clone(target);
		g_object_unref(target);
		target = NULL;
	}
	
	/* Step once more */
	if (!_smlXmlParserStep(parser)) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
		goto error;
	}
	
	smlTrace(TRACE_EXIT, "%s: %p", __func__, *cmd);
	return TRUE;
error:
	if (*cmd)
		smlCommandUnref(*cmd);
	*cmd = NULL;
	if (contenttype)
		smlSafeCFree(&contenttype);
	if (source)
		g_object_unref(source);
	if (target)
		g_object_unref(target);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}

static gboolean
_smlMessageParseSynchdrMeta (SmlXmlParser *parser,
                             gsize *maxmsgsize,
                             gsize *maxobjsize,
                             gchar **emi,
                             GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, parser, maxmsgsize, maxobjsize, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(maxmsgsize);
	smlAssert(maxobjsize);
	smlAssert(emi);

	while (1) {
		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_META) && \
			xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in SyncHdr/Meta.",
				xmlTextReaderConstName(parser->reader));
			goto error;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MAXMSGSIZE)) {
			guint64 id = 0;
			if (!_smlXmlParserGetID(parser, &id, SML_ELEMENT_MAXMSGSIZE, error))
				goto error;
			*maxmsgsize = id;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MAXOBJSIZE)) {
			guint64 id = 0;
			if (!_smlXmlParserGetID(parser, &id, SML_ELEMENT_MAXOBJSIZE, error))
				goto error;
			*maxobjsize = id;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_EMI)) {
			if (!_smlXmlParserGetString(parser, emi, SML_ELEMENT_EMI, error))
				goto error;
		} else {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "wrong initial node. expected MaxMsgSize, MaxObjSize or EMI. Instead of that: %s", xmlTextReaderConstName(parser->reader));
			goto error;
		}
	}
	
	if (!(*maxmsgsize) && !(*maxobjsize)) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No maxmsgsize set");
		goto error;
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
error:
	*maxmsgsize = 0;
	*maxobjsize = 0;
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}

static gboolean
_smlAlertParse (SmlXmlParser *parser,
                SmlCommand **cmd,
                GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, cmd, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(cmd);

	*cmd = smlCommandNew(SML_COMMAND_TYPE_ALERT, error);
	if (!*cmd)
		goto error;
	(*cmd)->refCount = 1;
	(*cmd)->private.alert.maxObjSize = 0;
		
	while (1) {
		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error_free_cmd;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_ALERT) && \
		xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in Alert.",
				xmlTextReaderConstName(parser->reader));
			goto error_free_cmd;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMDID)) {
			guint64 id = 0;
			if (!_smlXmlParserGetID(parser, &id, SML_ELEMENT_CMDID, error))
				goto error_free_cmd;
			(*cmd)->cmdID = id;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_ITEM)) {
			SmlItem *item = _smlItemParse(parser, (*cmd), SML_COMMAND_TYPE_ALERT, error);
			if (!item)
				goto error_free_cmd;
			
			(*cmd)->target = item->target;
			item->target = NULL;
			(*cmd)->source = item->source;
			item->source = NULL;
			
			smlItemUnref(item);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DATA)) {
			guint64 id = 0;
			if (!_smlXmlParserGetID(parser, &id, SML_ELEMENT_DATA, error))
				goto error_free_cmd;
			(*cmd)->private.alert.type = smlAlertTypeConvert(id, error);
			if ((*cmd)->private.alert.type == SML_ALERT_UNKNOWN &&
			    *error != NULL)
				goto error_free_cmd;
		} else {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "wrong initial node %s", xmlTextReaderConstName(parser->reader));
			goto error_free_cmd;
		}
	}
	
	if (!(*cmd)->private.alert.type) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No alert type set");
		goto error_free_cmd;
	}
	
	/* Step once more */
	if (!_smlXmlParserStep(parser)) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
		goto error_free_cmd;
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error_free_cmd:
	smlCommandUnref(*cmd);
error:
	*cmd = NULL;
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}

static SmlParserResult
_smlCommandSyncParse (SmlXmlParser *parser,
                      SmlCommand **cmd,
                      GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, cmd, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(cmd);
		
	*cmd = smlCommandNew(SML_COMMAND_TYPE_SYNC, error);
	if (!*cmd)
		goto error;
	(*cmd)->private.sync.maxObjSize = 0;
	
	while (1) {
		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error_free_cmd;
		}
		
		if (xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE && !strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SYNC))
			break;
		else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in Sync.",
				xmlTextReaderConstName(parser->reader));
			goto error_free_cmd;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_ADD) || \
		!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_REPLACE) || \
		!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DELETE))
			break;
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMDID)) {
			guint64 id = 0;
			if (!_smlXmlParserGetID(parser, &id, SML_ELEMENT_CMDID, error))
				goto error_free_cmd;
			(*cmd)->cmdID = id;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_ITEM)) {
			SmlItem *item = _smlItemParse(parser, (*cmd), SML_COMMAND_TYPE_SYNC, error);
			if (!item)
				goto error_free_cmd;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_META)) {
			if (!_smlCommandMetaParse(parser, NULL, NULL, NULL, NULL, &((*cmd)->private.sync.maxObjSize), error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TARGET)) {
			if (!_smlLocationParse(&(*cmd)->target, parser, error))
				goto error_free_cmd;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCE)) {
			if (!_smlLocationParse(&(*cmd)->source, parser, error))
				goto error_free_cmd;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_NUMBEROFCHANGES)) {
			guint64 id = 0;
			(*cmd)->private.sync.hasNumChanged = TRUE;
			if (!_smlXmlParserGetID(parser, &id, SML_ELEMENT_NUMBEROFCHANGES, error))
				goto error_free_cmd;
			(*cmd)->private.sync.numChanged = id;
		} else {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "wrong initial node: %s", xmlTextReaderConstName(parser->reader));
			goto error_free_cmd;
		}
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error_free_cmd:
	smlCommandUnref(*cmd);
error:
	*cmd = NULL;
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}

static SmlMapItem*
_smlMapItemParse (SmlXmlParser *parser,
                  GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, parser, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	
	SmlMapItem *item = sml_map_item_new();
	if (!item) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Out of memory.");
		goto error;
	}
	
	while (1) {
		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error_free_item;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MAPITEM) && \
		xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in MapItem.",
				xmlTextReaderConstName(parser->reader));
			goto error_free_item;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCE)) {
			SmlLocation *loc = NULL;
			if (!_smlLocationParse(&loc, parser, error))
				goto error_free_item;
			if (!sml_map_item_set_remote(item, loc, error)) {
				g_object_unref(loc);
				goto error_free_item;
			}
			g_object_unref(loc);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TARGET)) {
			SmlLocation *loc = NULL;
			if (!_smlLocationParse(&loc, parser, error))
				goto error_free_item;
			if (!sml_map_item_set_local(item, loc, error)) {
				g_object_unref(loc);
				goto error_free_item;
			}
			g_object_unref(loc);
		} else {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "wrong initial node %s", xmlTextReaderConstName(parser->reader));
			goto error_free_item;
		}
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return item;

error_free_item:
	g_object_unref(item);
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return NULL;
}

static gboolean
_smlCommandMapParse (SmlXmlParser *parser,
                     SmlCommand **cmd,
                     GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, cmd, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(cmd);
	
	*cmd = smlCommandNew(SML_COMMAND_TYPE_MAP, error);
	if (!*cmd)
		goto error;
	(*cmd)->refCount = 1;
	
	while (1) {
		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error_free_cmd;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MAP) && \
		xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in Map.",
				xmlTextReaderConstName(parser->reader));
			goto error_free_cmd;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMDID)) {
			guint64 id = 0;
			if (!_smlXmlParserGetID(parser, &id, SML_ELEMENT_CMDID, error))
				goto error_free_cmd;
			(*cmd)->cmdID = id;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MAPITEM)) {
			SmlMapItem *item = _smlMapItemParse(parser, error);
			if (!item)
				goto error_free_cmd;
			(*cmd)->private.map.items = g_list_append((*cmd)->private.map.items, item);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TARGET)) {
			if (!_smlLocationParse(&(*cmd)->target, parser, error))
				goto error_free_cmd;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCE)) {
			if (!_smlLocationParse(&(*cmd)->source, parser, error))
				goto error_free_cmd;
		} else {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "wrong initial node");
			goto error_free_cmd;
		}
	}
	
	/* Step once more */
	if (!_smlXmlParserStep(parser)) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
		goto error_free_cmd;
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error_free_cmd:
	smlCommandUnref(*cmd);
error:
	*cmd = NULL;
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}

static gboolean
_smlCommandAccessParse (SmlXmlParser *parser,
                        SmlCommand **cmd,
                        SmlCommandType type,
                        GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %i, %p)", __func__, parser, cmd, type, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(cmd);
	char *contenttype = NULL;
		
	*cmd = smlCommandNew(type, error);
	if (!*cmd)
		goto error;
	(*cmd)->refCount = 1;
	
	while (1) {
		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error_free_cmd;
		}
		
		if ((*cmd)->type == SML_COMMAND_TYPE_PUT &&!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_PUT) && \
		xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if ((*cmd)->type == SML_COMMAND_TYPE_GET &&!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_GET) && \
		xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in Put or Get.",
				xmlTextReaderConstName(parser->reader));
			goto error_free_cmd;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMDID)) {
			guint64 id = 0;
			if (!_smlXmlParserGetID(parser, &id, SML_ELEMENT_CMDID, error))
				goto error_free_cmd;
			(*cmd)->cmdID = id;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_LANG)) {
			if (!_smlXmlParserGetString(parser, &((*cmd)->private.access.lang), SML_ELEMENT_LANG, error))
				goto error_free_cmd;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_ITEM)) {
			(*cmd)->private.access.item = _smlItemParse(parser, (*cmd), (*cmd)->type, error);
			if (!(*cmd)->private.access.item)
				goto error_free_cmd;
			
			(*cmd)->target = (*cmd)->private.access.item->target;
			(*cmd)->private.access.item->target = NULL;
			(*cmd)->source = (*cmd)->private.access.item->source;
			(*cmd)->private.access.item->source = NULL;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_META)) {
			char *format = NULL;
			if (!_smlCommandMetaParse(parser, &format, &contenttype, NULL, NULL, NULL, error))
				goto error_free_cmd;
			if (format) smlSafeCFree(&format);
		} else {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "wrong initial node");
			goto error_free_cmd;
		}
	}
	
	if (!(*cmd)->private.access.item) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Put/Get is missing item");
		goto error_free_cmd;
	}
	
	/* We only use the content type of the put command if the item itself did not have
	 * a content type set */
	if (!(*cmd)->private.access.item->contenttype && contenttype)
		(*cmd)->private.access.item->contenttype = g_strdup(contenttype);
	
	if (contenttype)
		smlSafeCFree(&contenttype);
	
	/* Step once more */
	if (!_smlXmlParserStep(parser)) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
		goto error_free_cmd;
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error_free_cmd:
	smlCommandUnref(*cmd);
error:
	*cmd = NULL;
	if (contenttype)
		smlSafeCFree(&contenttype);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}

static gboolean
_smlResultsParse (SmlXmlParser *parser,
                  SmlCommand **cmd,
                  GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, cmd, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(cmd);
	char *contenttype = NULL;
	char *locURI = NULL;
		
	*cmd = smlCommandNew(SML_COMMAND_TYPE_RESULTS, error);
	if (!*cmd)
		goto error;
	(*cmd)->refCount = 1;
	
	(*cmd)->private.results.status = smlTryMalloc0(sizeof(SmlStatus), error);
	if (!(*cmd)->private.results.status)
		goto error;
	(*cmd)->private.results.status->refCount = 1;
	(*cmd)->private.results.status->result = (*cmd);
	(*cmd)->private.results.status->type = SML_COMMAND_TYPE_RESULTS;
	
	while (1) {
		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_RESULTS) && \
		xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in Results.",
				xmlTextReaderConstName(parser->reader));
			goto error;
		}
		
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMDID)) {
			guint64 id = 0;
			if (!_smlXmlParserGetID(parser, &id, SML_ELEMENT_CMDID, error))
				goto error;
			(*cmd)->cmdID = id;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MSGREF)) {
			guint64 id = 0;
			if (!_smlXmlParserGetID(parser, &id, SML_ELEMENT_MSGREF, error))
				goto error;
			(*cmd)->private.results.status->msgRef = id;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMDREF)) {
			guint64 id = 0;
			if (!_smlXmlParserGetID(parser, &id, SML_ELEMENT_CMDREF, error))
				goto error;
			(*cmd)->private.results.status->cmdRef = id;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCEREF)) {
			if (!_smlXmlParserGetString(parser, &locURI, SML_ELEMENT_SOURCEREF, error))
				goto error;
			
			(*cmd)->private.results.status->sourceRef = sml_location_new();
			sml_location_set_uri((*cmd)->private.results.status->sourceRef, locURI);
			smlSafeCFree(&locURI);
			if (!(*cmd)->private.results.status->sourceRef)
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TARGETREF)) {
			if (!_smlXmlParserGetString(parser, &locURI, SML_ELEMENT_TARGETREF, error))
				goto error;
				
			(*cmd)->private.results.status->targetRef = sml_location_new();
			sml_location_set_uri((*cmd)->private.results.status->targetRef, locURI);
			smlSafeCFree(&locURI);
			if (!(*cmd)->private.results.status->targetRef)
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_ITEM)) {
			(*cmd)->private.results.status->item = _smlItemParse(parser, (*cmd), (*cmd)->type, error);
			if (!(*cmd)->private.results.status->item)
				goto error;
			
			(*cmd)->target = (*cmd)->private.results.status->item->target;
			(*cmd)->private.results.status->item->target = NULL;
			(*cmd)->source = (*cmd)->private.results.status->item->source;
			(*cmd)->private.results.status->item->source = NULL;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_META)) {
			/* ignore format */
			char *format = NULL;
			if (!_smlCommandMetaParse(parser, &format, &contenttype, NULL, NULL, NULL, error))
				goto error;
			if (format) smlSafeCFree(&format);
		} else {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "wrong initial node");
			goto error;
		}
	}
	
	if (!(*cmd)->private.results.status->item) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Result is missing item");
		goto error;
	}
	
	/* We only use the content type of the put command if the item itself did not have
	 * a content type set */
	if (!(*cmd)->private.results.status->item->contenttype) {
		if (!contenttype) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Result is missing content type");
			goto error;
		}
		
		(*cmd)->private.results.status->item->contenttype = g_strdup(contenttype);
	}
	
	if (contenttype)
		smlSafeCFree(&contenttype);
	
	/* Step once more */
	if (!_smlXmlParserStep(parser)) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
		goto error;
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error:
	if (*cmd)
		smlCommandUnref(*cmd);
	*cmd = NULL;
	if (contenttype)
		smlSafeCFree(&contenttype);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}

static gboolean
_smlXmlParserFixBrokenItemData (const gchar *data,
                                gsize size,
                                gchar **fixed_data,
                                gsize *fixed_size,
                                GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %d, %p, %p, %p)", __func__, data, size, fixed_data, fixed_size, error);
	CHECK_ERROR_REF
	smlAssert(data);
	smlAssert(size);
	smlAssert(fixed_data);
	smlAssert(fixed_size);

	/* *******************
	 * fix wrong enconding
	 * *******************
	 */

	/* This fix was implemented for text/x-vMessage.
	 * Some mobiles encode SMS always as UTF-16
	 * even if the XML document is encoded in UTF-8.
	 * Example: Nokia E71
	 */

	*fixed_size = size;
	*fixed_data = smlTryMalloc0(size + 1, error);
	if (!*fixed_data)
	{
		smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, (*error)->message);
		return FALSE;
	}
	memcpy(*fixed_data, data, size);
	const char *position = *fixed_data;

	while (position + 1 < *fixed_data + *fixed_size)
	{
		/* check if the next character is a NULL byte */
		const char *byte = position + 1;
		if (*byte != 0) {
			/* the next byte is not NULL */
			position++;
			continue;
		}
		smlTrace(TRACE_INTERNAL, "%s: Found NULL byte in XML document at %p.", __func__, position);

		/* the next character is a NULL byte
		 * so let's check how long the UTF-16 string is
		 */
		const char *last_utf16 = position;
		while (last_utf16 + 1 < *fixed_data + *fixed_size &&
		       *((char *)(last_utf16 + 1)) == 0)
		{
			last_utf16 += 2;
		}

		/* go to the last NULL byte */
		last_utf16--;

		/* convert the whole strong to UTF-8 */
		smlTrace(TRACE_INTERNAL, "%s: Converting %d bytes ...", __func__, last_utf16 - position + 1);
		gsize read_bytes = 0;
		gsize written_bytes = 0;
		gchar *conv_string = g_convert(
					position, (last_utf16 - position + 1),
					"UTF-8", "UTF-16",
					&read_bytes, &written_bytes,
					error);
		if (*error != NULL)
		{
			GError *ext = NULL;
			g_set_error(&ext, SML_ERROR, SML_ERROR_GENERIC,
			            "Character conversion from UTF-16 to UTF-8 failed. %s",
			            (*error)->message);
			g_error_free(*error);
			*error = ext;
			smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, (*error)->message);
			return FALSE;
		}
		smlTrace(TRACE_INTERNAL, "%s: read %d --> written %d --> %d ::= %s", __func__, read_bytes, written_bytes, strlen(conv_string), conv_string);

		/* replace the embedded string */
		char *new_data = smlTryMalloc0(*fixed_size - read_bytes + written_bytes + 1, error);
		if (!new_data)
		{
			smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, (*error)->message);
			return FALSE;
		}
		memcpy(new_data, *fixed_data, (size_t) position - (size_t) *fixed_data);
		memcpy(new_data + (size_t) position - (size_t) *fixed_data, conv_string, written_bytes);
		memcpy(new_data + (size_t) position - (size_t) *fixed_data + written_bytes,
			position + read_bytes, *fixed_size - ( (size_t) position - (size_t) *fixed_data ) - read_bytes );

		/* fix pointers */
		*fixed_size = *fixed_size - read_bytes + written_bytes;
		position = new_data + (position - *fixed_data) + written_bytes;
		smlSafeCFree(fixed_data);
		*fixed_data = new_data;
		new_data = NULL;
		smlSafeCFree(&conv_string);

		smlTrace(TRACE_INTERNAL, "%s: Converted UTF-16 string to UTF-8", __func__);
	}
	smlTrace(TRACE_INTERNAL, "%s: Correctly encoded: %s", __func__, *fixed_data);

	/* *****************
	 * add missing CDATA
	 * *****************
	 */

	position = *fixed_data;
	
	while (position + 1 < *fixed_data + *fixed_size)
	{
		/* find item */
		const char *limit_item_start = strstr(position, "<Item>");
		if (! limit_item_start)
		{
			smlTrace(TRACE_EXIT, "%s - no (more) Item found", __func__);
			return TRUE;
		} else {
			smlTrace(TRACE_INTERNAL, "%s - Item found", __func__);
		}
		const char *limit_item_end   = strstr(limit_item_start, "</Item>");
		if (! limit_item_end)
		{
			g_warning("%s - no end of Item found.", __func__);
			smlTrace(TRACE_EXIT, "%s - no end of Item found", __func__);
			return TRUE;
		} else {
			smlTrace(TRACE_INTERNAL, "%s - end of Item found", __func__);
		}
		if (limit_item_start >= limit_item_end)
		{
			/* </Item> is before <Item> ?! */
			position = limit_item_end + strlen("</Item>");
			continue;
		}

		/* find data */
		const char *limit_data_start = strstr(limit_item_start, "<Data>");
		if (! limit_data_start)
		{
			smlTrace(TRACE_EXIT, "%s - no Data found", __func__);
			return TRUE;
		} else {
			smlTrace(TRACE_INTERNAL, "%s - Data found", __func__);
		}
		const char *limit_data_end   = strstr(limit_data_start, "</Data>");
		if (! limit_data_end)
		{
			g_warning("%s - no end of Data found.", __func__);
			smlTrace(TRACE_EXIT, "%s - no end of Data found", __func__);
			return TRUE;
		} else {
			smlTrace(TRACE_INTERNAL, "%s - end of Data found", __func__);
		}
		if (limit_data_start >= limit_data_end)
		{
			/* </Data> is before <Data> ?! */
			position = limit_data_end + strlen("</Data>");
			continue;
		}

		/* check limits */
		if (limit_item_start >= limit_data_start ||
		    limit_data_start >= limit_data_end ||
		    limit_data_end   >= limit_item_end)
		{
			/* There is something wrong with the encapsulation.
			 * Expected: <Item>...<Data>...</Data>...</Item>
			 */
			position = limit_item_end + strlen("</Item>");
			continue;
		}

		/* check for CDATA */
		const char *cdata_start = strstr(limit_data_start, "<![CDATA[");
		const char *cdata_end = NULL;
		if (cdata_start)
			cdata_end = strstr(cdata_start, "]]");
		if (cdata_start && cdata_end &&
		    limit_data_start < cdata_start &&
		    cdata_start      < cdata_end &&
		    cdata_end        < limit_data_end)
		{
			/* Anything is fine. */
			position = limit_item_end + strlen("</Item>");
			continue;
		}

		/* check that there is no tag inside Data */
		if (g_strstr_len(
			limit_data_start + strlen("<Data>"),
			limit_data_end - limit_data_start - strlen("<Data>"),
			"<"))
		{
			/* There is a tag in the Data area. */
			position = limit_item_end + strlen("</Item>");
			continue;
		}

		/* There is data in an item element and it is not CDATA.
		 *     - allocate new memory
		 *     - copy before data
		 *     - add <![CDATA[
		 *     - copy data
		 *     - add ]]
		 *     - copy after data
		 *     - fix position
		 *     - cleanup
		 */
		
		smlTrace(TRACE_INTERNAL, "%s: There is data which is not CDATA.", __func__);
		/* 1 is added to the real length * because of the
		 * trailing NULL byte. This NULL byte is required
		 * because XML documents can be represented as strings
		 * and a string must be terminated by a NULL byte.
		 */
		char *new_data = smlTryMalloc0(*fixed_size + strlen("<![CDATA[]]>") + 1, error);
		if (!new_data)
		{
			smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, (*error)->message);
			return FALSE;
		}
		size_t before_size = limit_data_start + strlen("<Data>") - *fixed_data;
		size_t data_size   = limit_data_end - limit_data_start - strlen("<Data>");
		size_t after_size  = *fixed_size - before_size - data_size;

		smlTrace(TRACE_INTERNAL, "%s: %i = %i + %i +%i", __func__,
			*fixed_size, before_size, data_size, after_size);

		memcpy(new_data, *fixed_data, before_size);
		memcpy(new_data + before_size, "<![CDATA[", strlen ("<![CDATA["));
		memcpy(new_data + before_size + strlen ("<![CDATA["),
			limit_data_start + strlen("<Data>"),
			data_size);
		memcpy(new_data + before_size + strlen ("<![CDATA[") + data_size,
			"]]>", strlen("]]>"));
		memcpy(new_data + before_size + strlen ("<![CDATA[]]>") + data_size,
			limit_data_end,
			after_size);
		smlSafeCFree(fixed_data);
		*fixed_data = new_data;
		new_data = NULL;
		*fixed_size += strlen("<![CDATA[]]>");
		/* The position must be calculated on base of the new string. */
		position = *fixed_data +
			before_size + strlen ("<![CDATA[]]>") +
			data_size + strlen("</Data></Item>");
		smlTrace(TRACE_INTERNAL, "%s: CDATA inserted", __func__);
	}

	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
}

SmlDevInfDevTyp
_smlParseDevInfDevType (const gchar *name,
                        GError **error)
{
	CHECK_ERROR_REF

	if (!strcmp(name, SML_ELEMENT_DEVTYP_PAGER)) {
		return SML_DEVINF_DEVTYP_PAGER;
	} else if (!strcmp(name, SML_ELEMENT_DEVTYP_HANDHELD)) {
		return SML_DEVINF_DEVTYP_HANDHELD;
	} else if (!strcmp(name, SML_ELEMENT_DEVTYP_PDA)) {
		return SML_DEVINF_DEVTYP_PDA;
	} else if (!strcmp(name, SML_ELEMENT_DEVTYP_PHONE)) {
		return SML_DEVINF_DEVTYP_PHONE;
	} else if (!strcmp(name, SML_ELEMENT_DEVTYP_SMARTPHONE)) {
		return SML_DEVINF_DEVTYP_SMARTPHONE;
	} else if (!strcmp(name, SML_ELEMENT_DEVTYP_SERVER)) {
		return SML_DEVINF_DEVTYP_SERVER;
	} else if (!strcmp(name, SML_ELEMENT_DEVTYP_WORKSTATION)) {
		return SML_DEVINF_DEVTYP_WORKSTATION;
	} else {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
                            "The device information DevTyp \"%s\" is unknown.", name);
		return SML_DEVINF_DEVTYP_UNKNOWN;
	}
}

SmlDevInfSyncCap
_smlParseDevInfSyncCap (guint id,
                        GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%u, %p)", __func__, id, error);
	CHECK_ERROR_REF
	SmlDevInfSyncCap result = SML_DEVINF_SYNCTYPE_UNKNOWN;

	switch (id)
	{
		case SML_DEVINF_SYNCTYPE_TWO_WAY:
			result = SML_DEVINF_SYNCTYPE_TWO_WAY;
			break;
		case SML_DEVINF_SYNCTYPE_SLOW_SYNC:
			result = SML_DEVINF_SYNCTYPE_SLOW_SYNC;
			break;
		case SML_DEVINF_SYNCTYPE_ONE_WAY_FROM_CLIENT:
			result = SML_DEVINF_SYNCTYPE_ONE_WAY_FROM_CLIENT;
			break;
		case SML_DEVINF_SYNCTYPE_REFRESH_FROM_CLIENT:
			result = SML_DEVINF_SYNCTYPE_REFRESH_FROM_CLIENT;
			break;
		case SML_DEVINF_SYNCTYPE_ONE_WAY_FROM_SERVER:
			result = SML_DEVINF_SYNCTYPE_ONE_WAY_FROM_SERVER;
			break;
		case SML_DEVINF_SYNCTYPE_REFRESH_FROM_SERVER:
			result = SML_DEVINF_SYNCTYPE_REFRESH_FROM_SERVER;
			break;
		case SML_DEVINF_SYNCTYPE_SERVER_ALERTED_SYNC:
			result = SML_DEVINF_SYNCTYPE_SERVER_ALERTED_SYNC;
			break;
		default:
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
			            "The synchronization type %u is unknwon.", id);
			smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, (*error)->message);
			return SML_DEVINF_SYNCTYPE_UNKNOWN;
			break;
	}
	smlTrace(TRACE_EXIT, "%s - %u", __func__, result);
	return result;
}

SmlDevInfCTCapType
_smlParseDevInfCTCapType (const gchar *name,
                          GError **error)
{
	CHECK_ERROR_REF

	if (!strcmp(name, SML_ELEMENT_CTTYPE)) {
		return SML_DEVINF_CTCAP_CTTYPE;
	} else if (!strcmp(name, SML_ELEMENT_PROPNAME)) {
		return SML_DEVINF_CTCAP_PROPNAME;
	} else if (!strcmp(name, SML_ELEMENT_VALENUM)) {
		return SML_DEVINF_CTCAP_VALENUM;
	} else if (!strcmp(name, SML_ELEMENT_DATATYPE)) {
		return SML_DEVINF_CTCAP_DATATYPE;
	} else if (!strcmp(name, SML_ELEMENT_SIZE)) {
		return SML_DEVINF_CTCAP_SIZE;
	} else if (!strcmp(name, SML_ELEMENT_DISPLAYNAME)) {
		return SML_DEVINF_CTCAP_DISPLAYNAME;
	} else if (!strcmp(name, SML_ELEMENT_PARAMNAME)) {
		return SML_DEVINF_CTCAP_PARAMNAME;
	} else if (!strcmp(name, SML_ELEMENT_VERCT)) {
		return SML_DEVINF_CTCAP_VERCT;
	} else if (!strcmp(name, SML_ELEMENT_PROPERTY)) {
		return SML_DEVINF_CTCAP_PROPERTY;
	} else if (!strcmp(name, SML_ELEMENT_PROPPARAM)) {
		return SML_DEVINF_CTCAP_PROPPARAM;
	} else if (!strcmp(name, SML_ELEMENT_NOTRUNCATE)) {
		return SML_DEVINF_CTCAP_NOTRUNCATE;
	} else if (!strcmp(name, SML_ELEMENT_MAXOCCUR)) {
		return SML_DEVINF_CTCAP_MAXOCCUR;
	} else if (!strcmp(name, SML_ELEMENT_MAXSIZE)) {
		return SML_DEVINF_CTCAP_MAXSIZE;
	}
	
	g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
	            "Unknown ctcap type name \"%s\"", name);
	smlTrace(TRACE_ERROR, "%s - %s", __func__, (*error)->message);
	return SML_DEVINF_CTCAP_UNKNOWN;
}


/** @brief Start the parsing
 * 
 * This will set everything up and parse until the SyncHdr
 * 
 */
gboolean
smlXmlParserStart (SmlXmlParser *parser,
                   const gchar *data,
                   gsize size,
                   GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %i, %p)", __func__, parser, data, size, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(data);
	smlAssert(size);

	/* Repair item data if necessary.
	 * Some devices does not encapsulate the data correctly
	 * (e.g. SE M600i send the second part of a chunked item
	 * without CDATA and so the CR LF is normalied to a LF
	 * which creates a wrong length and so the item is detected
	 * as damaged).
	 */
	parser->data = NULL;
	parser->size = 0;
	if (! _smlXmlParserFixBrokenItemData(
		data, size, &(parser->data), &(parser->size), error))
	{
		smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, (*error)->message);
		return FALSE;
	}
	
	char *debugstr = smlPrintBinary(parser->data, parser->size);
	smlTrace(TRACE_INTERNAL, "Xml input: %s", debugstr);
	smlLog("received-%i.xml", parser->data, parser->size);
	smlSafeCFree(&debugstr);
	
	parser->got_command = FALSE;
	
	/* Create the new parser */
	parser->reader = xmlReaderForMemory(parser->data, parser->size, "/", NULL, XML_PARSE_NONET | XML_PARSE_NOERROR | XML_PARSE_NOWARNING | XML_PARSE_NOCDATA);
	if (!parser->reader) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
		            "Unable to create new reader");
		goto error;
	}
	xmlSubstituteEntitiesDefault(1);
	
	/* Check the First Node (SyncML)*/
	if (!_smlXmlParserExpectNode(parser, XML_NODE_START, FALSE, SML_ELEMENT_SYNCML, error))
		goto error_free_reader;
	
	/* Check the Second Node (SyncHdr)*/
	if (!_smlXmlParserExpectNode(parser, XML_NODE_START, FALSE, SML_ELEMENT_SYNCHDR, error))
		goto error_free_reader;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error_free_reader:
	xmlFreeTextReader(parser->reader);
error:
	parser->reader = NULL;
	if (parser->data)
		smlSafeCFree(&(parser->data));
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}

gboolean
smlXmlParserEnd (SmlXmlParser *parser,
                 gboolean *final,
                 gboolean *end,
                 GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, parser, final, end, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	gboolean got_final = FALSE;
	
	/* Check for final */
	if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_FINAL)) {
		got_final = TRUE;
		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_FINAL) && \
			xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			if (!_smlXmlParserStep(parser)) {
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
				goto error;
			}
		}
	}
	
	if (final)
		*final = got_final;
	
	/* Check for the end. We can only decide if this was the last message if this
	 * was the final message */
	if (end) {
		if (got_final)
			*end = parser->got_command ? FALSE : TRUE;
		else
			*end = FALSE;
	}
	
	/* Check the closing SyncBody - which is perhaps only an empty element. */
	if (strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SYNCBODY) ||
	    (!xmlTextReaderIsEmptyElement(parser->reader) &&
	     xmlTextReaderNodeType(parser->reader) != XML_NODE_CLOSE
	    )
	   ) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
		            "Wrong closing syncbody node");
		goto error;
	}

	/* Check the next node (SyncML)*/
	if (!_smlXmlParserExpectNode(parser, XML_NODE_CLOSE, FALSE, SML_ELEMENT_SYNCML, error))
		goto error;
		
	xmlFreeTextReader(parser->reader);
	parser->reader = NULL;
	parser->size = 0;
	smlSafeCFree(&(parser->data));
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}

void smlXmlParserFree(SmlXmlParser *parser)
{
	smlTrace(TRACE_ENTRY, "%s(%p)", __func__, parser);
	smlAssert(parser);
	
	if (parser->reader) {
		xmlFreeTextReader(parser->reader);
	}
	if (parser->data)
		smlSafeCFree(&(parser->data));
	
	smlSafeFree((gpointer *)&parser);

	smlTrace(TRACE_EXIT, "%s", __func__);
}	

gboolean
smlXmlParserGetHeader (SmlXmlParser *parser,
                       SmlHeader **header,
                       SmlCred **cred,
                       GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, header, cred, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(header);
	smlAssert(cred);
	
	if (!xmlTextReaderConstName(parser->reader) || strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SYNCHDR) || \
		xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
		/* It is important to notice that empty headers are not allowed. */
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
		            "Wrong starting node for a header");
		goto error;
	}

	parser->version = SML_VERSION_UNKNOWN;
	
	*header = smlTryMalloc0(sizeof(SmlHeader), error);
	if (!*header)
		goto error;
	
	while (1) {
		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error_free_header;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SYNCHDR) && \
			xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in SyncHdr.",
				xmlTextReaderConstName(parser->reader));
			goto error_free_header;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_VERDTD)) {
			if (!_smlSyncHeaderParseDTD(&((*header)->version), parser, error))
				goto error_free_header;
			parser->version = (*header)->version;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_VERPROTO)) {
			if (!_smlSyncHeaderParseProto(&((*header)->protocol), parser, error))
				goto error_free_header;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SESSIONID)) {
			guint64 id = 0;
			if (!_smlXmlParserGetID(parser, &id, SML_ELEMENT_SESSIONID, error))
				goto error_free_header;
			(*header)->sessionID = id;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MSGID)) {
			guint64 id = 0;
			if (!_smlXmlParserGetID(parser, &id, SML_ELEMENT_MSGID, error))
				goto error_free_header;
			(*header)->messageID = id;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TARGET)) {
			if (!_smlLocationParse(&((*header)->target), parser, error))
				goto error_free_header;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCE)) {
			if (!_smlLocationParse(&((*header)->source), parser, error))
				goto error_free_header;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_RESPURI)) {
			/* Only one RespURI is allowed.
			 * If the header is not correct (more than one RespURI)
			 * then the last RespURI is taken.
			 */
			if ((*header)->responseURI) {
				smlTrace(TRACE_ERROR, "%s: There is more than on RespURI.", __func__);
				smlSafeCFree(&((*header)->responseURI));
			}
			if (!_smlXmlParserGetString(parser, &((*header)->responseURI), SML_ELEMENT_RESPURI, error))
				goto error_free_header;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_NORESP)) {
			/* This is an empty element. So the parser must not move one step forward. */
			(*header)->noResponse = TRUE;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_META)) {
			if (!_smlMessageParseSynchdrMeta(parser, &((*header)->maxmsgsize), &((*header)->maxobjsize), &((*header)->emi), error))
				goto error_free_header;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CRED)) {
			*cred = _smlCredParse(parser, error);
			if (!(*cred))
				goto error_free_header;
		} 
		
		else {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
			            "Element %s not supported for SyncHdr.",
				(char *)xmlTextReaderConstName(parser->reader));
			goto error_free_header;
		}
	}
	
	if (!(*header)->protocol) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No protocol set");
		goto error_free_header;
	}
	
	if (!(*header)->version) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No dtd version set");
		goto error_free_header;
	}
	
	if (!(*header)->sessionID) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No sessionID set");
		goto error_free_header;
	}
	
	if (!(*header)->target) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No target set");
		goto error_free_header;
	}
	
	if (!(*header)->source) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No source set");
		goto error_free_header;
	}
	
	if (!(*header)->messageID) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No msgid set");
		goto error_free_header;
	}
	
	/* Check the Next Node (SyncBody)*/
	if (!_smlXmlParserExpectNode(parser, XML_NODE_START, FALSE, SML_ELEMENT_SYNCBODY, error))
		goto error_free_header;
	
	/* Step once more if it is not an empty element. */
	if (!xmlTextReaderIsEmptyElement(parser->reader) && !_smlXmlParserStep(parser)) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
		goto error_free_header;
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error_free_header:
	smlHeaderFree(*header);
	*header = NULL;
	
	if (*cred)
		smlCredUnref(*cred);
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}

gboolean
smlXmlParserGetStatus (SmlXmlParser *parser,
                       SmlStatus **status,
                       GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, status, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(status);
	
	char *locURI = NULL;
	SmlItem *item = NULL;
	
	/* Lets check if the next node is a command node */
	if (smlCommandTypeFromString((char *)xmlTextReaderConstName(parser->reader), error) != SML_COMMAND_TYPE_UNKNOWN && xmlTextReaderNodeType(parser->reader) == XML_NODE_START) {
		*status = NULL;
		smlTrace(TRACE_EXIT, "%s: Next is command", __func__);
		return TRUE;
	} else {
		g_error_free(*error);
		*error = NULL;
	}
	
	/* Lets check if the next node is a final node */
	if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_FINAL) && (xmlTextReaderNodeType(parser->reader) == XML_NODE_START || xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE)) {
		*status = NULL;
		smlTrace(TRACE_EXIT, "%s: Next is final", __func__);
		return TRUE;
	}
	
	/* Let's check if the next node is a closing or empty syncbody node. */
	if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SYNCBODY) &&
	    (xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE ||
             xmlTextReaderIsEmptyElement(parser->reader)
            )
           ) {
		*status = NULL;
		smlTrace(TRACE_EXIT, "%s: Next is closing syncbody", __func__);
		return TRUE;
	}

	if (strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_STATUS)) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unexpected element <%s>.",
			(char *)xmlTextReaderConstName(parser->reader));
		goto error;
	}

	if (xmlTextReaderIsEmptyElement(parser->reader))
	{
		/* An empty status element is not allowed.
		 * Try to finish in a way that the parser can still be used.
		 * The parser can ignore this mistake and finish correctly.
		 * Nevertheless a second mistake which crashs the NextStep
		 * function cannot be handled.
		 */
		_smlXmlParserStep(parser);
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Empty status element");
		goto error;
	}
	
	if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
			"The element %s is not the start node of Status.",
			xmlTextReaderConstName(parser->reader));
		goto error;
	}
	
	*status = smlStatusNew(SML_ERROR_UNKNOWN, 0, 0, NULL, NULL, SML_COMMAND_TYPE_UNKNOWN, error);
	if (!*status)
		goto error;
		
	while (1) {
		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_STATUS) && \
			xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in Status.",
				xmlTextReaderConstName(parser->reader));
			goto error;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMDID)) {
			//Errr... who cares?
			if (!_smlXmlParserExpectNode(parser, XML_NODE_TEXT, FALSE, NULL, error))
				goto error;
			if (!_smlXmlParserExpectNode(parser, XML_NODE_CLOSE, FALSE, SML_ELEMENT_CMDID, error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MSGREF)) {
			/* MsgRef is defined as parsed character data:
			   OMA-SUP-DTD_SyncML_RepPro-V1_2-20070221-A.txt:<!ELEMENT MsgRef (#PCDATA)>

			   SyncML Reprent Version 1.1 20020215 - Chapter 5.1.13 MsgRef:
			   "[...] reference to a SyncML session-uniquie identifier referenced by SyncML result or response status."

			   So it could be something else then an unsigned integer.
			*/
			guint64 id = 0;
			if (!_smlXmlParserGetID(parser, &id, SML_ELEMENT_MSGREF, error))
				goto error;
			(*status)->msgRef = id;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMDREF)) {
			guint64 id = 0;
			if (!_smlXmlParserGetID(parser, &id, SML_ELEMENT_CMDREF, error))
				goto error;
			(*status)->cmdRef = id;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMD)) {
			char *cmdname = NULL;
			if (!_smlXmlParserGetString(parser, &cmdname, SML_ELEMENT_CMD, error))
				goto error;
			(*status)->type = smlCommandTypeFromString(cmdname, error);
			smlSafeCFree(&cmdname);
			if ((*status)->type == SML_COMMAND_TYPE_UNKNOWN)
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DATA)) {
			if (!_smlXmlParserGetString(parser, &((*status)->data), SML_ELEMENT_DATA, error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCEREF)) {
			if (!_smlXmlParserGetString(parser, &locURI, SML_ELEMENT_SOURCEREF, error))
				goto error;
			
			(*status)->sourceRef = sml_location_new();
			sml_location_set_uri((*status)->sourceRef, locURI);
			smlSafeCFree(&locURI);
			if (!(*status)->sourceRef)
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TARGETREF)) {
			if (!_smlXmlParserGetString(parser, &locURI, SML_ELEMENT_TARGETREF, error))
				goto error;
				
			(*status)->targetRef = sml_location_new();
			sml_location_set_uri((*status)->targetRef, locURI);
			smlSafeCFree(&locURI);
			if (!(*status)->targetRef)
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_ITEM)) {
			/* Do not send SML_COMMAND_TYPE_ALERT
			 * because this can give trouble.
			 * Example: Error message for a failed ADD
			 */
			item = _smlItemParse(parser, NULL, (*status)->type, error);
			if (!item)
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CHAL)) {
			(*status)->chal = _smlChalParse(parser, error);
			if (!(*status)->chal)
				goto error;
		} else {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "wrong initial node");
			goto error;
		}
	}
	
	/* Step once more */
	if (!_smlXmlParserStep(parser)) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
		goto error;
	}

	if (item)
	{
		if ((*status)->type == SML_COMMAND_TYPE_ALERT) {
			(*status)->anchor = item->anchor;
			item->anchor = NULL;
		} else if ((*status)->type == SML_COMMAND_TYPE_ADD ||
		           (*status)->type == SML_COMMAND_TYPE_REPLACE ||
		           (*status)->type == SML_COMMAND_TYPE_DELETE) {
			/* Usually Sync is commited at once or together with an anchor.
			 * If a single ADD, REPLACE or DELETE gets an item
			 * then there is something going wrong.
			 */
			g_warning("Received explicit status for add, delete or replace (%s).", xmlBufferContent(item->buffer));
		} else {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Got wrong item");
			smlItemUnref(item);
			goto error;
		}
		smlItemUnref(item);
	}
	
	/* MsgRef is defined as #PCDATA - so it doesn't have to be a
	   unsigned integer AND it could be set to 0/zero as well.

	   This sanity check breaks if MsgRef is set to 0/zero.

	if (!(*status)->msgRef) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No msgref set");
		goto error_free_status;
	}
	*/
	
	if ((*status)->type == SML_COMMAND_TYPE_UNKNOWN) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No cmd set");
		goto error;
	}
	
	smlTrace(TRACE_INTERNAL, "Got status %p with: cmdRef %i, msgRef %i, type %i, data %s", *status, (*status)->cmdRef, (*status)->msgRef, (*status)->type, VA_STRING((*status)->data));
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error:
	if (*status)
		smlStatusUnref(*status);
	*status = NULL;
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}

SmlParserResult
smlXmlParserGetCommand (SmlXmlParser *parser,
                        SmlCommand **cmd,
                        GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, cmd, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(cmd);
	*cmd = NULL;
	
	/* Lets check if the next node is a final node */
	if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_FINAL) && (xmlTextReaderNodeType(parser->reader) == XML_NODE_START || xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE)) {
		*cmd = NULL;
		smlTrace(TRACE_EXIT, "%s: Next is final", __func__);
		return SML_PARSER_RESULT_OTHER;
	}
	
	/* Let's check if the next node is a closing or empty syncbody node. */
	if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SYNCBODY) &&
	    (xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE ||
             xmlTextReaderIsEmptyElement(parser->reader)
            )
           ) {
		*cmd = NULL;
		smlTrace(TRACE_EXIT, "%s: Next is closing syncbody", __func__);
		return SML_PARSER_RESULT_OTHER;
	}

	/* Lets check if the next node status node */
	if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_STATUS) && xmlTextReaderNodeType(parser->reader) == XML_NODE_START) {
		*cmd = NULL;
		smlTrace(TRACE_EXIT, "%s: Next is status", __func__);
		return SML_PARSER_RESULT_STATUS;
	}
	
	SmlCommandType type = smlCommandTypeFromString((char *)xmlTextReaderConstName(parser->reader), error);
	if (!type)
		goto error;
	
	SmlParserResult result = SML_PARSER_RESULT_NORMAL;
	
	switch (type) {
		case SML_COMMAND_TYPE_ALERT:
			if (!_smlAlertParse(parser, cmd, error))
				goto error;
			break;
		case SML_COMMAND_TYPE_PUT:
		case SML_COMMAND_TYPE_GET:
			if (!_smlCommandAccessParse(parser, cmd, type, error))
				goto error;
			break;
		case SML_COMMAND_TYPE_SYNC:
			if (xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
				if (!_smlXmlParserStep(parser)) {
					g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
					goto error_free_cmd;
				}
				
				smlTrace(TRACE_EXIT, "%s: SML_PARSER_RESULT_CLOSE", __func__);
				return SML_PARSER_RESULT_CLOSE;
			} else {
				if (!_smlCommandSyncParse(parser, cmd, error))
					goto error;
				
				result = SML_PARSER_RESULT_OPEN;
			}
			break;
		case SML_COMMAND_TYPE_MAP:
			if (!_smlCommandMapParse(parser, cmd, error))
				goto error;
			break;
		case SML_COMMAND_TYPE_ADD:
			if (!_smlChangeParse(parser, cmd, SML_COMMAND_TYPE_ADD, SML_ELEMENT_ADD, error))
				goto error;
			break;
		case SML_COMMAND_TYPE_REPLACE:
			if (!_smlChangeParse(parser, cmd, SML_COMMAND_TYPE_REPLACE, SML_ELEMENT_REPLACE, error))
				goto error;
			break;
		case SML_COMMAND_TYPE_DELETE:
			if (!_smlChangeParse(parser, cmd, SML_COMMAND_TYPE_DELETE, SML_ELEMENT_DELETE, error))
				goto error;
			break;
		case SML_COMMAND_TYPE_RESULTS:
			if (!_smlResultsParse(parser, cmd, error))
				goto error;
			break;
		default:
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unsupported command type");
			goto error;
	}
	
	if (!(*cmd)->cmdID) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No cmdid set");
		goto error_free_cmd;
	}
	
	parser->got_command = TRUE;
	
	smlTrace(TRACE_EXIT, "%s: %i", __func__, result);
	return result;

error_free_cmd:
	if (*cmd)
		smlCommandUnref(*cmd);
error:
	*cmd = NULL;
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return SML_PARSER_RESULT_ERROR;
}

SmlXmlParser*
smlXmlParserNew (SmlParserFunctions *functions,
                 GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, functions,error);
	CHECK_ERROR_REF
	smlAssert(functions);
	
	SmlXmlParser *parser = smlTryMalloc0(sizeof(SmlXmlParser), error);
	if (!parser)
		goto error;
	
	functions->start = (SmlParserStartFunction)smlXmlParserStart;
	functions->free = (SmlParserFreeFunction)smlXmlParserFree;
	functions->end = (SmlParserEndFunction)smlXmlParserEnd;
	functions->get_header = (SmlParserHeaderFunction)smlXmlParserGetHeader;
	functions->get_cmd = (SmlParserCommandFunction)smlXmlParserGetCommand;
	functions->get_status = (SmlParserStatusFunction)smlXmlParserGetStatus;

	smlTrace(TRACE_EXIT, "%s: %p", __func__, parser);
	return parser;
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return NULL;
}

static gboolean
_smlXmlDevInfDataStoreParseDSMem (SmlXmlParser *parser,
                                  SmlDevInfDataStore *datastore,
                                  GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, datastore, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(datastore);

	if (xmlTextReaderIsEmptyElement(parser->reader)) {
		/* An empty DSMem element is allowed according to SyncML DevInf 1.1.2. */
		smlTrace(TRACE_EXIT, "%s - empty DSMem", __func__);
		return TRUE;
	}
		
	while (1) {
		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DSMEM) && \
		xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in DSMem.",
				xmlTextReaderConstName(parser->reader));
			goto error;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MAXID)) {
			guint64 maxid = 0;
			if (!_smlXmlParserGetID(parser, &maxid, SML_ELEMENT_MAXID, error))
				goto error;
			sml_dev_inf_data_store_set_max_id(datastore, maxid);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SHAREDMEM)) {	
			sml_dev_inf_data_store_set_shared_mem(datastore, TRUE);
			if (!_smlXmlParserStep(parser)) {
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
				goto error;
			}
			
			if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SHAREDMEM) && \
				xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
				if (!_smlXmlParserStep(parser)) {
					g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes2");
					goto error;
				}
			}
			continue;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MAXMEM)) {
			guint64 maxmem = 0;
			if (!_smlXmlParserGetID(parser, &maxmem, SML_ELEMENT_MAXMEM, error))
				goto error;
			sml_dev_inf_data_store_set_max_mem(datastore, maxmem);
		} else {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "wrong initial node");
			goto error;
		}
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}

static gboolean
_smlXmlDevInfDataStoreParseSyncCap (SmlXmlParser *parser,
                                    SmlDevInfDataStore *datastore,
                                    GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, datastore, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(datastore);

	while (1) {
		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SYNCCAP) && \
		xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in SyncCap.",
				xmlTextReaderConstName(parser->reader));
			goto error;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SYNCTYPE)) {
			guint64 id = 0;
			if (!_smlXmlParserGetID(parser, &id, SML_ELEMENT_SYNCTYPE, error))
				goto error;
			id = 1 << (id - 1);
			SmlDevInfSyncCap synccap = _smlParseDevInfSyncCap(id, error);
			if (synccap == SML_DEVINF_SYNCTYPE_UNKNOWN &&
			    error != NULL)
				goto error;
			
			if (synccap != SML_DEVINF_SYNCTYPE_UNKNOWN)
				sml_dev_inf_data_store_set_sync_cap(datastore, synccap, TRUE);
		} else {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "wrong initial node");
			goto error;
		}
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}

static gboolean
_smlXmlDevInfDataStoreParseCTCap11 (SmlXmlParser *parser,
                                    SmlDevInf *devinf,
                                    GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, devinf, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(devinf);

	/* This function was designed to parse one CTCap section */
	/* which can include several CTCap definitions           */

	SmlDevInfContentType *ct = NULL;
	SmlDevInfCTCap *ctcap = NULL;
	SmlDevInfProperty *property = NULL;
	SmlDevInfPropParam *param = NULL;

	if (!_smlXmlParserStep(parser)) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
		goto error;
	}

	char *value = NULL;
	while (1) {

		const char *elem_name;
		elem_name = (const char*)xmlTextReaderConstName(parser->reader);

		smlTrace(TRACE_INTERNAL, "read: %s\n", elem_name);
		if (!strcmp(elem_name, SML_ELEMENT_CTCAP) &&
			xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			// this is the end of the CTCap section
			break;
		}

		if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START)
		{
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in CTCap.",
				xmlTextReaderConstName(parser->reader));
			goto error;
		}

		/* determine list item type */
		SmlDevInfCTCapType type;
		type = _smlParseDevInfCTCapType(elem_name, error);
		value = NULL;
		if (type != SML_DEVINF_CTCAP_UNKNOWN)
		{
			_smlXmlParserGetString(parser, &value, elem_name, error);
		}

		/* now react on the different items */
		switch(type)
		{
			case SML_DEVINF_CTCAP_CTTYPE:
				// CTType => new ctcap definition
				if (ctcap != NULL)
				{
					if (property != NULL)
                                	{
                                        	if (param != NULL)
                                        	{
                                                	if (!sml_dev_inf_property_add_param(property, param, error))
								goto error;
							g_object_unref(param);
                                                	param = NULL;
                                        	}
                                        	if (!sml_dev_inf_ctcap_add_property(ctcap, property, error))
							goto error;
						g_object_unref(property);
						property = NULL;
					}
					if (!sml_dev_inf_add_ctcap(devinf, ctcap, error))
						goto error;
					g_object_unref(ctcap);
					ctcap = NULL;
					g_object_unref(ct);
					ct = NULL;
				}
				ct = sml_dev_inf_content_type_new(value, NULL, error);
				if (!ct)
					goto error;
				ctcap = sml_dev_inf_ctcap_new(ct, error);
				if (!ctcap)
					goto error;
				break;
			case SML_DEVINF_CTCAP_VERCT:
				if (ct == NULL)
					goto error;
				if (!sml_dev_inf_content_type_set_verct(ct, value, error))
					goto error;
				break;
			case SML_DEVINF_CTCAP_PROPNAME:
				// PropName => new property
				if (ctcap == NULL)
					goto error;
				if (property != NULL)
                               	{
                                       	if (param != NULL)
                                       	{
                                               	if (!sml_dev_inf_property_add_param(property, param, error))
							goto error;
						g_object_unref(param);
                                               	param = NULL;
                                       	}
                                       	if (!sml_dev_inf_ctcap_add_property(ctcap, property, error))
						goto error;
					g_object_unref(property);
					property = NULL;
				}
				property = sml_dev_inf_property_new();
				if (!property) {
					g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Cannot create new instance of SmlDevInfProperty - out of memory.");
					goto error;
				}
				if (!sml_dev_inf_property_set_prop_name(property, value, error))
					goto error;
				break;
			case SML_DEVINF_CTCAP_DATATYPE:
				if (property == NULL)
					goto error;
				if (param != NULL) {
					if (!sml_dev_inf_prop_param_set_data_type(param, value, error))
						goto error;
				} else {
					if (!sml_dev_inf_property_set_data_type(property, value, error))
						goto error;
				}
				break;
			case SML_DEVINF_CTCAP_MAXOCCUR:
				if (property == NULL)
					goto error;
				sml_dev_inf_property_set_max_occur(property, atoi(value));
				break;
			case SML_DEVINF_CTCAP_MAXSIZE:
				if (property == NULL)
					goto error;
				if (!sml_dev_inf_property_set_max_size(property, atoi(value), error))
					goto error;
				break;
			case SML_DEVINF_CTCAP_NOTRUNCATE:
				if (property == NULL)
					goto error;
				sml_dev_inf_property_set_no_truncate(property, TRUE);
				break;
			case SML_DEVINF_CTCAP_DISPLAYNAME:
				if (property == NULL)
					goto error;
				if (param != NULL) {
					if (!sml_dev_inf_prop_param_set_display_name(param, value, error))
						goto error;
				} else {
					if (!sml_dev_inf_property_set_display_name(property, value, error))
						goto error;
				}
				break;
			case SML_DEVINF_CTCAP_VALENUM:
				if (property == NULL)
					goto error;
				if (param != NULL) {
					if (!sml_dev_inf_prop_param_add_val_enum(param, value, error))
						goto error;
				} else {
					if (!sml_dev_inf_property_add_val_enum(property, value, error))
						goto error;
				}
				break;
			case SML_DEVINF_CTCAP_PARAMNAME:
				if (property == NULL)
					goto error;
                                if (param != NULL)
                                {
                                       	if (!sml_dev_inf_property_add_param(property, param, error))
						goto error;
					g_object_unref(param);
                                       	param = NULL;
                                }
				param = sml_dev_inf_prop_param_new();
				if (!param) {
					g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Cannot create SmlDevInfPropParam during SyncML 1.1 DevInf parsing.");
					goto error;
				}
				if (!sml_dev_inf_prop_param_set_param_name(param, value, error))
					goto error;
				break;
			case SML_DEVINF_CTCAP_SIZE:
				if (!sml_dev_inf_property_set_size(property, atoi(value), error))
					goto error;
				break;
			default:
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unknown CTCapType: %s", elem_name);
				goto error;
		}
		smlSafeCFree(&value);

		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error;
		}
	}
	if (param != NULL) {
		if (!sml_dev_inf_property_add_param(property, param, error))
			goto error;
		g_object_unref(param);
		param = NULL;
	}
	if (property != NULL) {
		if (!sml_dev_inf_ctcap_add_property(ctcap, property, error))
			goto error;
		g_object_unref(property);
		property = NULL;
	}
	if (ctcap != NULL) {
		if (!sml_dev_inf_add_ctcap(devinf, ctcap, error))
			goto error;
		g_object_unref(ctcap);
		ctcap = NULL;
	}
	if (ct != NULL)
		g_object_unref(ct);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
error:
	if (value != NULL)
		smlSafeCFree(&value);
	if (param)
		g_object_unref(param);
	if (property)
		g_object_unref(property);
	if (ctcap)
		g_object_unref(ctcap);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}

static SmlDevInfPropParam*
_smlXmlDevInfDataStoreParseCTCap12PropParam (SmlXmlParser *parser,
                                             GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, parser, error);
	CHECK_ERROR_REF
	smlAssert(parser);

	if (!_smlXmlParserStep(parser)) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
		goto error;
	}

	SmlDevInfPropParam *param = sml_dev_inf_prop_param_new();
	if (!param) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Cannot create SmlDevInfPropParam during SyncML 1.2 DevInf parsing.");
		goto error;
	}

	char *value = NULL;
	while (1) {

		const char *elem_name;
		elem_name = (const char*)xmlTextReaderConstName(parser->reader);

		smlTrace(TRACE_INTERNAL, "read: %s\n", elem_name);
		if (!strcmp(elem_name, SML_ELEMENT_PROPPARAM) &&
			xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			// this is the end of the PropParam section
			break;
		}

		if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START)
		{
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in PropParam.",
				xmlTextReaderConstName(parser->reader));
			goto error;
		}

		/* determine item type */
		SmlDevInfCTCapType type;
		type = _smlParseDevInfCTCapType(elem_name, error);
		value = NULL;
		if (type != SML_DEVINF_CTCAP_UNKNOWN)
		{
			_smlXmlParserGetString(parser, &value, elem_name, error);
		}

		/* now react on the different items */
		switch(type)
		{
			case SML_DEVINF_CTCAP_PARAMNAME:
				sml_dev_inf_prop_param_set_param_name(param, value, error);
				break;
			case SML_DEVINF_CTCAP_DATATYPE:
				sml_dev_inf_prop_param_set_data_type(param, value, error);
				break;
			case SML_DEVINF_CTCAP_DISPLAYNAME:
				sml_dev_inf_prop_param_set_display_name(param, value, error);
				break;
			case SML_DEVINF_CTCAP_VALENUM:
				sml_dev_inf_prop_param_add_val_enum(param, value, error);
				break;
			default:
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unknown CTCapType for PropParam: %s", elem_name);
				goto error;
		}
		smlSafeCFree(&value);

		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error;
		}
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return param;
error:
	if (value != NULL)
		smlSafeCFree(&value);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return NULL;
}

static SmlDevInfProperty*
_smlXmlDevInfDataStoreParseCTCap12Property (SmlXmlParser *parser,
                                            GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, parser, error);
	CHECK_ERROR_REF
	smlAssert(parser);

	SmlDevInfProperty *property = NULL;

	if (!_smlXmlParserStep(parser)) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
		goto error;
	}

	property = sml_dev_inf_property_new();
	if (!property) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Cannot create new instance of SmlDevInfProperty - out of memory.");
		goto error;
	}

	SmlDevInfPropParam *param;
	char *value = NULL;
	while (1) {

		const char *elem_name;
		elem_name = (const char*)xmlTextReaderConstName(parser->reader);

		smlTrace(TRACE_INTERNAL, "read: %s\n", elem_name);
		if (!strcmp(elem_name, SML_ELEMENT_PROPERTY) &&
			xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			// this is the end of the Property section
			break;
		}

		if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START)
		{
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in Property.",
				xmlTextReaderConstName(parser->reader));
			goto error;
		}

		/* determine item type */
		SmlDevInfCTCapType type;
		type = _smlParseDevInfCTCapType(elem_name, error);
		value = NULL;
		if (type != SML_DEVINF_CTCAP_UNKNOWN &&
		    type != SML_DEVINF_CTCAP_PROPPARAM)
		{
			_smlXmlParserGetString(parser, &value, elem_name, error);
		}

		/* now react on the different items */
		switch(type)
		{
			case SML_DEVINF_CTCAP_PROPNAME:
				if(!sml_dev_inf_property_set_prop_name(property, value, error))
					goto error;
				break;
			case SML_DEVINF_CTCAP_DATATYPE:
				if (!sml_dev_inf_property_set_data_type(property, value, error))
					goto error;
				break;
			case SML_DEVINF_CTCAP_MAXOCCUR:
				sml_dev_inf_property_set_max_occur(property, atoi(value));
				break;
			case SML_DEVINF_CTCAP_MAXSIZE:
				if (!sml_dev_inf_property_set_max_size(property, atoi(value), error))
					goto error;
				break;
			case SML_DEVINF_CTCAP_NOTRUNCATE:
				sml_dev_inf_property_set_no_truncate(property, TRUE);
				break;
			case SML_DEVINF_CTCAP_DISPLAYNAME:
				if (!sml_dev_inf_property_set_display_name(property, value, error))
					goto error;
				break;
			case SML_DEVINF_CTCAP_VALENUM:
				if (!sml_dev_inf_property_add_val_enum(property, value, error))
					goto error;
				break;
			case SML_DEVINF_CTCAP_PROPPARAM:
				param = _smlXmlDevInfDataStoreParseCTCap12PropParam(parser, error);
				if (!param)
					goto error;
				if (!sml_dev_inf_property_add_param(property, param, error))
					goto error;
				g_object_unref(param);
				param = NULL;
				break;
			case SML_DEVINF_CTCAP_SIZE:
				if (!sml_dev_inf_property_set_size(property, atoi(value), error))
					goto error;
				break;
			default:
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unknown CTCapType for Property: %s", elem_name);
				goto error;
		}
		if (value) smlSafeCFree(&value);

		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error;
		}
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return property;
error:
	if (value != NULL)
		smlSafeCFree(&value);
	if (property)
		g_object_unref(property);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return NULL;
}

static gboolean
_smlXmlDevInfDataStoreParseCTCap12 (SmlXmlParser *parser,
                                    SmlDevInf *devinf,
                                    GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, devinf, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(devinf);

	SmlDevInfContentType *ct = NULL;
	SmlDevInfProperty *property = NULL;
	SmlDevInfCTCap *ctcap = NULL;

	/* This function was designed to parse one CTCap section */
	/* which can include only one CTCap definition           */

	if (!_smlXmlParserStep(parser)) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
		goto error;
	}

	ct = sml_dev_inf_content_type_new(NULL, NULL, error);
	if (!ct)
		goto error;
	ctcap = sml_dev_inf_ctcap_new(ct, error);
	if (!ctcap)
		goto error;

	char *value = NULL;
	while (1) {

		const char *elem_name;
		elem_name = (const char*)xmlTextReaderConstName(parser->reader);

		smlTrace(TRACE_INTERNAL, "read: %s\n", elem_name);
		if (!strcmp(elem_name, SML_ELEMENT_CTCAP) &&
			xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			// this is the end of the CTCap section
			break;
		}

		if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START)
		{
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in CtCap.",
				xmlTextReaderConstName(parser->reader));
			goto error;
		}

		/* determine item type */
		SmlDevInfCTCapType type;
		type = _smlParseDevInfCTCapType(elem_name, error);
		value = NULL;
		if (type != SML_DEVINF_CTCAP_UNKNOWN &&
		    type != SML_DEVINF_CTCAP_PROPERTY)
		{
			_smlXmlParserGetString(parser, &value, elem_name, error);
		}

		/* now react on the different items */
		switch(type)
		{
			case SML_DEVINF_CTCAP_CTTYPE:
				if (!sml_dev_inf_content_type_set_cttype(ct, value, error))
					goto error;
				break;
			case SML_DEVINF_CTCAP_VERCT:
				if (!sml_dev_inf_content_type_set_verct(ct, value, error))
					goto error;
				break;
			case SML_DEVINF_CTCAP_PROPERTY:
				property = _smlXmlDevInfDataStoreParseCTCap12Property(parser, error);
				if (!property)
					goto error;
				if (!sml_dev_inf_ctcap_add_property(ctcap, property, error))
					goto error;
				g_object_unref(property);
				property = NULL;
				break;
			default:
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unknown CTCapType for CTCap: %s", elem_name);
				goto error;
		}
		if (value) smlSafeCFree(&value);

		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error;
		}
	}

	g_object_unref(ct);
	ct = NULL;

	if (!sml_dev_inf_add_ctcap(devinf, ctcap, error))
		goto error;
	g_object_unref(ctcap);
	ctcap = NULL;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
error:
	if (ct)
		g_object_unref(ct);
	if (ctcap)
		g_object_unref(ctcap);
	if (property)
		g_object_unref(ctcap);
	if (value != NULL)
		smlSafeCFree(&value);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}

static gboolean
_smlXmlDevInfDataStoreParseCTCap (SmlXmlParser *parser,
                                  SmlDevInf *devinf,
                                  GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, devinf, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(devinf);

	/* SyncML 1.0 and 1.1 start a new CTCap with a new CTType definition */
	/* SyncML 1.2 starts a new CTCap with a new CTCap definition */
	/* SyncML 1.2 attaches the CTCaps to the appropriate datastore */
	/* SyncML 1.0 and 1.1 attach the CTCaps to DevInf */
	/* The SyncML version is in devinf->version */

	gboolean ret;
	if (sml_dev_inf_get_ver_dtd(devinf) == SML_DEVINF_VERSION_12)
		ret = _smlXmlDevInfDataStoreParseCTCap12(parser, devinf, error);
	else
		ret = _smlXmlDevInfDataStoreParseCTCap11(parser, devinf, error);

	smlTrace(TRACE_EXIT, "%s (%d)", __func__, ret);
	return ret;
}

static gboolean
_smlXmlDevInfDataStoreParseRxTx (SmlXmlParser *parser,
                                 const gchar *element,
                                 SmlDevInfDataStore *datastore,
                                 GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %s, %p, %p)", __func__, parser, VA_STRING(element), datastore, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(element);
	
	SmlDevInfContentType *ct = NULL;
	char *cttype = NULL;
	char *verct = NULL;

	if (!_smlXmlParserStep(parser)) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
		goto error;
	}
	
	while (1) {
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), element) && \
		xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in %s.",
				xmlTextReaderConstName(parser->reader), element);
			goto error;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CTTYPE)) {
			if (!_smlXmlParserGetString(parser, &cttype, SML_ELEMENT_CTTYPE, error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_VERCT)) {
			/* Some phones send an empty VerCT for formats that dont have a version
			 * (like notes). */
			if (!_smlXmlParserStep(parser)) {
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No node at all");
				goto error;
			}
			
			if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_VERCT) && \
				xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
				verct = g_strdup("");
			} else if (xmlTextReaderNodeType(parser->reader) == XML_NODE_TEXT) {
				verct = g_strstrip(g_strdup((char *)xmlTextReaderConstValue(parser->reader)));
				
				if (!_smlXmlParserExpectNode(parser, XML_NODE_CLOSE, FALSE, SML_ELEMENT_VERCT, error))
					goto error;
			} else {
				verct = g_strdup("");
				continue;
			}
		} else {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "wrong initial node");
			goto error;
		}
		
		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error;
		}
	}

	/* configure data store */

	ct = sml_dev_inf_content_type_new(cttype, verct, error);
	if (!ct)
		goto error;
	if (!strcmp(SML_ELEMENT_RX, element)) {
		if (!sml_dev_inf_data_store_add_tx(datastore, ct, error))
			goto error;
	} else if (!strcmp(SML_ELEMENT_RXPREF, element)) {
		if (!sml_dev_inf_data_store_set_rx_pref(datastore, ct, error))
			goto error;
	} else if (!strcmp(SML_ELEMENT_TX, element)) {
		if (!sml_dev_inf_data_store_add_tx(datastore, ct, error))
			goto error;
	} else if (!strcmp(SML_ELEMENT_TXPREF, element)) {
		if (!sml_dev_inf_data_store_set_tx_pref(datastore, ct, error))
			goto error;
	} else {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "The element %s is an unknown Rx/Tx element.", element);
		goto error;
	}

	smlSafeCFree(&cttype);
	smlSafeCFree(&verct);
	g_object_unref(ct);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
error:
	if (cttype)
		smlSafeCFree(&cttype);
	if (verct)
		smlSafeCFree(&verct);
	if (ct)
		g_object_unref(ct);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}

static gboolean
_smlXmlDevInfDataStoreParse (SmlXmlParser *parser,
                             SmlDevInf *devinf,
                             GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, devinf, error);
	CHECK_ERROR_REF
	smlAssert(devinf);
	smlAssert(parser);
	
	SmlDevInfDataStore *datastore = sml_dev_inf_data_store_new("dummy", error);
	if (!datastore)
		goto error;
	
	if (!_smlXmlParserStep(parser)) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
		goto error;
	}
		
	while (1) {
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DATASTORE) && \
		xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in Datastore.",
				xmlTextReaderConstName(parser->reader));
			goto error;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCEREF)) {
			char *sourceref = NULL;
			if (!_smlXmlParserGetString(parser, &sourceref, SML_ELEMENT_SOURCEREF, error))
				goto error;
			if (!sml_dev_inf_data_store_set_source_ref(datastore, sourceref, error))
				goto error;
			smlSafeCFree(&sourceref);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DISPLAYNAME)) {
			char *displayname = NULL;
			if (!_smlXmlParserGetString(parser, &displayname, SML_ELEMENT_DISPLAYNAME, error))
				goto error;
			if (!sml_dev_inf_data_store_set_display_name(datastore, displayname, error))
				goto error;
			smlSafeCFree(&displayname);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MAXGUIDSIZE)) {
			guint64 maxguidsize = 0;
			if (!_smlXmlParserGetID(parser, &maxguidsize, SML_ELEMENT_MAXGUIDSIZE, error))
				goto error;
			sml_dev_inf_data_store_set_max_guid_size(datastore, maxguidsize);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_RXPREF)) {
			if (!_smlXmlDevInfDataStoreParseRxTx(parser, SML_ELEMENT_RXPREF, datastore, error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_RX)) {
			if (!_smlXmlDevInfDataStoreParseRxTx(parser, SML_ELEMENT_RX, datastore, error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TXPREF)) {
			if (!_smlXmlDevInfDataStoreParseRxTx(parser, SML_ELEMENT_TXPREF, datastore, error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TX)) {
			if (!_smlXmlDevInfDataStoreParseRxTx(parser, SML_ELEMENT_TX, datastore, error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CTCAP)) {
			if (!_smlXmlDevInfDataStoreParseCTCap(parser, devinf, error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SYNCCAP)) {
			if (!_smlXmlDevInfDataStoreParseSyncCap(parser, datastore, error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DSMEM)) { 
			if (!_smlXmlDevInfDataStoreParseDSMem(parser, datastore, error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SUPPORT_HIERARCHICAL_SYNC)) {
			if (sml_dev_inf_get_ver_dtd(devinf) < SML_DEVINF_VERSION_12) {
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "SupportHierarchicalSync is only supported in OMA DS 1.2 DevInf and later.");
				goto error;
			}
			sml_dev_inf_data_store_set_support_hierarchical_sync(datastore, TRUE);
			if (!xmlTextReaderIsEmptyElement(parser->reader)) {
				if (!_smlXmlParserStep(parser)) {
					g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "The closing element of %s is missing.", SML_ELEMENT_SUPPORT_HIERARCHICAL_SYNC);
					goto error;
				}
			}
		} else {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "A DataStore within DevInf includes the unsupported element %s.", (char *)xmlTextReaderConstName(parser->reader));
			goto error;
		}
		
		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error;
		}
	}
	
	if (!sml_dev_inf_add_data_store(devinf, datastore, error))
		goto error;
	g_object_unref(datastore);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
error:
	if (datastore)
		g_object_unref(datastore);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}

SmlDevInf*
smlXmlDevInfParse (const gchar *data,
                   gsize size,
                   GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, data, size, error);
	CHECK_ERROR_REF
	smlAssert(data);
	smlAssert(size);
	SmlDevInf *devinf = NULL;
	
	//Fixme debug if
	char *debugstr = smlPrintBinary(data, size);
	smlTrace(TRACE_INTERNAL, "Xml devinf input: %s", debugstr);
	smlSafeCFree(&debugstr);
	
	SmlXmlParser *parser = smlTryMalloc0(sizeof(SmlXmlParser), error);
	if (!parser)
		goto error;
	
	/* Create the new parser */
	parser->reader = xmlReaderForMemory(data, size, "/", NULL, XML_PARSE_NONET | XML_PARSE_NOERROR | XML_PARSE_NOWARNING | XML_PARSE_NOCDATA | XML_PARSER_SUBST_ENTITIES);
	if (!parser->reader) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unable to create new reader");
		goto error;
	}
	xmlSubstituteEntitiesDefault(1);
	
	/* Check the First Node (DevInf)*/
	if (!_smlXmlParserExpectNode(parser, XML_NODE_START, FALSE, SML_ELEMENT_DEVINF, error))
		goto error;
	
	devinf = sml_dev_inf_new();
	if (!devinf) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Cannot create new instance of SmlDevInf - out of memory.");
		goto error;
	}

	if (!_smlXmlParserStep(parser)) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
		goto error;
	}
		
	while (1) {
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DEVINF) && \
		xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
				"The element %s is not a start node in DevInf.",
				xmlTextReaderConstName(parser->reader));
			goto error;
		}
		
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_VERDTD)) {
			char *verdtd = NULL;
			if (!_smlXmlParserGetString(parser, &verdtd, SML_ELEMENT_VERDTD, error))
				goto error;
			
			if (verdtd && !strcmp(verdtd, "1.1"))
				sml_dev_inf_set_ver_dtd(devinf, SML_DEVINF_VERSION_11);
			else if (verdtd && !strcmp(verdtd, "1.0"))
				sml_dev_inf_set_ver_dtd(devinf, SML_DEVINF_VERSION_10);
			else if (verdtd && !strcmp(verdtd, "1.2"))
				sml_dev_inf_set_ver_dtd(devinf, SML_DEVINF_VERSION_12);
			else {
				smlSafeCFree(&verdtd);
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unknown devinf version");
				goto error;
			}
			smlSafeCFree(&verdtd);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MAN)) {
			char *man = NULL;
			if (!_smlXmlParserGetString(parser, &man, SML_ELEMENT_MAN, error))
				goto error;
			if (!sml_dev_inf_set_man(devinf, man, error))
				goto error;
			smlSafeCFree(&man);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MOD)) {
			char *mod = NULL;
			if (!_smlXmlParserGetString(parser, &mod, SML_ELEMENT_MOD, error))
				goto error;
			if (!sml_dev_inf_set_mod(devinf, mod, error))
				goto error;
			smlSafeCFree(&mod);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_OEM)) {
			char *oem = NULL;
			if (!_smlXmlParserGetString(parser, &oem, SML_ELEMENT_OEM, error))
				goto error;
			if (!sml_dev_inf_set_oem(devinf, oem, error))
				goto error;
			smlSafeCFree(&oem);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_FWV)) {
			char *fwv = NULL;
			if (!_smlXmlParserGetString(parser, &fwv, SML_ELEMENT_FWV, error))
				goto error;
			if (!sml_dev_inf_set_fwv(devinf, fwv, error))
				goto error;
			smlSafeCFree(&fwv);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SWV)) {
			char *swv = NULL;
			if (!_smlXmlParserGetString(parser, &swv, SML_ELEMENT_SWV, error))
				goto error;
			if (!sml_dev_inf_set_swv(devinf, swv, error))
				goto error;
			smlSafeCFree(&swv);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_HWV)) {
			char *hwv = NULL;
			if (!_smlXmlParserGetString(parser, &hwv, SML_ELEMENT_HWV, error))
				goto error;
			if (!sml_dev_inf_set_hwv(devinf, hwv, error))
				goto error;
			smlSafeCFree(&hwv);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DEVID)) {
			char *devid = NULL;
			if (!_smlXmlParserGetString(parser, &devid, SML_ELEMENT_DEVID, error))
				goto error;
			if (!sml_dev_inf_set_dev_id(devinf, devid, error))
				goto error;
			smlSafeCFree(&devid);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DEVTYP)) {
			char *devtype = NULL;
			if (!_smlXmlParserGetString(parser, &devtype, SML_ELEMENT_DEVTYP, error))
				goto error;
			
			sml_dev_inf_set_dev_typ(devinf, _smlParseDevInfDevType(devtype, error));
			smlSafeCFree(&devtype);
			
			if (sml_dev_inf_get_dev_typ(devinf) == SML_DEVINF_DEVTYP_UNKNOWN)
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_UTC)) {
			if (sml_dev_inf_get_ver_dtd(devinf) == SML_DEVINF_VERSION_10) {
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Devinf 1.0 does not allow UTC");
				goto error;
			}
			
			sml_dev_inf_set_support_utc(devinf, TRUE);
			if (!_smlXmlParserStep(parser)) {
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
				goto error;
			}
			
			if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_UTC) && \
				xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
				if (!_smlXmlParserStep(parser)) {
					g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes2");
					goto error;
				}
			}
			continue;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SUPPORTLARGEOBJS)) {
			if (sml_dev_inf_get_ver_dtd(devinf) == SML_DEVINF_VERSION_10) {
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Devinf 1.0 does not allow large objects");
				goto error;
			}
			
			sml_dev_inf_set_support_large_objs(devinf, TRUE);
			if (!_smlXmlParserStep(parser)) {
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
				goto error;
			}
			
			if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SUPPORTLARGEOBJS) && \
				xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
				if (!_smlXmlParserStep(parser)) {
					g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes2");
					goto error;
				}
			}
			continue;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SUPPORTNUMBEROFCHANGES)) {
			if (sml_dev_inf_get_ver_dtd(devinf) == SML_DEVINF_VERSION_10) {
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Devinf 1.0 does not allow number of changes");
				goto error;
			}
			
			sml_dev_inf_set_support_number_of_changes(devinf, TRUE);
			if (!_smlXmlParserStep(parser)) {
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
				goto error;
			}
			
			if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SUPPORTNUMBEROFCHANGES) && \
				xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
				if (!_smlXmlParserStep(parser)) {
					g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes2");
					goto error;
				}
			}
			continue;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DATASTORE)) {
			if (!_smlXmlDevInfDataStoreParse(parser, devinf, error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CTCAP)) {
			/* This must be SyncML 1.0 or 1.1.
			 * OMA DM 1.2 puts CTCap into the datastores.
			 */
			if (!_smlXmlDevInfDataStoreParseCTCap(parser, devinf, error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_EXT)) {
			/* Ignored for now */
			smlTrace(TRACE_INTERNAL, "%s: Skipping ext node");
			if (!_smlXmlSkipNode(parser)) {
				g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unable to skip ext node");
				goto error;
			}
			continue;
		} else {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "wrong initial node: %s", (char *)xmlTextReaderConstName(parser->reader));
			goto error;
		}
		
		if (!_smlXmlParserStep(parser)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing nodes");
			goto error;
		}
	}
		
	xmlFreeTextReader(parser->reader);
	parser->reader = NULL;
	smlSafeFree((gpointer *)&parser);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return devinf;
error:
	if (parser && parser->reader) {
		xmlFreeTextReader(parser->reader);
		parser->reader = NULL;
	}
	if (parser)
		smlSafeFree((gpointer *)&parser);
	if (devinf)
		g_object_unref(devinf);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return NULL;
}

