/*
 * 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_assm.h"
#include "sml_xml_assm_internals.h"

#define BUFFER_SIZE 500

/**
 * @defgroup Assembler SyncML XML Assembler
 * @ingroup PublicAPI
 * @brief Interfaces to assemble syncml messages
 * 
 */
/*@{*/

static SmlBool _smlXmlAssemblerStartNodeNS(SmlXmlAssembler *assm, const char *name, const char *uri, SmlError **error)
{
	CHECK_ERROR_REF
	smlAssert(assm);
	smlAssert(assm->writer);
	smlAssert(name != NULL);
	smlAssert(strlen(name) > 0);
	smlTrace(TRACE_INTERNAL, "%s: Starting \"%s\"", __func__, VA_STRING(name));
	int rc = xmlTextWriterStartElementNS(assm->writer, NULL, (xmlChar *)name, (xmlChar *)uri);
	if (rc < 0) {
        	smlErrorSet(error, SML_ERROR_GENERIC, "Unable to start node");
		return FALSE;
	}
	return TRUE;
}

static SmlBool _smlXmlAssemblerStartNode(SmlXmlAssembler *assm, const char *name, SmlError **error)
{
	CHECK_ERROR_REF
	return _smlXmlAssemblerStartNodeNS(assm, name, NULL, error);
}

static SmlBool _smlXmlAssemblerEndNode(SmlXmlAssembler *assm, SmlError **error)
{
	CHECK_ERROR_REF
	smlTrace(TRACE_INTERNAL, "%s: Ending node", __func__);
	int rc = xmlTextWriterEndElement(assm->writer);
	if (rc < 0) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unable to end node (%d).", rc);
		return FALSE;
	}
	return TRUE;
}

static SmlBool _smlXmlAssemblerAddStringNS(SmlXmlAssembler *assm, const char *name, const char *uri, const char *value, SmlError **error)
{
	CHECK_ERROR_REF
	int rc = xmlTextWriterWriteElementNS(assm->writer, NULL, (xmlChar *)name, (xmlChar *)uri, (xmlChar *)value);
	if (rc < 0) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unable to add string (%d - %s:%s -> %s).", rc, VA_STRING(uri), VA_STRING(name), VA_STRING(value));
		return FALSE;
	}
	return TRUE;
}

static SmlBool _smlXmlAssemblerAddString(SmlXmlAssembler *assm, const char *name, const char *value, SmlError **error)
{
	CHECK_ERROR_REF
	int rc = xmlTextWriterWriteElement(assm->writer, (xmlChar *)name, (xmlChar *)value);
	if (rc < 0) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unable to add pure string (%d - %s -> %s).", rc, VA_STRING(name), VA_STRING(value));
		return FALSE;
	}
	return TRUE;
}

static SmlBool _smlXmlAssemblerAddData(SmlXmlAssembler *assm, const char *name, const char *value, unsigned int size, SmlBool raw, SmlError **error)
{
	CHECK_ERROR_REF
	int rc = 0;
	if (!_smlXmlAssemblerStartNode(assm, name, error))
		return FALSE;
	
	if (raw)
		rc = xmlTextWriterWriteRawLen(assm->writer, (xmlChar *)value, size);
	else
		rc = xmlTextWriterWriteFormatCDATA(assm->writer, "%*s", size, (xmlChar *)value);
		
	if (rc < 0) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unable to add data (%d).", rc);
		return FALSE;
	}
		
	if (!_smlXmlAssemblerEndNode(assm, error))
		return FALSE;
	
	return TRUE;
}

static SmlBool _smlXmlAssemblerAddID(SmlXmlAssembler *assm, const char *name, unsigned int id, SmlError **error)
{
	CHECK_ERROR_REF
	int rc = xmlTextWriterWriteFormatElement(assm->writer, (xmlChar *)name, "%i", id);
	if (rc < 0) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unable to add id");
		return FALSE;
	}
	return TRUE;
}

static SmlBool _smlXmlAssemblerAddIDNS(SmlXmlAssembler *assm, const char *name, const char *uri, unsigned int id, SmlError **error)
{
	CHECK_ERROR_REF
	int rc = xmlTextWriterWriteFormatElementNS(assm->writer, NULL, (xmlChar *)name, (xmlChar *)uri, "%i", id);
	if (rc < 0) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unable to add id");
		return FALSE;
	}
	return TRUE;
}

static SmlBool _smlAssembleUseMaxObjSize(SmlXmlAssembler *assm)
{
	smlTrace(TRACE_ENTRY, "%s(%p)", __func__, assm);

	/* check the configuration */
	const char *opt = smlAssemblerGetOption(assm->assembler, "USE_LARGEOBJECTS");
	SmlBool supportsLargeObjects = (opt && !atoi(opt)) ? FALSE : TRUE;

	/* server should react only the client behavior */
	if (assm->session->sessionType == SML_SESSION_TYPE_SERVER)
	{
		if (smlAssemblerGetRemoteMaxObjSize(assm->assembler) <= 0)
			supportsLargeObjects = FALSE;
		if (smlAssemblerGetRemoteMaxMsgSize(assm->assembler) <= 0)
			supportsLargeObjects = FALSE;
	}

	/* SyncML 1.0 does not support large objects */
	if (assm->session->version == SML_VERSION_10)
		supportsLargeObjects = FALSE;

	/* If MaxObjsize must really be sent
	 * then check if it is configured
	 */
	if (smlSessionGetLocalMaxObjSize(assm->session) <= 0)
		supportsLargeObjects = FALSE;

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

static SmlBool _smlAssembleMaxObjSize(SmlXmlAssembler *assm, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p)", __func__, assm);
	CHECK_ERROR_REF

	/* It is allowed to add MaxObjSize to the Alert or Sync
	 * command and libsyncml adds the MaxObjSize to the Sync
	 * command. Explanation from OMA DS Protocol v1.2:
	 *
	 * 6      Protocol Fundamentals
	 * 6.10   Large Object Handling
	 * 6.10.1 Conformance statements
	 * Citation: If a device supports receiving Large
	 *           Objects it MUST declare the maximum size of
	 *           object (<MaxObjSize> tag) it is capable of
	 *           receiving as Meta information within the
	 *           Alert or Sync command, as specified in
	 *           [META].
	 *
	 * SyncML 1.0 does not support large objects. Therefore
	 * MaxObjSize must not be sent because otherwise this
	 * could result in errors (bad request/message).
	 */
	if (!_smlXmlAssemblerAddIDNS(assm, SML_ELEMENT_MAXOBJSIZE, SML_NAMESPACE_METINF, smlSessionGetLocalMaxObjSize(assm->session), error))
		goto error;

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

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

SmlBool smlLocationAssemble(SmlLocation *location, SmlXmlAssembler *assm, const char *name, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %s, %p)", __func__, location, assm, VA_STRING(name), error);
	CHECK_ERROR_REF
	smlAssert(assm);
	smlAssert(location);
	
	if (name) {
		if (!_smlXmlAssemblerStartNode(assm, name, error))
			goto error;
	}
	
	if (!location->locURI) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No locURI set");
		goto error;
	}
	
	if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_LOCURI, location->locURI, error))
		goto error;
	
	if (location->locName) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_LOCNAME, location->locName, error))
			goto error;
	}
	
	if (name) {
		if (!_smlXmlAssemblerEndNode(assm, error))
			goto error;
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

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



SmlBool smlAnchorAssemble(SmlAnchor *anchor, SmlXmlAssembler *assm, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, anchor, assm, error);
	CHECK_ERROR_REF
	smlAssert(assm);
	smlAssert(anchor);

	/* Start the anchor and switch to syncml:metinf namespace.
	 * It is not possible to remove the namespace from Last and
	 * Next because otherwise the Status commands could be longer
	 * then the Alert commands. If this would happen then the
	 * Status commands alone could be longer than MaxMsgSize.
	 *
	 * It is not sure how a mobile would react in this situation.
	 */
	if (!_smlXmlAssemblerStartNodeNS(assm, SML_ELEMENT_ANCHOR, SML_NAMESPACE_METINF, error))
		goto error;
	
	if (!anchor->next) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No next set");
		goto error;
	}
	
	/* SyncML Meta-Information DTD 1.1, Page 6, 5.1 Anchor - Content Model: (Last?, Next)
	   Last is optional, don't write a empty Last node.
	 */ 
	if (anchor->last) {
		if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_LAST, SML_NAMESPACE_METINF, anchor->last, error))
			goto error;
		
	}
	
	if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_NEXT, SML_NAMESPACE_METINF, anchor->next, error))
		goto error;
	
	if (!_smlXmlAssemblerEndNode(assm, error))
		goto error;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

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

SmlBool smlItemAssemble(SmlItem *item, SmlXmlAssembler *assm, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, item, assm, error);
	CHECK_ERROR_REF
	smlAssert(assm);
	smlAssert(item);
	
	if (assm->moreDataSet) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Trying to start a new item while last item had more data");
		goto error;
	}

	/* Begin of Item */
	if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_ITEM, error))
		goto error;

	/* Target */
	if (smlItemGetTarget(item)) {
		if (!smlLocationAssemble(smlItemGetTarget(item), assm, SML_ELEMENT_TARGET, error))
			goto error;
	}

	/* Source */
	if (smlItemGetSource(item)) {
		if (!smlLocationAssemble(smlItemGetSource(item), assm, SML_ELEMENT_SOURCE, error))
			goto error;
	}

	/* SourceParent */
	if (smlItemGetSourceParent(item)) {
		if (!smlLocationAssemble(smlItemGetSourceParent(item), assm, SML_ELEMENT_SOURCE_PARENT, error))
			goto error;
	}

	/* TargetParent */
	if (smlItemGetTargetParent(item)) {
		if (!smlLocationAssemble(smlItemGetTargetParent(item), assm, SML_ELEMENT_TARGET_PARENT, error))
			goto error;
	}

	/* Data */
	if (smlItemHasData(item)) {
		if (item->disabled) {
			if (!_smlXmlAssemblerAddData(assm, SML_ELEMENT_DATA, "", 0, item->raw, error))
				goto error;
		} else {
			char *data = NULL;
			unsigned int size = 0;
			if (!smlItemGetData(item, &data, &size, error))
				goto error;
			
			if (!_smlXmlAssemblerAddData(assm, SML_ELEMENT_DATA, data, size, item->raw, error))
				goto error;
		}
	}

	/* MoreData */
	if (item->moreData) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_MOREDATA, "", error))
			goto error;
		
		assm->moreDataSet = TRUE;
	}

	/* End of Item */
	if (!_smlXmlAssemblerEndNode(assm, error))
		goto error;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

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

