/*
 * 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/syncml.h>

#include <libsyncml/syncml_internals.h>
#include <libsyncml/sml_elements_internals.h>
#include <libsyncml/sml_command_internals.h>
#include <libsyncml/sml_devinf_internals.h>
#include <libsyncml/sml_session_internals.h>
#include "libsyncml/sml_error_internals.h"

#include "sml_xml_parse.h"
#include "sml_xml_parse_internals.h"

#define BUFFER_SIZE 500

static SmlBool _smlXmlParserStep(SmlXmlParser *parser)
{
	SmlBool 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 SmlBool _smlXmlParserStepData(SmlXmlParser *parser)
{
	SmlBool 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 SmlBool _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 SmlBool _smlXmlParserExpectNode(SmlXmlParser *parser, int type, SmlBool empty, const char *name, SmlError **error)
{
	CHECK_ERROR_REF
	if (!_smlXmlParserStep(parser)) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No node at all");
		return FALSE;
	}
	
	if (xmlTextReaderNodeType(parser->reader) != type) {
		smlErrorSet(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)) {
					smlErrorSet(error, SML_ERROR_GENERIC, "no name");
					return FALSE;
				}
			
				if (strcmp((char *)xmlTextReaderConstName(parser->reader), name)) {
					smlErrorSet(error, SML_ERROR_GENERIC, "Wrong name");
					return FALSE;
				}
			}
			break;
		case XML_NODE_TEXT:
			if (!empty && !xmlTextReaderConstName(parser->reader)) {
				smlErrorSet(error, SML_ERROR_GENERIC, "Empty.");
				return FALSE;
			}
			break;
		default:
			smlErrorSet(error, SML_ERROR_GENERIC, "Unknown node type");
			return FALSE;
	}
	
	return TRUE;
}

static SmlBool _smlXmlParserGetID(SmlXmlParser *parser, unsigned int *id, const char *name, SmlError **error)
{
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(id);
	
	if (*id) {
		smlErrorSet(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 SmlBool _smlXmlParserGetString(SmlXmlParser *parser, char **string, const char *name, SmlError **error)
{
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(string);
	
	if (*string) {
		smlErrorSet(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)) {
		smlErrorSet(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 {
		smlErrorSet(error, SML_ERROR_GENERIC, "wrong node type");
		goto error_clear;
	}
	
	return TRUE;

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

static SmlBool _smlXmlParserGetData(SmlXmlParser *parser, char **string, unsigned int *size, const char *name, SmlError **error)
{
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(string);
	int rc = 0;
	
	if (*string) {
		smlErrorSet(error, SML_ERROR_GENERIC, "string already set");
		return FALSE;
	}
	
	
	xmlBuffer *buffer = xmlBufferCreateSize(BUFFER_SIZE);
	if (!buffer) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new buffer");
		goto error;
	}
	
	xmlTextWriter *writer = xmlNewTextWriterMemory(buffer, 0);
	if (!writer) {
		smlErrorSet(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)) {
			smlErrorSet(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) {
					smlErrorSet(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) {
					smlErrorSet(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) {
					smlErrorSet(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) {
					smlErrorSet(error, SML_ERROR_GENERIC, "Unable to add string");
					goto error_free_writer;
				}
				break;
			default:
				smlErrorSet(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 SmlBool _smlSyncHeaderParseDTD(SmlProtocolVersion *version, SmlXmlParser *parser, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, version, parser, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(version);
	
	if (*version) {
		smlErrorSet(error, SML_ERROR_GENERIC, "dtd already set");
		smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
		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);
		smlErrorSet(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__, smlErrorPrint(error));
	return FALSE;
}

static SmlBool _smlSyncHeaderParseProto(SmlProtocolType *type, SmlXmlParser *parser, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, type, parser, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(type);
	
	if (*type) {
		smlErrorSet(error, SML_ERROR_GENERIC, "protocol already set");
		smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
		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);
		smlErrorSet(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__, smlErrorPrint(error));
	return FALSE;
}


static SmlBool _smlLocationParse(SmlLocation **location, SmlXmlParser *parser, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, location, parser, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(location);
	
	if (*location) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Location already set");
		smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
		return FALSE;
	}
	
	*location = smlTryMalloc0(sizeof(SmlLocation), error);
	if (!location)
		goto error;
	(*location)->refCount = 1;
	
	while (1) {
		if (!_smlXmlParserStep(parser)) {
			smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
			goto error_free_location;
		}
		
		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) {
			smlErrorSet(error, SML_ERROR_GENERIC,
				"The element %s is not a start node in locations (Target, Source, SourceParent or TargetParent).",
				xmlTextReaderConstName(parser->reader));
			goto error_free_location;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_LOCURI)) {
			if (!_smlXmlParserGetString(parser, &((*location)->locURI), SML_ELEMENT_LOCURI, error))
				goto error_free_location;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_LOCNAME)) {
			if (!_smlXmlParserGetString(parser, &((*location)->locName), SML_ELEMENT_LOCNAME, error))
				goto error_free_location;
		} else {
			smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node. expected SyncHdr");
			goto error_free_location;
		}
	}
	
	if (!(*location)->locURI) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No locURI set");
		goto error_free_location;
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error_free_location:
	smlLocationUnref(*location);
error:
	*location = NULL;
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

static SmlBool _smlAnchorParse(SmlAnchor **anchor, SmlXmlParser *parser, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, anchor, parser, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(anchor);
	
	if (*anchor) {
		smlErrorSet(error, SML_ERROR_GENERIC, "anchor already set");
		smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
		return FALSE;
	}
	
	*anchor = smlTryMalloc0(sizeof(SmlAnchor), error);
	if (!anchor)
		goto error;
	
	while (1) {
		if (!_smlXmlParserStep(parser)) {
			smlErrorSet(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) {
			smlErrorSet(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 {
			smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node");
			goto error_free_anchor;
		}
	}
	
	if (!(*anchor)->next) {
		smlErrorSet(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__, smlErrorPrint(error));
	return FALSE;
}

static SmlBool _smlChalMetaParse(SmlXmlParser *parser, char **format, char **type, char **nonce, SmlError **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)) {
			smlErrorSet(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) {
			smlErrorSet(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 {
			smlErrorSet(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__, smlErrorPrint(error));
	return FALSE;
}

static SmlBool _smlCommandMetaParse(SmlXmlParser *parser, char **format, char **type, SmlAnchor **anchor, unsigned int *size, int *maxobjsize, SmlError **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 = -1;

	if (xmlTextReaderIsEmptyElement(parser->reader))
	{
		/* empty meta element <meta/> */
		smlTrace(TRACE_EXIT, "%s - empty meta element", __func__);
		return TRUE;
	}
	
	while (1) {
		if (!_smlXmlParserStep(parser)) {
			smlErrorSet(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)) {
				smlErrorSet(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) {
			smlErrorSet(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)) {
			if (!_smlXmlParserGetID(parser, size, SML_ELEMENT_SIZE, error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MAXOBJSIZE)) {
			/* MaxObjSize must be parsed even if there is no maxobjsize pointer */
			unsigned int loc_maxobjsize = 0;
			if (!_smlXmlParserGetID(parser, &loc_maxobjsize, SML_ELEMENT_MAXOBJSIZE, error))
				goto error;
			if (maxobjsize)
				*maxobjsize = loc_maxobjsize;
		} 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 {
			smlErrorSet(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__, smlErrorPrint(error));
	return FALSE;
}

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

	if (parser->gotMoreData) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Last item already had more data set");
		goto error;
	}
	
	SmlItem *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)) {
		smlErrorSet(error, SML_ERROR_GENERIC, "The first element inside of an Item structure cannot be parsed.");
		goto error_free_item;
	}

	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) {
			smlErrorSet(error, SML_ERROR_GENERIC,
				"The element %s is not a start node in Item.",
				xmlTextReaderConstName(parser->reader));
			goto error_free_item;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCE)) {
			SmlLocation *source = NULL;
			if (!_smlLocationParse(&source, parser, error))
				goto error_free_item;
			
			smlItemSetSource(item, source);
			smlLocationUnref(source);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TARGET)) {
			SmlLocation *target = NULL;
			if (!_smlLocationParse(&target, parser, error))
				goto error_free_item;
			
			smlItemSetTarget(item, target);
			smlLocationUnref(target);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCE_PARENT)) {
			SmlLocation *source = NULL;
			if (!_smlLocationParse(&source, parser, error))
				goto error_free_item;
			
			smlItemSetSourceParent(item, source);
			smlLocationUnref(source);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TARGET_PARENT)) {
			SmlLocation *target = NULL;
			if (!_smlLocationParse(&target, parser, error))
				goto error_free_item;
			
			smlItemSetTargetParent(item, target);
			smlLocationUnref(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_free_item;
					} 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_free_item;
					}
					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_free_item;
					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_free_item;
						
					if (!item->contenttype)
						goto error_free_item;
					break;
				default:
					smlErrorSet(error, SML_ERROR_GENERIC, "Unknown command type");
					goto error_free_item;
			}
		} 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)) {
							smlErrorSet(error, SML_ERROR_GENERIC, "The next element after the starting Data element in an Item is missing.");
							goto error_free_item;
						}

						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_free_item;
							anchor->next = g_strdup((char *)xmlTextReaderConstValue(parser->reader));
						} else {
							/* normal behaviour with anchor copy */
							if (!_smlAnchorParse(&anchor, parser, error))
								goto error_free_item;
						}
						item->anchor = anchor;

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

error_free_item:
	smlItemUnref(item);
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return NULL;
}

static SmlCred *_smlCredParse(SmlXmlParser *parser, SmlError **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)) {
			smlErrorSet(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) {
			smlErrorSet(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 {
			smlErrorSet(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 (!smlErrorIsSet(error))
	{
		smlTrace(TRACE_EXIT, "%s: %p", __func__, cred);
		return cred;
	} else {
		if (cred)
			smlCredUnref(cred);
		smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
		return NULL;
	}
}

static SmlChal *_smlChalParse(SmlXmlParser *parser, SmlError **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)) {
			smlErrorSet(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) {
			smlErrorSet(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 {
			smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node %s", xmlTextReaderConstName(parser->reader));
			goto error;
		}
	}
		
	if (format && strcmp(format, SML_BASE64)) {
		smlErrorSet(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 {
		smlErrorSet(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__, smlErrorPrint(error));
	return NULL;
}

static SmlBool _smlChangeParse(SmlXmlParser *parser, SmlCommand **cmd, SmlCommandType type, const char *name, SmlError **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;
	
	*cmd = smlCommandNew(type, error);
	if (!*cmd)
		goto error;	
	(*cmd)->refCount = 1;
	
	while (1) {
		if (!_smlXmlParserStep(parser)) {
			smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
			goto error_free_cmd;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), name) && \
		xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
			smlErrorSet(error, SML_ERROR_GENERIC,
				"The element %s is not a start node in %s.",
				xmlTextReaderConstName(parser->reader), name);
			goto error_free_cmd;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMDID)) {
			if (!_smlXmlParserGetID(parser, &((*cmd)->cmdID), SML_ELEMENT_CMDID, error))
				goto error_free_cmd;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_ITEM)) {
			SmlItem *item = _smlItemParse(parser, *cmd, type, error);
			if (!item)
				goto error_free_cmd;
			(*cmd)->private.change.items = g_list_append((*cmd)->private.change.items, item);
			if (!(*cmd)->private.change.items)
			{
				smlItemUnref(item);
				smlErrorSet(error, SML_ERROR_GENERIC, "g_list_append for item list of change command failed.");
				goto error_free_cmd;
			}
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_META)) {
			if (!_smlCommandMetaParse(parser, NULL, &contenttype, NULL, &(*cmd)->size, NULL, error))
				goto error_free_cmd;
		} else {
			smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node");
			goto error_free_cmd;
		}
	}
	
	if (!(*cmd)->cmdID) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No cmdid set");
		goto error_free_cmd;
	}
	
	if (!(*cmd)->private.change.items ||
	    !g_list_length((*cmd)->private.change.items)) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No items set");
		goto error_free_cmd;
	}
	
	/* 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++)
	{
		SmlItem *item = g_list_nth_data((*cmd)->private.change.items, i);
		if (item->contenttype == NULL && contenttype) {
			item->contenttype = g_strdup(contenttype);
		}
	}
	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;
	}
	
	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:
			smlErrorSet(error, SML_ERROR_GENERIC, "Unknown change type set");
			goto error_free_cmd;
	}

	if (!(*cmd)->source)
	{
		SmlItem *item = g_list_nth_data((*cmd)->private.change.items, 0);
		if (item->source) {
			(*cmd)->source = smlLocationClone(item->source, error);
			if (!(*cmd)->source)
				goto error_free_cmd;
		}
	}

	if (!(*cmd)->target)
	{
		SmlItem *item = g_list_nth_data((*cmd)->private.change.items, 0);
		if (item->target) {
			(*cmd)->target = smlLocationClone(item->target, error);
			if (!(*cmd)->target)
				goto error_free_cmd;
		}
	}
	
	/* Step once more */
	if (!_smlXmlParserStep(parser)) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
		goto error_free_cmd;
	}
	
	smlTrace(TRACE_EXIT, "%s: %p", __func__, *cmd);
	return TRUE;

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