SmlBool smlCredAssemble(SmlCred *cred, SmlXmlAssembler *assm, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, cred, assm, error);
	CHECK_ERROR_REF
	smlAssert(assm);
	smlAssert(cred);
		
	if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_CRED, error))
		goto error;
	
	//Meta
	if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_META, error))
		goto error;
	
	switch (cred->format) {
		case SML_FORMAT_TYPE_BASE64:
			if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_FORMAT, SML_NAMESPACE_METINF, SML_BASE64, error))
				goto error;
			break;
		default:
			smlErrorSet(error, SML_ERROR_GENERIC, "SyncML credential: Unknown format %d.", cred->format);
			goto error;
	}
	
	switch (cred->type) {
		case SML_AUTH_TYPE_BASIC:
			if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_TYPE, SML_NAMESPACE_METINF, SML_AUTH_BASIC, error))
				goto error;
			break;
		case SML_AUTH_TYPE_MD5:
			if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_TYPE, SML_NAMESPACE_METINF, SML_AUTH_MD5, error))
				goto error;
			break;
		default:
			smlErrorSet(error, SML_ERROR_GENERIC, "Unknown format");
			goto error;
	}
	
	//META
	if (!_smlXmlAssemblerEndNode(assm, error))
		goto error;
	
	if (!_smlXmlAssemblerAddData(assm, SML_ELEMENT_DATA, cred->data, strlen(cred->data), TRUE, error))
		goto error;
	
	if (!_smlXmlAssemblerEndNode(assm, error))
		goto error;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

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

SmlBool smlAccessAssemble(SmlXmlAssembler *assm, SmlCommand *change, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, change, error);
	CHECK_ERROR_REF
	smlAssert(change);
	smlAssert(assm);
	
	if (!change->private.access.item) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Missing item");
		goto error;
	}
	
	if (!change->private.access.item->contenttype) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Missing contenttype");
		goto error;
	}
	
	//Meta
	if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_META, error))
		goto error;
	
	if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_TYPE, SML_NAMESPACE_METINF, change->private.access.item->contenttype, error))
		goto error;
	
	//META
	if (!_smlXmlAssemblerEndNode(assm, error))
		goto error;
	
	if (!smlItemAssemble(change->private.access.item, assm, error))
		goto error;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

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

SmlBool smlChangeAssemble(SmlXmlAssembler *assm, SmlCommand *change, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, change, error);
	CHECK_ERROR_REF
	smlAssert(change);
	smlAssert(assm);
	
	if (!change->private.change.items ||
	    !g_list_length(change->private.change.items)) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Missing items");
		goto error;
	}

	/* libsyncml only supports one item per change command */
	smlAssert(g_list_length(change->private.change.items) == 1);
	SmlItem *item = g_list_nth_data(change->private.change.items, 0);

	if (!item) {
		smlErrorSet(error, SML_ERROR_GENERIC, "One item of the item list is NULL.");
		goto error;
	}
	if (!item->contenttype) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Missing contenttype");
		goto error;
	}
	
	//Meta
	if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_META, error))
		goto error;
	
	if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_TYPE, SML_NAMESPACE_METINF, item->contenttype, error))
		goto error;
	
	/* We will add the max obj size node, if USE_LARGEOBJECTS is true or not set at all.
	 * And the remote side must have set a maxobjsize if we are a server */
	const char *opt = smlAssemblerGetOption(assm->assembler, "USE_LARGEOBJECTS");
	SmlBool supportsLargeObjects = (opt && !atoi(opt)) ? FALSE : TRUE;
	
	smlTrace(TRACE_INTERNAL, "%s: Large object: use %i, server %i, requestedSize %i", __func__, supportsLargeObjects, assm->session->sessionType == SML_SESSION_TYPE_SERVER ? 1 : 0, smlAssemblerGetRemoteMaxObjSize(assm->assembler));
	
	if (supportsLargeObjects && change->size) {
		if (!_smlXmlAssemblerAddIDNS(assm, SML_ELEMENT_SIZE, SML_NAMESPACE_METINF, change->size, error))
			goto error;
	}

	//META
	if (!_smlXmlAssemblerEndNode(assm, error))
		goto error;

	//Item(s)

	/* Item has to deal with four different situations:
	 *   1. If this is a new item but the remote peer only supports
	 *      the Replace command and the local peer is an OMA DS
	 *      server then the ID/location must be send as target.
	 *      If the local peer is an OMA DS client then nothing
	 *      changes. FYI an OMA DS server must support the Add
	 *      command.
	 *      => Target must be set (Target is the former Source)
	 *   2. If this is a new item then the local peer only knows
	 *      the ID of the item.
	 *      => Source must be set
	 *   3. If this is a changed or deleted item and the local peer
	 *      is an OMA DS server then the item must reference the ID
	 *      of the remote peer because of the submitted map. Please
	 *      not that the remote peer as an OMA DS client must only
	 *      know its own ID of the item. The mapping of locations
	 *      (IDs) is the exclusive job of the OMA DS server.
	 *      => Target must be set (Source is optional)
	 *   4. If this is a changed or deleted item and the local peer
	 *      is an OMA DS client then the item must reference the ID
	 *      of the local peer only because the mapping of locations
	 *      (IDs) must be done by the OMA DS server.
	 *      => Source must be set
	 */
	SmlSessionType sessionType = assm->session->sessionType;
	opt = smlAssemblerGetOption(assm->assembler, "ONLY_REPLACE");
	if (change->type == SML_COMMAND_TYPE_ADD)
	{
		if (opt && atoi(opt) && /* remote peer supports ONLY_REPLACE */
		    sessionType == SML_SESSION_TYPE_SERVER) {
			/* only Target must be set */
			if (item->target)
				smlLocationUnref(item->target);
			item->target = item->source;
			item->source = NULL;
		} else {
			/* only Source must be set */
			if (item->target)
				smlLocationUnref(item->target);
			item->target = NULL;
		}
	} else {
		if (sessionType == SML_SESSION_TYPE_SERVER) {
			/* Target must be set */
			if (item->source)
				smlLocationUnref(item->source);
			item->source = NULL;
		} else {
			/* only Source must be set */
			if (item->target)
				smlLocationUnref(item->target);
			item->target = NULL;
		}
	}
	
	if (!smlItemAssemble(item, assm, error))
		goto error;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

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

SmlBool smlSyncAssemble(SmlXmlAssembler *assm, SmlCommand *cmd, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, cmd, error);
	CHECK_ERROR_REF
	smlAssert(cmd);
	smlAssert(assm);
	
	if (!cmd->target) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No target set");
		goto error;
	}
	
	/* The specification OMA DS Protocol v1.2 allows to add
	 * MaxObjsize to Alert or Sync commands but all examples show
	 * it only in Alert commands. Therefore libsyncml adds it to
	 * Alert commands only.
	 */

	if (!smlLocationAssemble(cmd->target, assm, SML_ELEMENT_TARGET, error))
		goto error;
	
	if (!cmd->source) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No source set");
		goto error;
	}
	
	if (!smlLocationAssemble(cmd->source, assm, SML_ELEMENT_SOURCE, error))
		goto error;

	if (_smlAssembleUseMaxObjSize(assm))
	{
		if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_META, error))
			goto error;

	    	if (!_smlAssembleMaxObjSize(assm, error))
			goto error;

		if (!_smlXmlAssemblerEndNode(assm, error))
			goto error;
	}

	const char *opt = smlAssemblerGetOption(assm->assembler, "USE_NUMBEROFCHANGES");
	SmlBool supportsNumberOfChanges = (opt && !atoi(opt)) ? FALSE : TRUE;
	if (supportsNumberOfChanges && assm->session->version != SML_VERSION_10) {
		if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_NUMBEROFCHANGES, cmd->private.sync.numChanged, error))
			goto error;
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

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

SmlBool smlMapItemAssemble(SmlXmlAssembler *assm, SmlMapItem *item, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, item, error);
	CHECK_ERROR_REF
	smlAssert(assm);
	smlAssert(item);
		
	if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_MAPITEM, error))
		goto error;
	
	if (item->source) {
		if (!smlLocationAssemble(item->source, assm, SML_ELEMENT_SOURCE, error))
			goto error;
	}
	
	if (item->target) {
		if (!smlLocationAssemble(item->target, assm, SML_ELEMENT_TARGET, error))
			goto error;
	}
	
	//MapItem
	if (!_smlXmlAssemblerEndNode(assm, error))
		goto error;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

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