static SmlBool _smlMessageParseSynchdrMeta(SmlXmlParser *parser, unsigned int *maxmsgsize, unsigned int *maxobjsize, char **emi, SmlError **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)) {
			smlErrorSet(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) {
			smlErrorSet(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)) {
			if (!_smlXmlParserGetID(parser, maxmsgsize, SML_ELEMENT_MAXMSGSIZE, error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MAXOBJSIZE)) {
			if (!_smlXmlParserGetID(parser, maxobjsize, SML_ELEMENT_MAXOBJSIZE, error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_EMI)) {
			if (!_smlXmlParserGetString(parser, emi, SML_ELEMENT_EMI, error))
				goto error;
		} else {
			smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node. expected MaxMsgSize, MaxObjSize or EMI. Instead of that: %s", xmlTextReaderConstName(parser->reader));
			goto error;
		}
	}
	
	if (!(*maxmsgsize) && !(*maxobjsize)) {
		smlErrorSet(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__, smlErrorPrint(error));
	return FALSE;
}

static SmlBool _smlAlertParse(SmlXmlParser *parser, SmlCommand **cmd, SmlError **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 = -1;
		
	while (1) {
		if (!_smlXmlParserStep(parser)) {
			smlErrorSet(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) {
			smlErrorSet(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)) {
			if (!_smlXmlParserGetID(parser, &((*cmd)->cmdID), SML_ELEMENT_CMDID, error))
				goto error_free_cmd;
		} 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)) {
			unsigned int 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 {
			smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node %s", xmlTextReaderConstName(parser->reader));
			goto error_free_cmd;
		}
	}
	
	if (!(*cmd)->private.alert.type) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No alert type set");
		goto error_free_cmd;
	}
	
	/* Step once more */
	if (!_smlXmlParserStep(parser)) {
		smlErrorSet(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__, smlErrorPrint(error));
	return FALSE;
}

static SmlParserResult _smlCommandSyncParse(SmlXmlParser *parser, SmlCommand **cmd, SmlError **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 = -1;
	
	while (1) {
		if (!_smlXmlParserStep(parser)) {
			smlErrorSet(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) {
			smlErrorSet(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)) {
			if (!_smlXmlParserGetID(parser, &((*cmd)->cmdID), SML_ELEMENT_CMDID, error))
				goto error_free_cmd;
		} 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)) {
			(*cmd)->private.sync.hasNumChanged = TRUE;
			if (!_smlXmlParserGetID(parser, &((*cmd)->private.sync.numChanged), SML_ELEMENT_NUMBEROFCHANGES, error))
				goto error_free_cmd;
		} else {
			smlErrorSet(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__, smlErrorPrint(error));
	return FALSE;
}

static SmlMapItem *_smlMapItemParse(SmlXmlParser *parser, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, parser, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	
	SmlMapItem *item = smlTryMalloc0(sizeof(SmlMapItem), error);
	if (!item)
		goto error;
	item->refCount = 1;
	
	while (1) {
		if (!_smlXmlParserStep(parser)) {
			smlErrorSet(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) {
			smlErrorSet(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)) {
			if (!_smlLocationParse(&item->source, parser, error))
				goto error_free_item;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TARGET)) {
			if (!_smlLocationParse(&item->target, parser, error))
				goto error_free_item;
		} else {
			smlErrorSet(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:
	smlMapItemUnref(item);
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return NULL;
}

static SmlBool _smlCommandMapParse(SmlXmlParser *parser, SmlCommand **cmd, SmlError **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)) {
			smlErrorSet(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) {
			smlErrorSet(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)) {
			if (!_smlXmlParserGetID(parser, &((*cmd)->cmdID), SML_ELEMENT_CMDID, error))
				goto error_free_cmd;
		} 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 {
			smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node");
			goto error_free_cmd;
		}
	}
	
	/* Step once more */
	if (!_smlXmlParserStep(parser)) {
		smlErrorSet(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__, smlErrorPrint(error));
	return FALSE;
}

static SmlBool _smlCommandAccessParse(SmlXmlParser *parser, SmlCommand **cmd, SmlCommandType type, SmlError **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)) {
			smlErrorSet(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) {
			smlErrorSet(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)) {
			if (!_smlXmlParserGetID(parser, &((*cmd)->cmdID), SML_ELEMENT_CMDID, 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 {
			smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node");
			goto error_free_cmd;
		}
	}
	
	if (!(*cmd)->private.access.item) {
		smlErrorSet(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)) {
		smlErrorSet(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__, smlErrorPrint(error));
	return FALSE;
}

static SmlBool _smlResultsParse(SmlXmlParser *parser, SmlCommand **cmd, SmlError **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)) {
			smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
			goto error_free_cmd;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_RESULTS) && \
		xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
			smlErrorSet(error, SML_ERROR_GENERIC,
				"The element %s is not a start node in Results.",
				xmlTextReaderConstName(parser->reader));
			goto error_free_cmd;
		}
		
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMDID)) {
			if (!_smlXmlParserGetID(parser, &((*cmd)->cmdID), SML_ELEMENT_CMDID, error))
				goto error_free_cmd;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MSGREF)) {
			if (!_smlXmlParserGetID(parser, &((*cmd)->private.results.status->msgRef), SML_ELEMENT_MSGREF, error))
				goto error_free_cmd;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMDREF)) {
			if (!_smlXmlParserGetID(parser, &((*cmd)->private.results.status->cmdRef), SML_ELEMENT_CMDREF, error))
				goto error_free_cmd;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCEREF)) {
			if (!_smlXmlParserGetString(parser, &locURI, SML_ELEMENT_SOURCEREF, error))
				goto error_free_cmd;
			
			(*cmd)->private.results.status->sourceRef = smlLocationNew(locURI, NULL, error);
			smlSafeCFree(&locURI);
			if (!(*cmd)->private.results.status->sourceRef)
				goto error_free_cmd;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TARGETREF)) {
			if (!_smlXmlParserGetString(parser, &locURI, SML_ELEMENT_TARGETREF, error))
				goto error_free_cmd;
				
			(*cmd)->private.results.status->targetRef = smlLocationNew(locURI, NULL, error);
			smlSafeCFree(&locURI);
			if (!(*cmd)->private.results.status->targetRef)
				goto error_free_cmd;
		} 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_free_cmd;
			
			(*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_free_cmd;
			if (format) smlSafeCFree(&format);
		} else {
			smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node");
			goto error_free_cmd;
		}
	}
	
	if (!(*cmd)->private.results.status->item) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Result 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.results.status->item->contenttype) {
		if (!contenttype) {
			smlErrorSet(error, SML_ERROR_GENERIC, "Result is missing content type");
			goto error_free_cmd;
		}
		
		(*cmd)->private.results.status->item->contenttype = g_strdup(contenttype);
	}
	
	if (contenttype)
		smlSafeCFree(&contenttype);
	
	/* Step once more */
	if (!_smlXmlParserStep(parser)) {
		smlErrorSet(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__, smlErrorPrint(error));
	return FALSE;
}

static SmlBool _smlXmlParserFixBrokenItemData(
		const char *data, unsigned int size,
		char **fixed_data, unsigned int *fixed_size,
		SmlError **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__, smlErrorPrint(error));
		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);
		GError *gerror = NULL;
		size_t read_bytes = 0;
		size_t written_bytes = 0;
		gchar *conv_string = g_convert(
					position, (last_utf16 - position + 1),
					"UTF-8", "UTF-16",
					&read_bytes, &written_bytes,
					&gerror);
		if (gerror != NULL)
		{
			smlErrorSet(
				error, SML_ERROR_GENERIC,
				"Character conversion from UTF-16 to UTF-8 failed. %s",
				gerror->message);
			g_error_free(gerror);
			smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
			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__, smlErrorPrint(error));
			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__, smlErrorPrint(error));
			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;
}

/** @brief Start the parsing
 * 
 * This will set everything up and parse until the SyncHdr
 * 
 */
SmlBool smlXmlParserStart(SmlXmlParser *parser, const char *data, unsigned int size, SmlError **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__, smlErrorPrint(error));
		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) {
		smlErrorSet(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__, smlErrorPrint(error));
	return FALSE;
}

SmlBool smlXmlParserEnd(SmlXmlParser *parser, SmlBool *final, SmlBool *end, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, parser, final, end, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	SmlBool got_final = FALSE;
	
	/* Check for final */
	if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_FINAL)) {
		got_final = TRUE;
		if (!_smlXmlParserStep(parser)) {
			smlErrorSet(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)) {
				smlErrorSet(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
	    )
	   ) {
		smlErrorSet(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__, smlErrorPrint(error));
	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__);
}	

SmlBool smlXmlParserGetHeader(SmlXmlParser *parser, SmlHeader **header, SmlCred **cred, SmlError **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. */
		smlErrorSet(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)) {
			smlErrorSet(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) {
			smlErrorSet(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)) {
			if (!_smlXmlParserGetString(parser, &((*header)->sessionID), SML_ELEMENT_SESSIONID, error))
				goto error_free_header;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MSGID)) {
			if (!_smlXmlParserGetID(parser, &((*header)->messageID), SML_ELEMENT_MSGID, error))
				goto error_free_header;
		} 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 {
			smlErrorSet(error, SML_ERROR_GENERIC, "Element %s not supported for SyncHdr.",
				(char *)xmlTextReaderConstName(parser->reader));
			goto error_free_header;
		}
	}
	
	if (!(*header)->protocol) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No protocol set");
		goto error_free_header;
	}
	
	if (!(*header)->version) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No dtd version set");
		goto error_free_header;
	}
	
	if (!(*header)->sessionID) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No sessionID set");
		goto error_free_header;
	}
	
	if (!(*header)->target) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No target set");
		goto error_free_header;
	}
	
	if (!(*header)->source) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No source set");
		goto error_free_header;
	}
	
	if (!(*header)->messageID) {
		smlErrorSet(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)) {
		smlErrorSet(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__, smlErrorPrint(error));
	return FALSE;
}