SmlBool smlMapAssemble(SmlXmlAssembler *assm, SmlCommand *cmd, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, cmd, error);
	CHECK_ERROR_REF
	smlAssert(cmd);
	smlAssert(assm);
	
	if (!cmd->target) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No target set");
		goto error;
	}
	
	if (!smlLocationAssemble(cmd->target, assm, SML_ELEMENT_TARGET, error))
		goto error;
	
	if (!cmd->source) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No source set");
		goto error;
	}
	
	if (!smlLocationAssemble(cmd->source, assm, SML_ELEMENT_SOURCE, error))
		goto error;
		
	GList *m = NULL;
	for (m = cmd->private.map.items; m; m = m->next) {
		SmlMapItem *item = m->data;
		if (!smlMapItemAssemble(assm, item, error))
			goto error;
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

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

SmlBool smlAlertAssemble(SmlXmlAssembler *assm, SmlCommand *cmd, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, cmd, error);
	CHECK_ERROR_REF
	smlAssert(cmd);
	smlAssert(assm);
	
	/* Results itself and CmdID must be set by the caller.
	 * This function only add the Results specific content.
	 */

	/* No Resp */

	/* Cred */

	/* Data */
	if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_DATA, cmd->private.alert.type, error))
		goto error;

	/* Correlator */

	/* Begin of Item */
	if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_ITEM, error))
		goto error;

	/* Target */
	if (cmd->target) {
		if (!smlLocationAssemble(cmd->target, assm, SML_ELEMENT_TARGET, error))
			goto error;
	}

	/* Source */
	if (cmd->source) {
		if (!smlLocationAssemble(cmd->source, assm, SML_ELEMENT_SOURCE, error))
			goto error;
	} else {
		// NEXT MESSAGE alerts does not need a source/target
		if (cmd->private.alert.type != SML_ALERT_NEXT_MESSAGE) {
			smlErrorSet(error, SML_ERROR_GENERIC, "No source set");
			goto error;
		}
	}

	/* SourceParent */

	/* TargetParent */

	/* Begin of META */

	SmlBool meta = FALSE;
	if (cmd->private.alert.contentType)
		meta = TRUE;
	if (cmd->private.alert.anchor)
		meta = TRUE;
	if (_smlAssembleUseMaxObjSize(assm))
		meta = TRUE;

	if (meta && !_smlXmlAssemblerStartNode(assm, SML_ELEMENT_META, error))
		goto error;

	if (cmd->private.alert.contentType &&
	    !_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_TYPE, SML_NAMESPACE_METINF,
	                                 cmd->private.alert.contentType, error))
		goto error;

	if (cmd->private.alert.anchor &&
	    !smlAnchorAssemble(cmd->private.alert.anchor, assm, error))
		goto error;

	if (_smlAssembleUseMaxObjSize(assm) &&
	    !_smlAssembleMaxObjSize(assm, error))
		goto error;

	if (meta && !_smlXmlAssemblerEndNode(assm, error))
		goto error;

	/* End of META */
	
	/* End of Item */
	if (!_smlXmlAssemblerEndNode(assm, error))
		goto error;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

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

SmlBool smlResultsAssemble(SmlXmlAssembler *assm, SmlCommand *cmd, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, cmd, error);
	CHECK_ERROR_REF
	smlAssert(cmd);
	smlAssert(assm);

	/* Results itself and CmdID must be set by the caller.
	 * This function only add the Results specific content.
	 */

	/* MsgRef */
	if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_MSGREF, cmd->private.results.status->msgRef, error))
		goto error;

	/* CmdRef */
	if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_CMDREF, cmd->private.results.status->cmdRef, error))
		goto error;

	/* Meta */
	if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_META, error))
		goto error;
	if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_TYPE, SML_NAMESPACE_METINF, cmd->private.results.status->item->contenttype, error))
		goto error;
	if (!_smlXmlAssemblerEndNode(assm, error))
		goto error;

	/* TargetRef */
	if (cmd->private.results.status->targetRef) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_TARGETREF, cmd->private.results.status->targetRef->locURI, error))
			goto error;
	}

	/* SourceRef */
	if (cmd->private.results.status->sourceRef) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SOURCEREF, cmd->private.results.status->sourceRef->locURI, error))
			goto error;
	}

	/* Item */
	if (!smlItemAssemble(cmd->private.results.status->item, assm, error))
		goto error;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

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

SmlBool smlXmlAssemblerAddHeader(SmlXmlAssembler *assm, SmlSession *session, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, session, error);
	CHECK_ERROR_REF
	smlAssert(assm);
	smlAssert(g_mutex_trylock(assm->mutex));
	smlAssert(session);
	
	/* Lets see if there already was a header before */
	if (assm->header_buffer) {
		xmlBufferFree(assm->header_buffer);
		assm->header_buffer = NULL;
	}
	
	/* We first start a new writer that will write our header into a buffer */
	assm->header_buffer = xmlBufferCreateSize(BUFFER_SIZE);
	if (!assm->header_buffer) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new buffer");
		goto error;
	}
	
	assm->writer = xmlNewTextWriterMemory(assm->header_buffer, 0);
	if (!assm->writer) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new writer");
		goto error;
	}
	
	if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_SYNCHDR, error))
		goto error;
	
	if (!session->protocol) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No version set");
		goto error;
	}
	
	if (!session->version) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No dtd set");
		goto error;
	}
	
	switch (session->protocol) {
		case SML_PROTOCOL_SYNCML:
			switch (session->version) {
				case SML_VERSION_10:
					if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERDTD, "1.0", error))
						goto error;
						
					if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERPROTO, SML_VERSION_STRING_10, error))
						goto error;
					break;
				case SML_VERSION_11:
					if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERDTD, "1.1", error))
						goto error;
						
					if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERPROTO, SML_VERSION_STRING_11, error))
						goto error;
					break;
				case SML_VERSION_12:
					if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERDTD, "1.2", error))
						goto error;
					
					if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERPROTO, SML_VERSION_STRING_12, error))
						goto error;
					break;
				default:
					smlErrorSet(error, SML_ERROR_GENERIC, "Unknown version");
					goto error;
			}
			break;
		default:
			smlErrorSet(error, SML_ERROR_GENERIC, "Unknown protocol");
			goto error;
	}
	
	if (session->sessionID) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SESSIONID, session->sessionID, error))
			goto error;
	}
		
	if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_MSGID, session->lastMessageID, error))
		goto error;
	
	if (!smlLocationAssemble(session->target, assm, SML_ELEMENT_TARGET, error))
		goto error;
	
	if (!smlLocationAssemble(session->source, assm, SML_ELEMENT_SOURCE, error))
		goto error;

	if (session->responseURI &&
	    !_smlXmlAssemblerAddString(assm, SML_ELEMENT_RESPURI, session->responseURI, error))
		goto error;

	/* The credentials must be present and calculated for the correct
	 * authentication schema (e.g. syncml:auth-basic or syncml:auth-md5).
	 */
	if (session->cred != NULL && session->cred->data != NULL) {
		if (!smlCredAssemble(session->cred, assm, error))
			goto error;
	}

	/* What do we have to check here?
	 * 
	 * SyncML 1.0 does not require any checks because large object
	 * handling was first specified for SyncML 1.1.
	 *
	 * SyncML 1.1 and 1.2 require some checks.
	 *
	 * 1. OMA DS server MUST support large objects.
	 *    This means that a local MaxMsgSize and a local MaxObjSize
	 *    MUST be specified.
	 * 2. If an OMA DS client suppors large objects then it MUST
	 *    match the same requirements like the OMA DS server.
	 */
	if (session->protocol == SML_PROTOCOL_SYNCML &&
	    session->version > SML_VERSION_10)
	{
		/* This is a SyncML protocol version which supports
		 * large objects.
		 */
		if (session->sessionType == SML_SESSION_TYPE_SERVER ||
		    smlSessionGetLocalMaxObjSize(session) > 0)
		{
			/* Large object support is enabled. */
			if (smlSessionGetLocalMaxMsgSize(session) < 1)
			{
				smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION,
					"MaxMsgSize must be set.");
				goto error;
			}
			if (smlSessionGetLocalMaxObjSize(session) < 1)
			{
				smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION,
					"MaxObjSize must be set.");
				goto error;
			}
		}
	}

	/* set MaxMsgSize if available */
	unsigned int localMaxMsgSize = smlSessionGetLocalMaxMsgSize(session);
	if (localMaxMsgSize) {
		if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_META, error))
			goto error;
		
		/* Do not add MaxObjSize here.
		 * Please see OMA DS Protocol v1.2 section 6.10.1
		 */
		if (!_smlXmlAssemblerAddIDNS(assm, SML_ELEMENT_MAXMSGSIZE, SML_NAMESPACE_METINF, localMaxMsgSize, error))
			goto error;

		//META
		if (!_smlXmlAssemblerEndNode(assm, error))
			goto error;
	}
	
	if (!_smlXmlAssemblerEndNode(assm, error))
		goto error;
	
	/* Now close the buffer and get the content */
	if (xmlTextWriterEndDocument(assm->writer) < 0) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unable to end writer");
		goto error;
	}
	
	xmlFreeTextWriter(assm->writer);
	assm->writer = NULL;

	g_mutex_unlock(assm->mutex);
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error:
	if (assm->writer) {
		xmlFreeTextWriter(assm->writer);
		assm->writer = NULL;
	}
	if (assm->header_buffer) {
		xmlBufferFree(assm->header_buffer);
		assm->header_buffer = NULL;
	}
	g_mutex_unlock(assm->mutex);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