SmlBool smlXmlParserGetStatus(SmlXmlParser *parser, SmlStatus **status, SmlError **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 {
		smlErrorDeref(error);
	}
	
	/* 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)) {
		smlErrorSet(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);
		smlErrorSet(error, SML_ERROR_GENERIC, "Empty status element");
		goto error;
	}
	
	if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
		smlErrorSet(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)) {
			smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
			goto error_free_status;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_STATUS) && \
			xmlTextReaderNodeType(parser->reader) == XML_NODE_CLOSE) {
			break;
		} else if (xmlTextReaderNodeType(parser->reader) != XML_NODE_START) {
			smlErrorSet(error, SML_ERROR_GENERIC,
				"The element %s is not a start node in Status.",
				xmlTextReaderConstName(parser->reader));
			goto error_free_status;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMDID)) {
			//Errr... who cares?
			if (!_smlXmlParserExpectNode(parser, XML_NODE_TEXT, FALSE, NULL, error))
				goto error_free_status;
			if (!_smlXmlParserExpectNode(parser, XML_NODE_CLOSE, FALSE, SML_ELEMENT_CMDID, error))
				goto error_free_status;
		} 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.
			*/
			if (!_smlXmlParserGetID(parser, &((*status)->msgRef), SML_ELEMENT_MSGREF, error))
				goto error_free_status;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMDREF)) {
			if (!_smlXmlParserGetID(parser, &((*status)->cmdRef), SML_ELEMENT_CMDREF, error))
				goto error_free_status;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CMD)) {
			char *cmdname = NULL;
			if (!_smlXmlParserGetString(parser, &cmdname, SML_ELEMENT_CMD, error))
				goto error_free_status;
			(*status)->type = smlCommandTypeFromString(cmdname, error);
			smlSafeCFree(&cmdname);
			if ((*status)->type == SML_COMMAND_TYPE_UNKNOWN)
				goto error_free_status;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DATA)) {
			if (!_smlXmlParserGetString(parser, &((*status)->data), SML_ELEMENT_DATA, error))
				goto error_free_status;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCEREF)) {
			if (!_smlXmlParserGetString(parser, &locURI, SML_ELEMENT_SOURCEREF, error))
				goto error_free_status;
			
			(*status)->sourceRef = smlLocationNew(locURI, NULL, error);
			smlSafeCFree(&locURI);
			if (!(*status)->sourceRef)
				goto error_free_status;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TARGETREF)) {
			if (!_smlXmlParserGetString(parser, &locURI, SML_ELEMENT_TARGETREF, error))
				goto error_free_status;
				
			(*status)->targetRef = smlLocationNew(locURI, NULL, error);
			smlSafeCFree(&locURI);
			if (!(*status)->targetRef)
				goto error_free_status;
		} 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_free_status;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CHAL)) {
			(*status)->chal = _smlChalParse(parser, error);
			if (!(*status)->chal)
				goto error_free_status;
		} else {
			smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node");
			goto error;
		}
	}
	
	/* Step once more */
	if (!_smlXmlParserStep(parser)) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
		goto error_free_status;
	}

	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 {
			smlErrorSet(error, SML_ERROR_GENERIC, "Got wrong item");
			smlItemUnref(item);
			goto error_free_status;
		}
		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) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No msgref set");
		goto error_free_status;
	}
	*/
	
	if ((*status)->type == SML_COMMAND_TYPE_UNKNOWN) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No cmd set");
		goto error_free_status;
	}
	
	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_free_status:
	smlStatusUnref(*status);
error:
	*status = NULL;
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

SmlParserResult smlXmlParserGetCommand(SmlXmlParser *parser, SmlCommand **cmd, SmlError **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)) {
					smlErrorSet(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:
			smlErrorSet(error, SML_ERROR_GENERIC, "Unsupported command type");
			goto error;
	}
	
	if (!(*cmd)->cmdID) {
		smlErrorSet(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__, smlErrorPrint(error));
	return SML_PARSER_RESULT_ERROR;
}

SmlXmlParser *smlXmlParserNew(SmlParserFunctions *functions, SmlError **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__, smlErrorPrint(error));
	return NULL;
}

static SmlBool _smlXmlDevInfDataStoreParseDSMem(SmlXmlParser *parser, SmlDevInfDataStore *datastore, SmlError **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)) {
			smlErrorSet(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) {
			smlErrorSet(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)) {
			if (!_smlXmlParserGetID(parser, &(datastore->maxid), SML_ELEMENT_MAXID, error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SHAREDMEM)) {	
			datastore->sharedMem = TRUE;
			if (!_smlXmlParserStep(parser)) {
				smlErrorSet(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)) {
					smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes2");
					goto error;
				}
			}
			continue;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MAXMEM)) {
			if (!_smlXmlParserGetID(parser, &(datastore->maxmem), SML_ELEMENT_MAXMEM, error))
				goto error;	
		} else {
			smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node");
			goto error;
		}
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
	
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

static SmlBool _smlXmlDevInfDataStoreParseSyncCap(SmlXmlParser *parser, SmlDevInfDataStore *datastore, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, datastore, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(datastore);
		
	while (1) {
		if (!_smlXmlParserStep(parser)) {
			smlErrorSet(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) {
			smlErrorSet(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)) {
			unsigned int id = 0;
			if (!_smlXmlParserGetID(parser, &id, SML_ELEMENT_SYNCTYPE, error))
				goto error;
			id = 1 << (id - 1);
			SmlDevInfSyncCap synccap = smlDevInfSyncCapConvert(id, error);
			if (synccap == SML_DEVINF_SYNCTYPE_UNKNOWN &&
			    error != NULL)
				goto error;
			
			if (synccap != SML_DEVINF_SYNCTYPE_UNKNOWN)
				smlDevInfDataStoreSetSyncCap(datastore, synccap, TRUE);
		} else {
			smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node");
			goto error;
		}
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
	
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

static SmlBool _smlXmlDevInfDataStoreParseCTCap11(SmlXmlParser *parser, SmlDevInf *devinf, SmlError **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           */

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

	SmlDevInfCTCap *ctcap = NULL;
	SmlDevInfProperty *property = NULL;
	SmlDevInfPropParam *param = NULL;
	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)
		{
			smlErrorSet(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 = smlDevInfCTCapTypeFromString(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)
                                        	{
                                                	smlDevInfPropertyAddPropParam(property, param);
                                                	param = NULL;
                                        	}
                                        	smlDevInfCTCapAddProperty(ctcap, property);
						property = NULL;
					}
					smlDevInfAppendCTCap(devinf, ctcap);
					ctcap = NULL;
				}
				ctcap = smlDevInfNewCTCap(error);
				if (!ctcap)
					goto error;
				smlDevInfCTCapSetCTType(ctcap, value);
				break;
			case SML_DEVINF_CTCAP_VERCT:
				if (ctcap == NULL)
					goto error;
				smlDevInfCTCapSetVerCT(ctcap, value);
				break;
			case SML_DEVINF_CTCAP_PROPNAME:
				// PropName => new property
				if (ctcap == NULL)
					goto error;
				if (property != NULL)
                               	{
                                       	if (param != NULL)
                                       	{
                                               	smlDevInfPropertyAddPropParam(property, param);
                                               	param = NULL;
                                       	}
                                       	smlDevInfCTCapAddProperty(ctcap, property);
					property = NULL;
				}
				property = smlDevInfNewProperty(error);
				if (!property)
					goto error;
				smlDevInfPropertySetPropName(property, value);
				break;
			case SML_DEVINF_CTCAP_DATATYPE:
				if (property == NULL)
					goto error;
				if (param != NULL)
					smlDevInfPropParamSetDataType(param, value);
				else
					smlDevInfPropertySetDataType(property, value);
				break;
			case SML_DEVINF_CTCAP_MAXOCCUR:
				if (property == NULL)
					goto error;
				smlDevInfPropertySetMaxOccur(property, atoi(value));
				break;
			case SML_DEVINF_CTCAP_MAXSIZE:
				if (property == NULL)
					goto error;
				smlDevInfPropertySetMaxSize(property, atoi(value));
				break;
			case SML_DEVINF_CTCAP_NOTRUNCATE:
				if (property == NULL)
					goto error;
				smlDevInfPropertySetNoTruncate(property);
				break;
			case SML_DEVINF_CTCAP_DISPLAYNAME:
				if (property == NULL)
					goto error;
				if (param != NULL)
					smlDevInfPropParamSetDisplayName(param, value);
				else
					smlDevInfPropertySetDisplayName(property, value);
				break;
			case SML_DEVINF_CTCAP_VALENUM:
				if (property == NULL)
					goto error;
				if (param != NULL)
					smlDevInfPropParamAddValEnum(param, value);
				else
					smlDevInfPropertyAddValEnum(property, value);
				break;
			case SML_DEVINF_CTCAP_PARAMNAME:
				if (property == NULL)
					goto error;
                                if (param != NULL)
                                {
                                       	smlDevInfPropertyAddPropParam(property, param);
                                       	param = NULL;
                                }
				param = smlDevInfNewPropParam(error);
				if (!param)
					goto error;
				smlDevInfPropParamSetParamName(param, value);
				break;
			case SML_DEVINF_CTCAP_SIZE:
				smlDevInfPropertySetPropSize(property, atoi(value));
				break;
			default:
				smlErrorSet(error, SML_ERROR_GENERIC, "Unknown CTCapType: %s", elem_name);
				goto error;
		}
		smlSafeCFree(&value);

		if (!_smlXmlParserStep(parser)) {
			smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
			goto error;
		}
	}
	if (param != NULL)
		smlDevInfPropertyAddPropParam(property, param);
	if (property != NULL)
		smlDevInfCTCapAddProperty(ctcap, property);
	if (ctcap != NULL)
		smlDevInfAppendCTCap(devinf, ctcap);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	if (value != NULL)
		smlSafeCFree(&value);
	return FALSE;
}

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

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

	SmlDevInfPropParam *param = smlDevInfNewPropParam(error);
	if (!param)
		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)
		{
			smlErrorSet(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 = smlDevInfCTCapTypeFromString(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:
				smlDevInfPropParamSetParamName(param, value);
				break;
			case SML_DEVINF_CTCAP_DATATYPE:
				smlDevInfPropParamSetDataType(param, value);
				break;
			case SML_DEVINF_CTCAP_DISPLAYNAME:
				smlDevInfPropParamSetDisplayName(param, value);
				break;
			case SML_DEVINF_CTCAP_VALENUM:
				smlDevInfPropParamAddValEnum(param, value);
				break;
			default:
				smlErrorSet(error, SML_ERROR_GENERIC, "Unknown CTCapType for PropParam: %s", elem_name);
				goto error;
		}
		smlSafeCFree(&value);

		if (!_smlXmlParserStep(parser)) {
			smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
			goto error;
		}
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return param;

error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	if (value != NULL)
		smlSafeCFree(&value);
	return NULL;
}

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

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

	SmlDevInfProperty *property = smlDevInfNewProperty(error);
	if (!property)
		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)
		{
			smlErrorSet(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 = smlDevInfCTCapTypeFromString(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:
				smlDevInfPropertySetPropName(property, value);
				break;
			case SML_DEVINF_CTCAP_DATATYPE:
				smlDevInfPropertySetDataType(property, value);
				break;
			case SML_DEVINF_CTCAP_MAXOCCUR:
				smlDevInfPropertySetMaxOccur(property, atoi(value));
				break;
			case SML_DEVINF_CTCAP_MAXSIZE:
				smlDevInfPropertySetMaxSize(property, atoi(value));
				break;
			case SML_DEVINF_CTCAP_NOTRUNCATE:
				smlDevInfPropertySetNoTruncate(property);
				break;
			case SML_DEVINF_CTCAP_DISPLAYNAME:
				smlDevInfPropertySetDisplayName(property, value);
				break;
			case SML_DEVINF_CTCAP_VALENUM:
				smlDevInfPropertyAddValEnum(property, value);
				break;
			case SML_DEVINF_CTCAP_PROPPARAM:
				param = _smlXmlDevInfDataStoreParseCTCap12PropParam(parser, error);
				if (!param)
					goto error;
				smlDevInfPropertyAddPropParam(property, param);
				break;
			case SML_DEVINF_CTCAP_SIZE:
				smlDevInfPropertySetPropSize(property, atoi(value));
				break;
			default:
				smlErrorSet(error, SML_ERROR_GENERIC, "Unknown CTCapType for Property: %s", elem_name);
				goto error;
		}
		if (value) smlSafeCFree(&value);

		if (!_smlXmlParserStep(parser)) {
			smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
			goto error;
		}
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return property;

error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	if (value != NULL)
		smlSafeCFree(&value);
	return NULL;
}

static SmlBool _smlXmlDevInfDataStoreParseCTCap12(SmlXmlParser *parser, SmlDevInf *devinf, SmlError **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 only one CTCap definition           */

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

	SmlDevInfCTCap *ctcap = smlDevInfNewCTCap(error);
	if (!ctcap)
		goto error;

	SmlDevInfProperty *property;
	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)
		{
			smlErrorSet(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 = smlDevInfCTCapTypeFromString(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:
				smlDevInfCTCapSetCTType(ctcap, value);
				break;
			case SML_DEVINF_CTCAP_VERCT:
				smlDevInfCTCapSetVerCT(ctcap, value);
				break;
			case SML_DEVINF_CTCAP_PROPERTY:
				property = _smlXmlDevInfDataStoreParseCTCap12Property(parser, error);
				if (!property)
					goto error;
				smlDevInfCTCapAddProperty(ctcap, property);
				break;
			default:
				smlErrorSet(error, SML_ERROR_GENERIC, "Unknown CTCapType for CTCap: %s", elem_name);
				goto error;
		}
		if (value) smlSafeCFree(&value);

		if (!_smlXmlParserStep(parser)) {
			smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
			goto error;
		}
	}
	smlDevInfAppendCTCap(devinf, ctcap);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	if (value != NULL)
		smlSafeCFree(&value);
	return FALSE;
}

static SmlBool _smlXmlDevInfDataStoreParseCTCap(SmlXmlParser *parser, SmlDevInf *devinf, SmlError **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 */

	SmlBool ret;
	if (devinf->version == 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 SmlBool _smlXmlDevInfDataStoreParseRxTx(SmlXmlParser *parser, const char *element, char **cttype, char **version, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %s, %p, %p, %p)", __func__, parser, VA_STRING(element), cttype, version, error);
	CHECK_ERROR_REF
	smlAssert(parser);
	smlAssert(element);
	smlAssert(cttype);
	
	if (!_smlXmlParserStep(parser)) {
		smlErrorSet(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) {
			smlErrorSet(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)) {
				smlErrorSet(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) {
				*version = g_strdup("");
			} else if (xmlTextReaderNodeType(parser->reader) == XML_NODE_TEXT) {
				*version = g_strstrip(g_strdup((char *)xmlTextReaderConstValue(parser->reader)));
				
				if (!_smlXmlParserExpectNode(parser, XML_NODE_CLOSE, FALSE, SML_ELEMENT_VERCT, error))
					goto error;
			} else {
				*version = g_strdup("");
				continue;
			}
		} else {
			smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node");
			goto error;
		}
		
		if (!_smlXmlParserStep(parser)) {
			smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
			goto error;
		}
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
	
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

static SmlBool _smlXmlDevInfDataStoreParse(SmlXmlParser *parser, SmlDevInf *devinf, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, parser, devinf, error);
	CHECK_ERROR_REF
	smlAssert(devinf);
	smlAssert(parser);
	
	SmlDevInfDataStore *datastore = smlTryMalloc0(sizeof(SmlDevInfDataStore), error);
	if (!datastore)
		goto error;
	datastore->refCount = 1;
	datastore->rxPrefContentType = NULL;
	datastore->txPrefContentType = NULL;
	
	if (!_smlXmlParserStep(parser)) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
		goto error_free_datastore;
	}
		
	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) {
			smlErrorSet(error, SML_ERROR_GENERIC,
				"The element %s is not a start node in Datastore.",
				xmlTextReaderConstName(parser->reader));
			goto error_free_datastore;
		}
		
		if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SOURCEREF)) {
			if (!_smlXmlParserGetString(parser, &(datastore->sourceref), SML_ELEMENT_SOURCEREF, error))
				goto error_free_datastore;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DISPLAYNAME)) {
			if (!_smlXmlParserGetString(parser, &(datastore->displayname), SML_ELEMENT_DISPLAYNAME, error))
				goto error_free_datastore;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MAXGUIDSIZE)) {
			if (!_smlXmlParserGetID(parser, &(datastore->maxGUIDSize), SML_ELEMENT_MAXGUIDSIZE, error))
				goto error_free_datastore;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_RXPREF)) {
			if (!_smlXmlDevInfDataStoreParseRxTx(parser, SML_ELEMENT_RXPREF, &(datastore->rxPrefContentType), &(datastore->rxPrefVersion), error))
				goto error_free_datastore;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_RX)) {
			char *cttype = NULL;
			char *verct = NULL;
			if (!_smlXmlDevInfDataStoreParseRxTx(parser, SML_ELEMENT_RX, &cttype, &verct, error))
				goto error_free_datastore;
			SmlDevInfContentType *ct = smlDevInfNewContentType(cttype, verct, error);
			smlSafeCFree(&cttype);
			if (verct)
				smlSafeCFree(&verct);
			if (!ct)
				goto error_free_datastore;
			smlDevInfDataStoreAddRx(datastore, ct);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TXPREF)) {
			if (!_smlXmlDevInfDataStoreParseRxTx(parser, SML_ELEMENT_TXPREF, &(datastore->txPrefContentType), &(datastore->txPrefVersion), error))
				goto error_free_datastore;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_TX)) {
			char *cttype = NULL;
			char *verct = NULL;
			if (!_smlXmlDevInfDataStoreParseRxTx(parser, SML_ELEMENT_TX, &cttype, &verct, error))
				goto error_free_datastore;
			SmlDevInfContentType *ct = smlDevInfNewContentType(cttype, verct, error);
			smlSafeCFree(&cttype);
			if (verct)
				smlSafeCFree(&verct);
			if (!ct)
				goto error_free_datastore;
			smlDevInfDataStoreAddTx(datastore, ct);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_CTCAP)) {
			if (!_smlXmlDevInfDataStoreParseCTCap(parser, devinf, error))
				goto error_free_datastore;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SYNCCAP)) {
			if (!_smlXmlDevInfDataStoreParseSyncCap(parser, datastore, error))
				goto error_free_datastore;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DSMEM)) { 
			if (!_smlXmlDevInfDataStoreParseDSMem(parser, datastore, error))
				goto error_free_datastore;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SUPPORT_HIERARCHICAL_SYNC)) {
			if (devinf->version < SML_DEVINF_VERSION_12) {
				smlErrorSet(error, SML_ERROR_GENERIC, "SupportHierarchicalSync is only supported in OMA DS 1.2 DevInf and later.");
				goto error;
			}
			datastore->supportsHierarchicalSync = TRUE;
			if (!xmlTextReaderIsEmptyElement(parser->reader)) {
				if (!_smlXmlParserStep(parser)) {
					smlErrorSet(error, SML_ERROR_GENERIC, "The closing element of %s is missing.", SML_ELEMENT_SUPPORT_HIERARCHICAL_SYNC);
					goto error;
				}
			}
		} else {
			smlErrorSet(error, SML_ERROR_GENERIC, "A DataStore within DevInf includes the unsupported element %s.", (char *)xmlTextReaderConstName(parser->reader));
			goto error_free_datastore;
		}
		
		if (!_smlXmlParserStep(parser)) {
			smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes");
			goto error_free_datastore;
		}
	}
	
	smlDevInfAddDataStore(devinf, datastore);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error_free_datastore:
	smlDevInfDataStoreUnref(datastore);
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