SmlBool smlXmlAssemblerStartCommand(SmlXmlAssembler *assm, unsigned int parentID, SmlCommand *cmd, SmlError **error)
{
	CHECK_ERROR_REF
	smlAssert(assm);
	smlAssert(cmd);
	
	if (cmd->type == SML_COMMAND_TYPE_UNKNOWN) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No cmd set");
		goto error;
	}
	
	if (!cmd->cmdID) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No cmd ID set");
		goto error;
	}
	
	/* Lets see if there already was a header before */
	if (!assm->header_buffer) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Header not yet added");
		goto error;
	}
	
	SmlXmlAssemblerCommand *assmcmd = smlTryMalloc0(sizeof(SmlXmlAssemblerCommand), error);
	if (!assmcmd)
		goto error;
	assmcmd->nodeType = SML_ASSEMBLER_NODE_OPEN;
	assmcmd->cmdID = cmd->cmdID;
	assmcmd->cmdType = cmd->type;
	
	GList **appendto = &(assm->commands);
	if (parentID) {
		/* If we have a parent we first have to search for this command */
		SmlXmlAssemblerCommand *excmd = NULL;
		GList *b = NULL;
		for (b = assm->commands; b; b = b->next) {
			excmd = b->data;
			if (excmd->cmdID == parentID)
				appendto = &(excmd->children);
		}
	}
	
	/* We first start a new parent writer that will write our command into a buffer */
	assmcmd->buffer = xmlBufferCreateSize(BUFFER_SIZE);
	if (!assmcmd->buffer) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new buffer");
		goto error_free_cmd;
	}
	
	assm->writer = xmlNewTextWriterMemory(assmcmd->buffer, 0);
	if (!assm->writer) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new writer");
		goto error_free_buffer;
	}
	
	/* We start without the root node */
	if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_CMDID, cmd->cmdID, error))
		goto error_free_writer;
	
	switch (cmd->type) {
		case SML_COMMAND_TYPE_ALERT:
			if (!smlAlertAssemble(assm, cmd, error))
				goto error_free_writer;
			break;
		case SML_COMMAND_TYPE_SYNC:
			if (!smlSyncAssemble(assm, cmd, error))
				goto error_free_writer;
			break;
		case SML_COMMAND_TYPE_ADD:
		case SML_COMMAND_TYPE_REPLACE:
		case SML_COMMAND_TYPE_DELETE:
			if (!smlChangeAssemble(assm, cmd, error))
				goto error_free_writer;
			break;
		case SML_COMMAND_TYPE_PUT:
		case SML_COMMAND_TYPE_GET:
			if (!smlAccessAssemble(assm, cmd, error))
				goto error_free_writer;
			break;
		case SML_COMMAND_TYPE_MAP:
			if (!smlMapAssemble(assm, cmd, error))
				goto error_free_writer;
			break;
		case SML_COMMAND_TYPE_RESULTS:
			if (!smlResultsAssemble(assm, cmd, error))
				goto error_free_writer;
			break;
		default:
			smlErrorSet(error, SML_ERROR_GENERIC, "Unknown command type");
			goto error_free_writer;
	}
	
	/* Now close the buffer */
	if (xmlTextWriterEndDocument(assm->writer) < 0) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unable to end writer");
		goto error_free_writer;
	}
	
	xmlFreeTextWriter(assm->writer);
	assm->writer = NULL;
	
	/* Now we can append the buffer and the parent*/
	*appendto = g_list_append(*appendto, assmcmd);
	
	return TRUE;
	
error_free_writer:
	xmlFreeTextWriter(assm->writer);
	assm->writer = NULL;
error_free_buffer:
	xmlBufferFree(assmcmd->buffer);
error_free_cmd:
	smlSafeFree((gpointer *)&assmcmd);
error:
	return FALSE;
}

SmlBool smlXmlAssemblerEndCommand(SmlXmlAssembler *assm, unsigned int parentID, SmlError **error)
{
	CHECK_ERROR_REF
	smlAssert(assm);
	
	/* Lets see if there already was a header before */
	if (!assm->header_buffer) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Header not yet added");
		goto error;
	}
	
	SmlXmlAssemblerCommand *assmcmd = smlTryMalloc0(sizeof(SmlXmlAssemblerCommand), error);
	if (!assmcmd)
		goto error;
	assmcmd->nodeType = SML_ASSEMBLER_NODE_CLOSE;
	
	GList **appendto = &(assm->commands);
	if (parentID) {
		/* If we have a parent we first have to search for this command */
		SmlXmlAssemblerCommand *excmd = NULL;
		GList *b = NULL;
		for (b = assm->commands; b; b = b->next) {
			excmd = b->data;
			if (excmd->cmdID == parentID)
				appendto = &(excmd->children);
		}
	}
	
	/* Now we can append command */
	*appendto = g_list_append(*appendto, assmcmd);
	
	return TRUE;
	
error:
	return FALSE;
}

SmlBool smlXmlAssemblerRemCommand(SmlXmlAssembler *assm, unsigned int parentID, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %i, %p)", __func__, assm, parentID, error);
	CHECK_ERROR_REF
	smlAssert(assm);
	
	GList **removefrom = &(assm->commands);
	if (parentID) {
		/* If we have a parent we first have to search for this command */
		SmlXmlAssemblerCommand *excmd = NULL;
		GList *b = NULL;
		for (b = assm->commands; b; b = b->next) {
			excmd = b->data;
			if (excmd->cmdID == parentID)
				removefrom = &(excmd->children);
		}
	}
	
	if (!*removefrom) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Nothing to remove");
		goto error;
	}
	
	GList *b = g_list_last(*removefrom);
	SmlXmlAssemblerCommand *cmd = b->data;
	*removefrom = g_list_delete_link(*removefrom, b);
	if (cmd->nodeType != SML_ASSEMBLER_NODE_OPEN) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Trying to remove not a starting command");
		goto error;
	}
	
	assm->moreDataSet = FALSE;
	
	xmlBufferFree(cmd->buffer);
	smlSafeFree((gpointer *)&cmd);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
	
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

SmlBool smlXmlAssemblerRemStatus(SmlXmlAssembler *assm, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, assm, error);
	CHECK_ERROR_REF
	smlAssert(assm);
	
	if (!assm->statuses) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Trying to remove status but no status available");
		goto error;
	}
	
	GList *b;
	SmlXmlAssemblerStatus *last = NULL;
	for (b = assm->statuses; b; b = b->next) {
		SmlXmlAssemblerStatus *status = b->data;
	
		if (!status->buffer)
			break;
		last = status;
	}
	
	if (last) {
		xmlBufferFree(last->buffer);
		last->buffer = NULL;
	}
	
	assm->added_statuses--;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
	
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

SmlBool smlXmlAssemblerReserveStatus(SmlXmlAssembler *assm, unsigned int cmdRef, unsigned int msgRef, unsigned int cmdID, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %i, %i, %i, %p)", __func__, assm, cmdRef, msgRef, cmdID, error);
	CHECK_ERROR_REF
	smlAssert(assm);
	
	/* We first start a new writer that will write our status into a buffer */
	SmlXmlAssemblerStatus *res = smlTryMalloc0(sizeof(SmlXmlAssemblerStatus), error);
	if (!res)
		goto error;
	res->cmdRef = cmdRef;
	res->cmdID = cmdID;
	res->msgRef = msgRef;
	
	/* Now we can append the buffer */
	if (cmdRef != 0)
		assm->statuses = g_list_append(assm->statuses, res);
	else
		assm->statuses = g_list_prepend(assm->statuses, res);
	assm->reserved_statuses++;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
	
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

SmlBool smlXmlAssemblerAddStatus(SmlXmlAssembler *assm, SmlStatus *status, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, status, error);
	CHECK_ERROR_REF
	smlAssert(assm);
	smlAssert(g_mutex_trylock(assm->mutex));
	smlAssert(status);
	SmlXmlAssemblerStatus *res = NULL;
	
	smlTrace(TRACE_INTERNAL, "Adding status with cmdRef %i, msgRef %i, cmd %s", status->cmdRef, status->msgRef, smlCommandTypeToString(status->type, error));
	/* this happens if the status is damaged */
	if (*error)
		goto error;
	
	if (!status->msgRef) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No msgref set");
		goto error;
	}
	
	if (status->type == SML_COMMAND_TYPE_UNKNOWN) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No cmd set");
		goto error;
	}
	
	/* Lets see if there already was a header before */
	if (!assm->header_buffer) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Header not yet added");
		goto error;
	}
	
	/* Get the reserved buffer */
	GList *s = NULL;
	for (s = assm->statuses; s; s = s->next) {
		res = s->data;
		if (res->cmdRef == status->cmdRef && res->msgRef == status->msgRef)
			break;
		res = NULL;
	}
	
	if (!res) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Status not reserved");
		goto error;
	}
	
	if (!res->cmdID) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No cmd ID set");
		goto error;
	}
	
	if (res->buffer) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Status already added");
		goto error;
	}
	
	/* We first start a new writer that will write our status into a buffer */
	res->buffer = xmlBufferCreateSize(BUFFER_SIZE);
	if (!res->buffer) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new buffer");
		goto error;
	}
	
	assm->writer = xmlNewTextWriterMemory(res->buffer, 0);
	if (!assm->writer) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new writer");
		goto error_free_buffer;
	}
	
	/* Begin of Status */
	if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_STATUS, error))
		goto error;
	
	/* CmdID */
	if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_CMDID, res->cmdID, error))
		goto error;
	
	/* MsgRef */
	if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_MSGREF, status->msgRef, error))
		goto error;
		
	/* CmdRef */
	if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_CMDREF, res->cmdRef, error))
		goto error;
	
	/* Cmd */
	const char *cmdname = smlCommandTypeToString(status->type, error);
	if (!cmdname)
		goto error;
	if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_CMD, cmdname, error))
		goto error;
	
	/* TargetRef */
	if (status->targetRef) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_TARGETREF, status->targetRef->locURI, error))
			goto error;
	}
	
	/* SourceRef */
	if (status->sourceRef) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SOURCEREF, status->sourceRef->locURI, error))
			goto error;
	}

	/* Cred */

	/* Chal */
	if (status->type == SML_COMMAND_TYPE_HEADER && status->chal)
	{
		if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_CHAL, error))
			goto error;

		if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_META, error))
			goto error;

		if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_FORMAT, SML_NAMESPACE_METINF, SML_BASE64, error))
			goto error;

		switch (status->chal->type) {
			case SML_AUTH_TYPE_BASIC:
				if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_TYPE, SML_NAMESPACE_METINF, SML_AUTH_BASIC, error))
					goto error;
				break;
			case SML_AUTH_TYPE_MD5:
				if (!_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_TYPE, SML_NAMESPACE_METINF, SML_AUTH_MD5, error))
					goto error;
				if (status->chal->nonce_b64 &&
				    !_smlXmlAssemblerAddStringNS(assm, SML_ELEMENT_NEXTNONCE, SML_NAMESPACE_METINF, status->chal->nonce_b64, error))
					goto error;
				break;
			default:
				smlErrorSet(error, SML_ERROR_GENERIC, "Unknown auth type");
				goto error;
		}

		//META
		if (!_smlXmlAssemblerEndNode(assm, error))
			goto error;

		//CHAL
		if (!_smlXmlAssemblerEndNode(assm, error))
			goto error;
	}

	/* Data */
	if (status->data) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_DATA, status->data, error))
			goto error;
	}

	/* Item */
	if (status->type == SML_COMMAND_TYPE_ALERT && status->anchor)
	{
		if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_ITEM, error))
			goto error;

		if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_DATA, error))
			goto error;

		if (!smlAnchorAssemble(status->anchor, assm, error))
			goto error;

		//DATA
		if (!_smlXmlAssemblerEndNode(assm, error))
			goto error;

		//ITEM
		if (!_smlXmlAssemblerEndNode(assm, error))
			goto error;
	}
	
	/* End of Status */
	if (!_smlXmlAssemblerEndNode(assm, error))
		goto error;
	
	/* Now close the buffer and get the content */
	if (xmlTextWriterEndDocument(assm->writer) < 0) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unable to end writer");
		goto error_free_writer;
	}
	
	xmlFreeTextWriter(assm->writer);
	assm->writer = NULL;
	
	assm->added_statuses++;
	
	g_mutex_unlock(assm->mutex);
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
	
error_free_writer:
	xmlFreeTextWriter(assm->writer);
	assm->writer = NULL;
error_free_buffer:
	xmlBufferFree(res->buffer);
	res->buffer = NULL;
error:
	g_mutex_unlock(assm->mutex);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

SmlBool smlXmlAssemblerMissingStatus(SmlXmlAssembler *assm)
{
	smlAssert(assm);
	
	if (assm->reserved_statuses - assm->added_statuses > 0)
		return TRUE;
	
	return FALSE;
}

static void flush_list(GList *list)
{
	GList *l = NULL;
	for (l = list; l; l = l->next) {
		SmlXmlAssemblerCommand *cmd = l->data;
		
		if (cmd->nodeType != SML_ASSEMBLER_NODE_CLOSE && cmd->children)
			flush_list(cmd->children);
		
		xmlBufferFree(cmd->buffer);
		smlSafeFree((gpointer *)&cmd);
	}	
	
	g_list_free(list);
}

void smlXmlAssemblerFree(SmlXmlAssembler *assm)
{
	smlTrace(TRACE_ENTRY, "%s(%p)", __func__, assm);
	smlAssert(assm);
	
	if (assm->header_buffer) {
		xmlBufferFree(assm->header_buffer);
		assm->header_buffer = NULL;
	}
	
	while (assm->statuses) {
		SmlXmlAssemblerStatus *status = assm->statuses->data;
		
		if (status->buffer)
			xmlBufferFree(status->buffer);
		
		smlSafeFree((gpointer *)&status);
		assm->statuses = g_list_delete_link(assm->statuses, assm->statuses);
	}
	
	flush_list(assm->commands);
	flush_list(assm->last_commands);

	g_mutex_free(assm->mutex);
	smlSafeFree((gpointer *)&assm);

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

/* This function returns the highest command id that is still in the
 * assembler after flushing which is: number of statuses + 1 */
unsigned int smlXmlAssemblerFlush(SmlXmlAssembler *assm)
{
	smlTrace(TRACE_ENTRY, "%s(%p)", __func__, assm);
	smlAssert(assm);
	
	unsigned int newid = 1;
	SmlBool missing = FALSE;
	
	/* Remove the statuses */
	GList *s = NULL;
	GList *s2 = g_list_copy(assm->statuses);
	for (s = s2; s; s = s->next) {
		SmlXmlAssemblerStatus *status = s->data;
		/* Only remove statuses that were added already */
		if (!status->buffer) {
			/* If the status was not yet added, we have to reset the command id.
			 * We start with id #2 (since #1 will be the header reply) */
			/* FIXME: why should we reset a command id? */
			newid++;
			// status->cmdID = newid;
			missing = TRUE;
		} else if (!missing) {
			xmlBufferFree(status->buffer);
			assm->statuses = g_list_remove(assm->statuses, status);
			smlSafeFree((gpointer *)&status);
			assm->reserved_statuses--;
			assm->added_statuses--;
		}
	}
	g_list_free(s2);
	
	/* Remove all commands that are complete
	 * Perhaps more correct remove all commands from the last message.
	 * The actual commands are cached to support error 407 handling.
	 * (AUTHENTICATION_REQUIRED)
	 */
	if (assm->last_commands)
		flush_list(assm->last_commands);
	assm->last_commands = assm->commands;
	assm->commands = NULL;

	/* Reset LargeObject handling */
	assm->moreDataSet = FALSE;
	
	smlTrace(TRACE_EXIT, "%s: %i", __func__, newid);
	return newid;
}

SmlBool smlXmlAssemblerStart(SmlXmlAssembler *assm, SmlSession *session, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, session, error);
	CHECK_ERROR_REF
	smlAssert(assm);
	smlAssert(g_mutex_trylock(assm->mutex));
	smlAssert(session);

	assm->session = session;
	
	g_mutex_unlock(assm->mutex);
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
}


SmlBool smlXmlAssemblerEnd(SmlXmlAssembler *assm, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, assm, error);
	CHECK_ERROR_REF
	smlAssert(assm);
	smlAssert(g_mutex_trylock(assm->mutex));
	
	//Close syncml
	if (!_smlXmlAssemblerEndNode(assm, error))
		goto error;
	
	if (_smlXmlAssemblerEndNode(assm, NULL)) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Extra node open");
		goto error;
	}
		
	g_mutex_unlock(assm->mutex);
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error:
	g_mutex_unlock(assm->mutex);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

SmlBool smlXmlAssemblerAddChildren(SmlXmlAssembler *assm, GList *b, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, b, error);
	CHECK_ERROR_REF
	smlAssert(assm);
	const char *opt = smlAssemblerGetOption(assm->assembler, "ONLY_REPLACE");
	SmlBool onlyReplace = (opt && atoi(opt)) ? TRUE : FALSE;
	
	SmlXmlAssemblerCommand *cmd = NULL;
	const char *cmdname = NULL;
	
	for (; b; b = b->next) {
		cmd = b->data;
		switch (cmd->nodeType) {
			case SML_ASSEMBLER_NODE_OPEN:
				/* Add the corresponding command opener */
				
				if (cmd->cmdType == SML_COMMAND_TYPE_ADD && onlyReplace)
					cmdname = SML_ELEMENT_REPLACE;
				else {
					cmdname = smlCommandTypeToString(cmd->cmdType, error);
					if (!cmdname)
						goto error;
				}
				smlTrace(TRACE_INTERNAL, "opening node %s", cmdname);
				
				if (!_smlXmlAssemblerStartNode(assm, cmdname, error))
					goto error;

				int rc = xmlTextWriterWriteRawLen(assm->writer, xmlBufferContent(cmd->buffer), xmlBufferLength(cmd->buffer));
				if (rc < 0) {
					smlErrorSet(error, SML_ERROR_GENERIC, "Unable to write raw node data (%d).", rc);
					goto error;
				}
				
				if (cmd->children) {
					if (!smlXmlAssemblerAddChildren(assm, cmd->children, error))
						goto error;
				}
				
				smlTrace(TRACE_INTERNAL, "%s: closing node", __func__);
				if (!_smlXmlAssemblerEndNode(assm, error))
					goto error;
				break;
			case SML_ASSEMBLER_NODE_CLOSE:
				/*smlTrace(TRACE_INTERNAL, "%s: closing node". __func__);
				if (!_smlXmlAssemblerEndNode(assm, error))
					goto error;*/
				break;
		}
	}

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

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