SmlDevInf *smlXmlDevInfParse(const char *data, unsigned int size, SmlError **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) {
		smlErrorSet(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 = smlTryMalloc0(sizeof(SmlDevInf), error);
	if (!devinf)
		goto error;

	smlDevInfRef(devinf);
	
	if (!_smlXmlParserStep(parser)) {
		smlErrorSet(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) {
			smlErrorSet(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"))
				devinf->version = SML_DEVINF_VERSION_11;
			else if (verdtd && !strcmp(verdtd, "1.0"))
				devinf->version = SML_DEVINF_VERSION_10;
			else if (verdtd && !strcmp(verdtd, "1.2"))
				devinf->version = SML_DEVINF_VERSION_12;
			else {
				smlSafeCFree(&verdtd);
				smlErrorSet(error, SML_ERROR_GENERIC, "Unknown devinf version");
				goto error;
			}
			smlSafeCFree(&verdtd);
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MAN)) {
			if (!_smlXmlParserGetString(parser, &(devinf->manufacturer), SML_ELEMENT_MAN, error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_MOD)) {
			if (!_smlXmlParserGetString(parser, &(devinf->model), SML_ELEMENT_MOD, error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_OEM)) {
			if (!_smlXmlParserGetString(parser, &(devinf->oem), SML_ELEMENT_OEM, error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_FWV)) {
			if (!_smlXmlParserGetString(parser, &(devinf->firmwareVersion), SML_ELEMENT_FWV, error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SWV)) {
			if (!_smlXmlParserGetString(parser, &(devinf->softwareVersion), SML_ELEMENT_SWV, error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_HWV)) {
			if (!_smlXmlParserGetString(parser, &(devinf->hardwareVersion), SML_ELEMENT_HWV, error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DEVID)) {
			if (!_smlXmlParserGetString(parser, &(devinf->devid), SML_ELEMENT_DEVID, error))
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_DEVTYPE)) {
			char *devtype = NULL;
			if (!_smlXmlParserGetString(parser, &devtype, SML_ELEMENT_DEVTYPE, error))
				goto error;
			
			devinf->devtyp = smlDevInfDevTypeFromString(devtype, error);
			smlSafeCFree(&devtype);
			
			if (devinf->devtyp == SML_DEVINF_DEVTYPE_UNKNOWN)
				goto error;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_UTC)) {
			if (devinf->version == SML_DEVINF_VERSION_10) {
				smlErrorSet(error, SML_ERROR_GENERIC, "Devinf 1.0 does not allow UTC");
				goto error;
			}
			
			devinf->supportsUTC = TRUE;
			if (!_smlXmlParserStep(parser)) {
				smlErrorSet(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)) {
					smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes2");
					goto error;
				}
			}
			continue;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SUPPORTLARGEOBJS)) {
			if (devinf->version == SML_DEVINF_VERSION_10) {
				smlErrorSet(error, SML_ERROR_GENERIC, "Devinf 1.0 does not allow large objects");
				goto error;
			}
			
			devinf->supportsLargeObjs = TRUE;
			if (!_smlXmlParserStep(parser)) {
				smlErrorSet(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)) {
					smlErrorSet(error, SML_ERROR_GENERIC, "Missing nodes2");
					goto error;
				}
			}
			continue;
		} else if (!strcmp((char *)xmlTextReaderConstName(parser->reader), SML_ELEMENT_SUPPORTNUMBEROFCHANGES)) {
			if (devinf->version == SML_DEVINF_VERSION_10) {
				smlErrorSet(error, SML_ERROR_GENERIC, "Devinf 1.0 does not allow number of changes");
				goto error;
			}
			
			devinf->supportsNumberOfChanges = TRUE;
			if (!_smlXmlParserStep(parser)) {
				smlErrorSet(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)) {
					smlErrorSet(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)) {
				smlErrorSet(error, SML_ERROR_GENERIC, "Unable to skip ext node");
				goto error;
			}
			continue;
		} else {
			smlErrorSet(error, SML_ERROR_GENERIC, "wrong initial node: %s", (char *)xmlTextReaderConstName(parser->reader));
			goto error;
		}
		
		if (!_smlXmlParserStep(parser)) {
			smlErrorSet(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)
		smlDevInfUnref(devinf);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return NULL;
}