SmlBool smlXmlAssemblerRunFull(SmlXmlAssembler *assm, char **data, unsigned int *size, SmlBool *end, SmlBool final, SmlBool check, unsigned int maxsize, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p, %i, %i, %i, %p)", __func__, assm, data, size, end, final, check, maxsize, error);
	CHECK_ERROR_REF
	smlAssert(assm);
	smlAssert(g_mutex_trylock(assm->mutex));
	smlAssert(data);
	smlAssert(size);
	smlAssert(assm->session);
	unsigned int buffersize = 0;
	
	/* Return an error if there is no header */
	if (!assm->header_buffer) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No header available");
		goto error;
	}
	
	/* Return an error if there is no status/command. Note that if
	 * statuses have been reserved, at least the first status
	 * must be added */
	if (check && !assm->statuses && !assm->commands) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No status/command available");
		goto error;
	}
	
	if (check && assm->statuses && final) {
		SmlXmlAssemblerStatus *status = assm->statuses->data;
		if (!status->buffer) {
			smlErrorSet(error, SML_ERROR_GENERIC, "Missing the first status with cmdRef %i", status->cmdRef);
			goto error;
		}
	}
		
	/* Create the final buffer and writer */
	xmlBuffer *buffer = xmlBufferCreateSize(BUFFER_SIZE);
	if (!buffer) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new buffer");
		goto error;
	}
	
	assm->writer = xmlNewTextWriterMemory(buffer, 0);
	if (!assm->writer) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new writer");
		goto error_free_buffer;
	}
	
	/* Sync4j has a bug and cannot handle the default XML encoding.
	 * The default XML encoding is UTF-8. So this default encoding
	 * is explicitly added to support buggy XML parsers.
	 */
	if (xmlTextWriterStartDocument(assm->writer, NULL, "UTF-8", NULL) < 0) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new writer");
		goto error_free_writer;
	}
	
	/* Add the syncml start node */
	switch (assm->session->version) {
		case SML_VERSION_10:
			xmlTextWriterWriteRaw(assm->writer, (xmlChar *)"<!DOCTYPE SyncML PUBLIC \"-//SYNCML//DTD SyncML 1.0//EN\" \"http://www.syncml.org/docs/syncml_represent_v10_20001207.dtd\">");
			if (!_smlXmlAssemblerStartNodeNS(assm, SML_ELEMENT_SYNCML, SML_NAMESPACE_SYNCML10, error))
				goto error_free_writer;
			break;
		case SML_VERSION_11:
			xmlTextWriterWriteRaw(assm->writer, (xmlChar *)"<!DOCTYPE SyncML PUBLIC \"-//SYNCML//DTD SyncML 1.1//EN\" \"http://www.syncml.org/docs/syncml_represent_v11_20020213.dtd\">");
			if (!_smlXmlAssemblerStartNodeNS(assm, SML_ELEMENT_SYNCML, SML_NAMESPACE_SYNCML11, error))
				goto error_free_writer;
			break;
		case SML_VERSION_12:
			xmlTextWriterWriteRaw(assm->writer, (xmlChar *)"<!DOCTYPE SyncML PUBLIC \"-//SYNCML//DTD SyncML 1.2//EN\" \"http://www.openmobilealliance.org/tech/DTD/OMA-TS-SyncML_RepPro_DTD-V1_2.dtd\">");
			if (!_smlXmlAssemblerStartNodeNS(assm, SML_ELEMENT_SYNCML, SML_NAMESPACE_SYNCML12, error))
				goto error_free_writer;
			break;
		default:
			smlErrorSet(error, SML_ERROR_GENERIC, "Unknown version");
			goto error_free_writer;
	}
	
	/* Add the header */
	int rc = xmlTextWriterWriteRawLen(assm->writer, xmlBufferContent(assm->header_buffer), xmlBufferLength(assm->header_buffer));
	if (rc < 0) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unable to write raw header data (%d).", rc);
		goto error_free_writer;
	}
	
	/* Start the sync body */
	if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_SYNCBODY, error))
		goto error_free_writer;

	// FIXME: why do we need 20 extra bytes here?
	// FIXME: buffersize is never read !!!
	buffersize += 20 + xmlBufferLength(assm->header_buffer);
	
	/* Add the statuses */
	GList *b = NULL;
	SmlBool missingstatus = FALSE;
	smlTrace(TRACE_INTERNAL, "%s: Now adding %i statuses",
		__func__, g_list_length(assm->statuses));
	for (b = assm->statuses; b; b = b->next) {
		SmlXmlAssemblerStatus *status = b->data;
		if (!status->buffer || xmlBufferLength(status->buffer) == 0) {
			if (status->cmdRef == 0 && check) {
				smlErrorSet(error, SML_ERROR_GENERIC, "Reserved status 0 has not been added");
				goto error_free_writer;
			}
			smlTrace(TRACE_INTERNAL, "%s: Reserved status %i is missing",
				__func__, status->cmdRef);
			missingstatus = TRUE;
			break;
		}

		buffersize += xmlBufferLength(status->buffer);
		//if (maxsize && buffersize > maxsize)
		//	break;
		rc = xmlTextWriterWriteRawLen(assm->writer, xmlBufferContent(status->buffer), xmlBufferLength(status->buffer));
		if (rc < 0) {
			smlErrorSet(error, SML_ERROR_GENERIC, "Unable to write raw status data (%d - %d of %d: %s).",
				rc, xmlBufferLength(status->buffer),
				buffersize, xmlBufferContent(status->buffer));
			goto error_free_writer;
		}
	}
	
	/* We cannot have missing statuses when its final. And we cannot add commands */
	if (missingstatus && final && check) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Reserved status has not been added");
		goto error_free_writer;
	}
	
	/* Add the commands. Parent commands are added in the order they were added. */
	if (!smlXmlAssemblerAddChildren(assm, assm->commands, error))
		goto error_free_writer;
	
	if (final) {
		smlTrace(TRACE_INTERNAL, "%s: setting final", __func__);
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_FINAL, "", error))
			goto error_free_writer;
	}
	
	/* Close syncbody */
	if (!_smlXmlAssemblerEndNode(assm, error))
		goto error_free_writer;
	
	/* Close syncml */
	if (!_smlXmlAssemblerEndNode(assm, error))
		goto error_free_writer;
	
	if (xmlTextWriterEndDocument(assm->writer) < 0) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unable to end writer");
		goto error;
	}
	
	
	xmlFreeTextWriter(assm->writer);
	assm->writer = NULL;

	// the buffer was created with xmlBufferCreateSize
	// so we must free it via the libxml API and not via smlSafeCFree
	*size = xmlBufferLength(buffer);
	*data = g_strndup((const char *) xmlBufferContent(buffer), *size);
	xmlBufferFree(buffer);
	if (end) {
	 	if (final && !assm->commands)
	 		*end = TRUE;
	 	else
	 		*end = FALSE;
	}
	smlTrace(TRACE_INTERNAL, "%s: Message Assembled: %s",
		__func__, VA_STRING(*data));

	g_mutex_unlock(assm->mutex);
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error_free_writer:
	xmlFreeTextWriter(assm->writer);
	assm->writer = NULL;
error_free_buffer:
	xmlBufferFree(buffer);
error:
	g_mutex_unlock(assm->mutex);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

static unsigned int calc_list(GList *list)
{
	unsigned int size = 0;
	
	for (; list; list = list->next) {
		SmlXmlAssemblerCommand *cmd = list->data;
		size += 15;
		if (cmd->nodeType != SML_ASSEMBLER_NODE_CLOSE) {
			size += xmlBufferLength(cmd->buffer);
			if (cmd->children)
				size += calc_list(cmd->children);
		}
	}
	
	return size;
}

SmlBool smlXmlAssemblerRun(SmlXmlAssembler *assm, char **data, unsigned int *size, SmlBool *end, SmlBool final, unsigned int maxsize, SmlError **error)
{
	CHECK_ERROR_REF
	SmlBool ans = smlXmlAssemblerRunFull(assm, data, size, end, final, TRUE, maxsize, error);

	if (ans)
		smlLog("sent-%i.xml", *data, *size);
	else
		smlTrace(TRACE_ERROR, "%s - %s", __func__, smlErrorPrint(error));

	return ans;
}

unsigned int smlXmlAssemblerCheckSize(SmlXmlAssembler *assm, SmlBool headeronly, SmlError **error)
{
	CHECK_ERROR_REF
	smlAssert(assm);
	SmlXmlAssemblerStatus *status = NULL;
	unsigned int size = 0;
	
	/* Add the size if the syncml tags etc */
	size += 20;
	
	/* Add the size of the header */
	if (assm->header_buffer)
		size += xmlBufferLength(assm->header_buffer);
	
	if (!headeronly) {
		/* Add the size of the status */
		GList *b = NULL;
		for (b = assm->statuses; b; b = b->next) {
			status = b->data;
			if (!status->buffer)
				break;
			
			size += xmlBufferLength(status->buffer);
		}
			
		/* Add the size of the commands */
		size += calc_list(assm->commands);
	}
	
	return size;
}

SmlBool smlXmlAssemblerNextCmdRef(SmlXmlAssembler *assm, unsigned int *cmdRef, unsigned int *msgRef)
{
	smlAssert(assm);
	smlAssert(cmdRef);
	smlAssert(msgRef);
	SmlXmlAssemblerStatus *status = NULL;
	
	GList *b = NULL;
	for (b = assm->statuses; b; b = b->next) {
		status = b->data;
		if (!status->buffer) {
			*cmdRef = status->cmdRef;
			*msgRef = status->msgRef;
			return TRUE;
		}
	}
	
	return FALSE;
}

/** @brief Creates a new XML assembler
 * 
 * @param session The session for which to create the assembler
 * @param error A pointer to an error struct
 * @return The new assembler or NULL in the case of an error
 * 
 */
SmlXmlAssembler *smlXmlAssemblerNew(SmlAssembler *assembler, SmlAssemblerFunctions *functions, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assembler, functions, error);
	CHECK_ERROR_REF
	
	SmlXmlAssembler *assm = smlTryMalloc0(sizeof(SmlXmlAssembler), error);
	if (!assm)
		goto error;
	assm->assembler = assembler;
	assm->mutex = g_mutex_new();
	if (!assm->mutex)
		goto error_mutex;
	
	functions->start = (SmlAssemblerStartFunction)smlXmlAssemblerStart;
	functions->free = (SmlAssemblerFreeFunction)smlXmlAssemblerFree;
	functions->run = (SmlAssemblerRunFunction)smlXmlAssemblerRun;
	functions->end = (SmlAssemblerEndFunction)smlXmlAssemblerEnd;
	functions->add_header = (SmlAssemblerHeaderFunction)smlXmlAssemblerAddHeader;
	functions->start_cmd = (SmlAssemblerStartCommandFunction)smlXmlAssemblerStartCommand;
	functions->end_cmd = (SmlAssemblerEndCommandFunction)smlXmlAssemblerEndCommand;
	functions->rem_cmd = (SmlAssemblerRemCommandFunction)smlXmlAssemblerRemCommand;
	functions->add_status = (SmlAssemblerStatusFunction)smlXmlAssemblerAddStatus;
	functions->rem_status = (SmlAssemblerRemStatusFunction)smlXmlAssemblerRemStatus;
	functions->reserve_status = (SmlAssemblerReserveStatusFunction)smlXmlAssemblerReserveStatus;
	functions->missing_status = (SmlAssemblerStatusMissingFunction)smlXmlAssemblerMissingStatus;
	functions->check_size = (SmlAssemblerCheckFunction)smlXmlAssemblerCheckSize;
	functions->next_cmdref = (SmlAssemblerNextCmdRefFunction)smlXmlAssemblerNextCmdRef;
	functions->flush = (SmlAssemblerFlushFunction)smlXmlAssemblerFlush;
	functions->restore_cmds = (SmlAssemblerRestoreCommandsFunction)smlXmlAssemblerRestoreCommands;
	
	smlTrace(TRACE_EXIT, "%s: %p", __func__, assm);
	return assm;

error_mutex:
	smlSafeFree((gpointer *)&assm);
	smlErrorSet(error, SML_ERROR_GENERIC, "%s - Cannot create new mutex.", __func__);
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return NULL;
}

static SmlBool _smlXmlDevInfDataStoreAssembleRxTx(SmlXmlAssembler *assm, const char *element, const char *cttype, const char *version, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %s, %s, %s, %p)", __func__, assm, VA_STRING(element), VA_STRING(cttype), VA_STRING(version), error);
	CHECK_ERROR_REF
	smlAssert(assm);
	smlAssert(element);
	smlAssert(cttype);
	
	if (!_smlXmlAssemblerStartNode(assm, element, error))
		goto error;
	
	if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_CTTYPE, cttype, error))
		goto error;
	
	if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERCT, version, error))
		goto error;
	
	if (!_smlXmlAssemblerEndNode(assm, error))
		goto error;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
	
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

static SmlBool _smlXmlDevInfDataStoreAssembleCTCap(
			SmlXmlAssembler *assm,
			const SmlDevInfCTCap *ctcap,
			SmlBool flat,
			SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, assm, ctcap, error);
	CHECK_ERROR_REF
	smlAssert(assm);
	smlAssert(ctcap);

	if (!flat)
	{
		// start CTCap
		if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_CTCAP, error))
			goto error;
	}

	// add CTType
	if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_CTTYPE, ctcap->ct->cttype, error))
		goto error;

	// add VerCT
	// SyncML 1.0 and 1.1 does not support VerCT in CTCap
	// because this info is in the VERSION property.
	if (!flat)
	{
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERCT, ctcap->ct->verct, error))
			goto error;
	}

	// add properties
	GList *hprop = NULL;
	for (hprop = ctcap->properties; hprop; hprop = hprop->next)
	{
		SmlDevInfProperty *property = hprop->data;

		if (!flat)
		{
			// add Property
			if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_PROPERTY, error))
				goto error;
		}

		// add PropName
		if (property->propName != NULL &&
		    !_smlXmlAssemblerAddString(assm, SML_ELEMENT_PROPNAME, property->propName, error))
			goto error;

		// add DataType
		if (property->dataType != NULL &&
		    !_smlXmlAssemblerAddString(assm, SML_ELEMENT_DATATYPE, property->dataType, error))
			goto error;

		// add MaxOccur
		if (property->maxOccur > 0 &&
		    !_smlXmlAssemblerAddID(assm, SML_ELEMENT_MAXOCCUR, property->maxOccur, error))
			goto error;

		// add MaxSize
		if (property->maxSize > 0) {
			/* There was a change of the element name in OMA DS DevInf 1.2. */
			if (flat) {
				if (!_smlXmlAssemblerAddID(
						assm,
						SML_ELEMENT_SIZE,
						property->maxSize,
						error))
					goto error;
			} else {
				if (!_smlXmlAssemblerAddID(
						assm,
						SML_ELEMENT_MAXSIZE,
						property->maxSize,
						error))
					goto error;
			}
		}

		// add NoTruncate
		if (property->noTruncate)
		{
			if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_NOTRUNCATE, error))
				goto error;
			if (!_smlXmlAssemblerEndNode(assm, error))
				goto error;
		}

		// add DisplayName
		if (property->displayName != NULL &&
		    !_smlXmlAssemblerAddString(assm, SML_ELEMENT_DISPLAYNAME, property->displayName, error))
			goto error;

		// add values
		GList *hvalue = NULL;
		for (hvalue = property->valEnums; hvalue; hvalue = hvalue->next)
		{
			char *valEnum = hvalue->data;

			// add ValEnum
		    	if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VALENUM, valEnum, error))
				goto error;
			
		} // end values for loop

		// add parameters
		GList *hparam = NULL;
		for (hparam = property->propParams; hparam; hparam = hparam->next)
		{
			SmlDevInfPropParam *param = hparam->data;

			if (!flat)
			{
				// add PropParam
				if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_PROPPARAM, error))
					goto error;
			}

			// add ParamName
			if (param->paramName != NULL &&
			    !_smlXmlAssemblerAddString(assm, SML_ELEMENT_PARAMNAME, param->paramName, error))
				goto error;

			// add DataType
			if (param->dataType != NULL &&
			    !_smlXmlAssemblerAddString(assm, SML_ELEMENT_DATATYPE, param->dataType, error))
				goto error;

			// add DisplayName
			if (param->displayName != NULL &&
		    	    !_smlXmlAssemblerAddString(assm, SML_ELEMENT_DISPLAYNAME, param->displayName, error))
				goto error;

			// add values
			hvalue = NULL;
			for (hvalue = param->valEnums; hvalue; hvalue = hvalue->next)
			{
				char *valEnum = hvalue->data;

				// add ValEnum
		    		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VALENUM, valEnum, error))
					goto error;
			
			} // end values for loop

			if (!flat)
			{
				// end PropParam
				if (!_smlXmlAssemblerEndNode(assm, error))
					goto error;
			}

		} // end params for loop

		if (!flat)
		{
			// end Property
			if (!_smlXmlAssemblerEndNode(assm, error))
				goto error;
		}

	} //end of ctcap->properties for loop

	if (!flat)
	{
		// end CTCap
		if (!_smlXmlAssemblerEndNode(assm, error))
			goto error;
	}

	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
	
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

static SmlBool _smlXmlDevInfDataStoreAssemble(
			SmlXmlAssembler *assm,
			SmlDevInfDataStore *datastore,
			SmlDevInf *devinf,
			SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, assm, datastore, devinf, error);
	CHECK_ERROR_REF
	smlAssert(datastore);
	smlAssert(assm);

	GList *contentTypes = NULL;
	
	if (!datastore->sourceref) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Missing sourceref");
		goto error;
	}
	
	if (!datastore->rxPrefContentType) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Missing rx-pref");
		goto error;
	}
	
	if (!datastore->txPrefContentType) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Missing tx-pref");
		goto error;
	}
	
	if (!datastore->synccap) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No sync capabilities");
		goto error;
	}
	
	// Datastore
	if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_DATASTORE, error))
		goto error;
	
	//SourceRef
	if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SOURCEREF, datastore->sourceref, error))
		goto error;
	
	//displayname
	if (datastore->displayname) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_DISPLAYNAME, datastore->displayname, error))
			goto error;
	}
	
	//maxguidsize
	if (datastore->maxGUIDSize) {
		if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_MAXGUIDSIZE, datastore->maxGUIDSize, error))
			goto error;
	}
	
	//rx-pref
	if (!_smlXmlDevInfDataStoreAssembleRxTx(assm, SML_ELEMENT_RXPREF, datastore->rxPrefContentType, datastore->rxPrefVersion, error))
		goto error;
	contentTypes = g_list_append(
				contentTypes,
				smlDevInfNewContentType(
					datastore->rxPrefContentType,
					datastore->rxPrefVersion, error));
			
	//rx
	if (datastore->rx) {
		GList *hl;
		for (hl = datastore->rx; hl; hl = hl->next)
		{
			SmlDevInfContentType *ct = hl->data;
			if (!_smlXmlDevInfDataStoreAssembleRxTx(assm, SML_ELEMENT_RX, ct->cttype, ct->verct, error))
				goto error;
			contentTypes = g_list_append(
					contentTypes,
					smlDevInfNewContentType(ct->cttype, ct->verct, error));
		}
	}
	
	//tx-pref
	if (!_smlXmlDevInfDataStoreAssembleRxTx(assm, SML_ELEMENT_TXPREF, datastore->txPrefContentType, datastore->txPrefVersion, error))
		goto error;
	contentTypes = g_list_append(
				contentTypes,
				smlDevInfNewContentType(
					datastore->txPrefContentType,
					datastore->txPrefVersion, error));
			
	//tx
	if (datastore->tx) {
		GList *hl;
		for (hl = datastore->tx; hl; hl = hl->next)
		{
			SmlDevInfContentType *ct = hl->data;
			if (!_smlXmlDevInfDataStoreAssembleRxTx(assm, SML_ELEMENT_TX, ct->cttype, ct->verct, error))
				goto error;
			contentTypes = g_list_append(
					contentTypes,
					smlDevInfNewContentType(ct->cttype, ct->verct, error));
		}
	}

	// CTCap (if SyncML version 1.2 device info)
	if (devinf->version >= SML_DEVINF_VERSION_12)
	{
		GList *hct = NULL;
		for (hct = contentTypes; hct; hct = hct->next) {
			SmlDevInfContentType *ct = hct->data;
			const SmlDevInfCTCap *ctcap = smlDevInfGetCTCap(devinf, ct);
			if (ctcap != NULL)
			{
				// we found a matching CTCap
				// so let's dump it
				if (!_smlXmlDevInfDataStoreAssembleCTCap(assm, ctcap, FALSE, error))
					goto error;
			} else {
				// we have a content type without CTCap
				// this is a real source of trouble
				// WARNING: should we fail on this issue?
				smlTrace(TRACE_INTERNAL, "%s: found a content type (%s %d) without CTCap",
					 __func__, VA_STRING(ct->cttype), ct->verct);
			}
		} // end of contentTypes for loop
	} // end of SML_DEVINF_VERSION_12

	// cleanup list of content types
	while(contentTypes)
	{
		SmlDevInfContentType *ct = contentTypes->data;
		contentTypes = g_list_remove(contentTypes, ct);
		smlDevInfFreeContentType(ct);
	}
	
	//Dsmem
	if (datastore->maxmem || datastore->maxid) {
		//Dsmem
		if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_DSMEM, error))
			goto error;
		
		//shared
		if (datastore->sharedMem) {
			if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SHAREDMEM, "", error))
				goto error;
		}
		
		//maxid
		if (datastore->maxid) {
			if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_MAXID, datastore->maxid, error))
				goto error;
		}
		
		//maxmem
		if (datastore->maxmem) {
			if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_MAXMEM, datastore->maxmem, error))
				goto error;
		}
		
		//DsMem
		if (!_smlXmlAssemblerEndNode(assm, error))
			goto error;
	}
	
	//SyncCap
	if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_SYNCCAP, error))
		goto error;
	
	//SyncTypes
	if (smlDevInfDataStoreGetSyncCap(datastore, SML_DEVINF_SYNCTYPE_TWO_WAY)) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "1", error))
			goto error;
	}
	if (smlDevInfDataStoreGetSyncCap(datastore, SML_DEVINF_SYNCTYPE_SLOW_SYNC)) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "2", error))
			goto error;
	}
	if (smlDevInfDataStoreGetSyncCap(datastore, SML_DEVINF_SYNCTYPE_ONE_WAY_FROM_CLIENT)) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "3", error))
			goto error;
	}
	if (smlDevInfDataStoreGetSyncCap(datastore, SML_DEVINF_SYNCTYPE_REFRESH_FROM_CLIENT)) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "4", error))
			goto error;
	}
	if (smlDevInfDataStoreGetSyncCap(datastore, SML_DEVINF_SYNCTYPE_ONE_WAY_FROM_SERVER)) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "5", error))
			goto error;
	}
	if (smlDevInfDataStoreGetSyncCap(datastore, SML_DEVINF_SYNCTYPE_REFRESH_FROM_SERVER)) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "6", error))
			goto error;
	}
	if (smlDevInfDataStoreGetSyncCap(datastore, SML_DEVINF_SYNCTYPE_SERVER_ALERTED_SYNC)) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "7", error))
			goto error;
	}
	
	//SyncCap
	if (!_smlXmlAssemblerEndNode(assm, error))
		goto error;
		
	//DataStore
	if (!_smlXmlAssemblerEndNode(assm, error))
		goto error;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
	
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

SmlBool smlXmlDevInfAssemble(SmlDevInf *devinf, SmlDevInfVersion version, char **data, unsigned int *size, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %i, %p, %p, %p)", __func__, devinf, version, data, size, error);
	CHECK_ERROR_REF
	smlAssert(devinf);
	smlAssert(data);
	smlAssert(size);

	// sometime devinf->version is empty
	// both version fields should be identical
	smlTrace(TRACE_INTERNAL, "%s: devinf version: %i, version: %i", __func__, devinf->version, version);
	if (devinf->version == SML_DEVINF_VERSION_UNKNOWN)
		devinf->version = version;
	if (version == SML_DEVINF_VERSION_UNKNOWN)
		version = devinf->version;
	smlAssert(devinf->version == version);

	SmlXmlAssembler *assm = smlTryMalloc0(sizeof(SmlXmlAssembler), error);
	if (!assm)
		goto error;
	
	/* We first start a new parent writer that will write our command into a buffer */
	xmlBuffer *buffer = xmlBufferCreateSize(BUFFER_SIZE);
	if (!buffer) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new buffer");
		goto error_free_assm;
	}
	
	assm->writer = xmlNewTextWriterMemory(buffer, 0);
	if (!assm->writer) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unable to create new writer");
		goto error_free_buffer;
	}
	
	//Devinf
	if (!_smlXmlAssemblerStartNodeNS(assm, SML_ELEMENT_DEVINF, SML_NAMESPACE_DEVINF, error))
		goto error_free_writer;
	
	//Verdtd
	switch (version) {
		case SML_DEVINF_VERSION_12:
			if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERDTD, "1.2", error))
				goto error_free_writer;
			break;
		case SML_DEVINF_VERSION_11:
			if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERDTD, "1.1", error))
				goto error_free_writer;
			break;
		case SML_DEVINF_VERSION_10:
			if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_VERDTD, "1.0", error))
				goto error_free_writer;
			break;
		case SML_DEVINF_VERSION_UNKNOWN:
			smlErrorSet(error, SML_ERROR_GENERIC, "Unknown devinf version");
			goto error_free_writer;
			break;
	}
	
	//Man
	if (devinf->manufacturer) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_MAN, devinf->manufacturer, error))
			goto error_free_writer;
	}
	
	//Mod
	if (devinf->model) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_MOD, devinf->model, error))
			goto error_free_writer;
	}
	
	//OEM
	if (devinf->oem) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_OEM, devinf->oem, error))
			goto error_free_writer;
	}
	
	//FwV
	if (devinf->firmwareVersion) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_FWV, devinf->firmwareVersion, error))
			goto error_free_writer;
	}
	
	//SwV
	if (devinf->softwareVersion) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SWV, devinf->softwareVersion, error))
			goto error_free_writer;
	}
	
	//HwV
	if (devinf->hardwareVersion) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_HWV, devinf->hardwareVersion, error))
			goto error_free_writer;
	}
	
	//DevID
	if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_DEVID, devinf->devid, error))
		goto error_free_writer;
		
	//Devtyp
	const char *devtype = smlDevInfDevTypeToString(devinf->devtyp, error);
	if (!devtype)
		goto error_free_writer;
		
	if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_DEVTYPE, devtype, error))
		goto error_free_writer;
	
	//UTC (>= 1.1)
	if (devinf->supportsUTC && version != SML_DEVINF_VERSION_10) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_UTC, "", error))
			goto error_free_writer;
	}
	
	//Large objs (>= 1.1)
	if (devinf->supportsLargeObjs && version != SML_DEVINF_VERSION_10) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SUPPORTLARGEOBJS, "", error))
			goto error_free_writer;
	}
	
	//Number of changes (>= 1.1)
	if (devinf->supportsNumberOfChanges && version != SML_DEVINF_VERSION_10) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SUPPORTNUMBEROFCHANGES, "", error))
			goto error_free_writer;
	}

	//Add the datastores
	GList *d = NULL;
	for (d = devinf->datastores; d; d = d->next) {
		SmlDevInfDataStore *datastore = d->data;
		if (!_smlXmlDevInfDataStoreAssemble(assm, datastore, devinf, error))
			goto error_free_writer;
	}

	// CTCap (only for SyncML 1.0 and 1.1)
	if (
	    devinf->version < SML_DEVINF_VERSION_12 &&
	    devinf->ctcaps) {
		if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_CTCAP, error))
			goto error_free_writer;
		
		for (d = devinf->ctcaps; d; d = d->next) {
			SmlDevInfCTCap *ctcap = d->data;

			if (!_smlXmlDevInfDataStoreAssembleCTCap(assm, ctcap, TRUE, error))
				goto error_free_writer;
		}
		
		if (!_smlXmlAssemblerEndNode(assm, error))
			goto error_free_writer;
	}

	//Devinf
	if (!_smlXmlAssemblerEndNode(assm, error))
		goto error_free_writer;
		
	/* Now close the buffer */
	if (xmlTextWriterEndDocument(assm->writer) < 0) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unable to end writer");
		goto error_free_writer;
	}
	
	xmlFreeTextWriter(assm->writer);
	assm->writer = NULL;
	
	*size = xmlBufferLength(buffer);
	*data = g_strndup((const char *) xmlBufferContent(buffer), *size);

	xmlBufferFree(buffer);
	
	smlSafeFree((gpointer *)&assm);

	smlTrace(TRACE_INTERNAL, "Message Assembled: %s", *data);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error_free_writer:
	xmlFreeTextWriter(assm->writer);
	assm->writer = NULL;
error_free_buffer:
	xmlBufferFree(buffer);
error_free_assm:
	smlSafeFree((gpointer *)&assm);
error:
	g_warning("%s: %s",  __func__, smlErrorPrint(error));
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

void smlXmlAssemblerRestoreCommands(SmlXmlAssembler *assm)
{
	smlTrace(TRACE_ENTRY, "%s", __func__);
	smlAssert(assm->commands == NULL);

	assm->commands = assm->last_commands;
	assm->last_commands = NULL;

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

/*@}*/
