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

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

#include "libsyncml/data_sync_api/sml_map_item_internals.h"
#include "libsyncml/data_sync_api/sml_data_sync_change_item_internals.h"
#include "sml_xml_assm_internals.h"

#include <string.h>

#define BUFFER_SIZE 500

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

static gboolean
_smlXmlAssemblerStartNodeNS (SmlXmlAssembler *assm,
                             const gchar *name,
                             const gchar *uri,
                             GError **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) {
        	g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unable to start node");
		return FALSE;
	}
	return TRUE;
}

static gboolean
_smlXmlAssemblerStartNode (SmlXmlAssembler *assm,
                           const gchar *name,
                           GError **error)
{
	CHECK_ERROR_REF
	return _smlXmlAssemblerStartNodeNS(assm, name, NULL, error);
}

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

static gboolean
_smlXmlAssemblerAddStringNS (SmlXmlAssembler *assm,
                             const gchar *name,
                             const gchar *uri,
                             const gchar *value,
                             GError **error)
{
	CHECK_ERROR_REF
	int rc = xmlTextWriterWriteElementNS(assm->writer, NULL, (xmlChar *)name, (xmlChar *)uri, (xmlChar *)value);
	if (rc < 0) {
		g_set_error(error, SML_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 gboolean
_smlXmlAssemblerAddString (SmlXmlAssembler *assm,
                           const gchar *name,
                           const gchar *value,
                           GError **error)
{
	CHECK_ERROR_REF
	int rc = xmlTextWriterWriteElement(assm->writer, (xmlChar *)name, (xmlChar *)value);
	if (rc < 0) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
		            "Unable to add pure string (%d - %s -> %s).",
		            rc, VA_STRING(name), VA_STRING(value));
		return FALSE;
	}
	return TRUE;
}

static gboolean
_smlXmlAssemblerAddData (SmlXmlAssembler *assm,
                         const gchar *name,
                         const gchar *value,
                         gsize size,
                         gboolean raw,
                         GError **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) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
		            "Unable to add data (%d).", rc);
		return FALSE;
	}
		
	if (!_smlXmlAssemblerEndNode(assm, error))
		return FALSE;
	
	return TRUE;
}

static gboolean
_smlXmlAssemblerAddID (SmlXmlAssembler *assm,
                       const gchar *name,
                       guint64 id,
                       GError **error)
{
	CHECK_ERROR_REF
	int rc = xmlTextWriterWriteFormatElement(assm->writer, (xmlChar *)name, "%lli", id);
	if (rc < 0) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
		            "Unable to add id");
		return FALSE;
	}
	return TRUE;
}

static gboolean
_smlXmlAssemblerAddIDNS (SmlXmlAssembler *assm,
                         const gchar *name,
                         const gchar *uri,
                         gsize id,
                         GError **error)
{
	CHECK_ERROR_REF
	int rc = xmlTextWriterWriteFormatElementNS(assm->writer, NULL, (xmlChar *)name, (xmlChar *)uri, "%i", id);
	if (rc < 0) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
		            "Unable to add id");
		return FALSE;
	}
	return TRUE;
}

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

	/* check the configuration */
	const char *opt = smlAssemblerGetOption(assm->assembler, "USE_LARGEOBJECTS");
	gboolean 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 gboolean
_smlAssembleMaxObjSize (SmlXmlAssembler *assm,
                        GError **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__, (*error)->message);
	return FALSE;
}

static const gchar*
_smlAssembleDevInfDevType (SmlDevInfDevTyp type,
                           GError **error)
{
	CHECK_ERROR_REF

	switch (type) {
		case SML_DEVINF_DEVTYP_PAGER:
			return SML_ELEMENT_DEVTYP_PAGER;
		case SML_DEVINF_DEVTYP_HANDHELD:
			return SML_ELEMENT_DEVTYP_HANDHELD;
		case SML_DEVINF_DEVTYP_PDA:
			return SML_ELEMENT_DEVTYP_PDA;
		case SML_DEVINF_DEVTYP_PHONE:
			return SML_ELEMENT_DEVTYP_PHONE;
		case SML_DEVINF_DEVTYP_SMARTPHONE:
			return SML_ELEMENT_DEVTYP_SMARTPHONE;
		case SML_DEVINF_DEVTYP_SERVER:
			return SML_ELEMENT_DEVTYP_SERVER;
		case SML_DEVINF_DEVTYP_WORKSTATION:
			return SML_ELEMENT_DEVTYP_WORKSTATION;
		default:
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
			            "The devince information DevTyp \"%i\" is unknown.",
			            type);
			/* fall through! */
	}
		
	return NULL;
}

gboolean
smlLocationAssemble (SmlLocation *location,
                     SmlXmlAssembler *assm,
                     const gchar *name,
                     GError **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 (name && 
	    (strcmp(SML_ELEMENT_SOURCE_PARENT, name) == 0 ||
	     strcmp(SML_ELEMENT_TARGET_PARENT, name) == 0)) {
		if (!sml_location_get_parent_uri(location)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
			            "No parent URI set");
			goto error;
		}
	
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_LOCURI, sml_location_get_parent_uri(location), error))
			goto error;
	
	} else {
		if (!sml_location_get_uri(location)) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
			            "No location URI set");
			goto error;
		}
	
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_LOCURI, sml_location_get_uri(location), error))
			goto error;
	
		if (sml_location_get_name(location)) {
			if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_LOCNAME, sml_location_get_name(location), 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__, (*error)->message);
	return FALSE;
}



gboolean
smlAnchorAssemble (SmlAnchor *anchor,
                   SmlXmlAssembler *assm,
                   GError **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) {
		g_set_error(error, SML_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__, (*error)->message);
	return FALSE;
}

gboolean
smlItemAssemble (SmlItem *item,
                 SmlXmlAssembler *assm,
                 GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, item, assm, error);
	CHECK_ERROR_REF
	smlAssert(assm);
	smlAssert(item);
	
	if (assm->moreDataSet) {
		g_set_error(error, SML_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 (smlItemGetSource(item) &&
	    sml_location_get_parent_uri(smlItemGetSource(item))) {
		if (!smlLocationAssemble(smlItemGetSource(item), assm, SML_ELEMENT_SOURCE_PARENT, error))
			goto error;
	}

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

	/* Data */
	if (smlItemHasData(item)) {
		char *data = NULL;
		gsize 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__, (*error)->message);
	return FALSE;
}

gboolean
smlChangeItemAssemble (SmlDataSyncChangeItem *item,
                       SmlXmlAssembler *assm,
                       GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, item, assm, error);
	CHECK_ERROR_REF
	smlAssert(assm);
	smlAssert(item);
	
	if (assm->moreDataSet) {
		g_set_error(error, SML_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;

	/* 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
	 */
	gboolean fillSource = TRUE;
	SmlSessionType sessionType = assm->session->sessionType;
	const char *opt = smlAssemblerGetOption(assm->assembler, "ONLY_REPLACE");
	if (sml_data_sync_change_item_get_action(item) == SML_CHANGE_ADD)
	{
		if (opt && atoi(opt) && /* remote peer supports ONLY_REPLACE */
		    sessionType == SML_SESSION_TYPE_SERVER) {
			/* only Target must be set */
			fillSource = FALSE;
		} else {
			/* only Source must be set */
			fillSource = TRUE;
		}
	} else {
		if (sessionType == SML_SESSION_TYPE_SERVER) {
			/* Target must be set */
			fillSource = FALSE;
		} else {
			/* only Source must be set */
			fillSource = TRUE;
		}
	}
	
	/* Target */
	if (!fillSource && sml_data_sync_change_item_get_location(item)) {
		if (!smlLocationAssemble(sml_data_sync_change_item_get_location(item), assm, SML_ELEMENT_TARGET, error))
			goto error;
	}

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

	/* SourceParent */
	if (fillSource && sml_data_sync_change_item_get_location(item) &&
	    sml_location_get_parent_uri(sml_data_sync_change_item_get_location(item))) {
		if (!smlLocationAssemble(sml_data_sync_change_item_get_location(item), assm, SML_ELEMENT_SOURCE_PARENT, error))
			goto error;
	}

	/* TargetParent */
	if (!fillSource && sml_data_sync_change_item_get_location(item) &&
	    sml_location_get_parent_uri(sml_data_sync_change_item_get_location(item))) {
		if (!smlLocationAssemble(sml_data_sync_change_item_get_location(item), assm, SML_ELEMENT_TARGET_PARENT, error))
			goto error;
	}

	/* Data */
	if (sml_data_sync_change_item_get_data(item)) {
		const gchar *data = sml_data_sync_change_item_get_data(item);
		gsize size = strlen(data);;
		
		if (!_smlXmlAssemblerAddData(assm, SML_ELEMENT_DATA, data, size, FALSE, error))
			goto error;
	}

	/* MoreData */
	if (sml_data_sync_change_item_get_missing_data(item)) {
		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__, (*error)->message);
	return FALSE;
}

gboolean
smlCredAssemble (SmlCred *cred,
                 SmlXmlAssembler *assm,
                 GError **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:
			g_set_error(error, SML_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:
			g_set_error(error, SML_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__, (*error)->message);
	return FALSE;
}

gboolean
smlAccessAssemble (SmlXmlAssembler *assm,
                   SmlCommand *change,
                   GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, change, error);
	CHECK_ERROR_REF
	smlAssert(change);
	smlAssert(assm);
	
	if (!change->private.access.item) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Missing item");
		goto error;
	}
	
	if (!change->private.access.item->contenttype) {
		g_set_error(error, SML_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__, (*error)->message);
	return FALSE;
}

gboolean
smlChangeAssemble (SmlXmlAssembler *assm,
                   SmlCommand *change,
                   GError **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)) {
		g_set_error(error, SML_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);
	SmlDataSyncChangeItem *item = g_list_nth_data(change->private.change.items, 0);

	if (!item) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
		            "One item of the item list is NULL.");
		goto error;
	}
	if (!sml_data_sync_change_item_get_content_type(item)) {
		g_set_error(error, SML_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, sml_data_sync_change_item_get_content_type(item), 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");
	gboolean 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)

	if (!smlChangeItemAssemble(item, assm, error))
		goto error;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}

gboolean
smlSyncAssemble (SmlXmlAssembler *assm,
                 SmlCommand *cmd,
                 GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, cmd, error);
	CHECK_ERROR_REF
	smlAssert(cmd);
	smlAssert(assm);
	
	if (!cmd->target) {
		g_set_error(error, SML_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) {
		g_set_error(error, SML_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");
	gboolean 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__, (*error)->message);
	return FALSE;
}

gboolean
smlMapItemAssemble (SmlXmlAssembler *assm,
                    SmlMapItem *item,
                    GError **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 (sml_map_item_get_remote(item)) {
		if (!smlLocationAssemble(sml_map_item_get_remote(item), assm, SML_ELEMENT_SOURCE, error))
			goto error;
	}
	
	if (sml_map_item_get_local(item)) {
		if (!smlLocationAssemble(sml_map_item_get_local(item), 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__, (*error)->message);
	return FALSE;
}

gboolean
smlMapAssemble (SmlXmlAssembler *assm,
                SmlCommand *cmd,
                GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, assm, cmd, error);
	CHECK_ERROR_REF
	smlAssert(cmd);
	smlAssert(assm);
	
	if (!cmd->target) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No target set");
		goto error;
	}
	
	if (!smlLocationAssemble(cmd->target, assm, SML_ELEMENT_TARGET, error))
		goto error;
	
	if (!cmd->source) {
		g_set_error(error, SML_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__, (*error)->message);
	return FALSE;
}

gboolean
smlAlertAssemble (SmlXmlAssembler *assm,
                  SmlCommand *cmd,
                  GError **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) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No source set");
			goto error;
		}
	}

	/* SourceParent */
	if (cmd->source && sml_location_get_parent_uri(cmd->source)) {
		if (!smlLocationAssemble(cmd->source, assm, SML_ELEMENT_SOURCE_PARENT, error))
			goto error;
	}

	/* TargetParent */
	if (cmd->target && sml_location_get_parent_uri(cmd->target)) {
		if (!smlLocationAssemble(cmd->target, assm, SML_ELEMENT_TARGET_PARENT, error))
			goto error;
	}


	/* Begin of META */

	gboolean 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__, (*error)->message);
	return FALSE;
}

gboolean
smlResultsAssemble (SmlXmlAssembler *assm,
                    SmlCommand *cmd,
                    GError **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, sml_location_get_uri(cmd->private.results.status->targetRef), error))
			goto error;
	}

	/* SourceRef */
	if (cmd->private.results.status->sourceRef) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SOURCEREF, sml_location_get_uri(cmd->private.results.status->sourceRef), 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__, (*error)->message);
	return FALSE;
}

gboolean
smlXmlAssemblerAddHeader (SmlXmlAssembler *assm,
                          SmlSession *session,
                          GError **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) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
		            "Unable to create new buffer");
		goto error;
	}
	
	assm->writer = xmlNewTextWriterMemory(assm->header_buffer, 0);
	if (!assm->writer) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unable to create new writer");
		goto error;
	}
	
	if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_SYNCHDR, error))
		goto error;
	
	if (!session->protocol) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No version set");
		goto error;
	}
	
	if (!session->version) {
		g_set_error(error, SML_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:
					g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
					            "Unknown version %d", session->version);
					goto error;
			}
			break;
		default:
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
			            "Unknown protocol");
			goto error;
	}
	
	if (session->sessionID) {
		if (!_smlXmlAssemblerAddID(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)
			{
				g_set_error(error, SML_ERROR, SML_ERROR_INTERNAL_MISCONFIGURATION,
					"MaxMsgSize must be set.");
				goto error;
			}
			if (smlSessionGetLocalMaxObjSize(session) < 1)
			{
				g_set_error(error, SML_ERROR, SML_ERROR_INTERNAL_MISCONFIGURATION,
					"MaxObjSize must be set.");
				goto error;
			}
		}
	}

	/* set MaxMsgSize if available */
	gsize 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) {
		g_set_error(error, SML_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__, (*error)->message);
	return FALSE;
}

gboolean
smlXmlAssemblerStartCommand (SmlXmlAssembler *assm,
                             gsize parentID,
                             SmlCommand *cmd,
                             GError **error)
{
	CHECK_ERROR_REF
	smlAssert(assm);
	smlAssert(cmd);

	SmlXmlAssemblerCommand *assmcmd = NULL;
	
	if (cmd->type == SML_COMMAND_TYPE_UNKNOWN) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No cmd set");
		goto error;
	}
	
	if (!cmd->cmdID) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No cmd ID set");
		goto error;
	}
	
	/* Lets see if there already was a header before */
	if (!assm->header_buffer) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Header not yet added");
		goto error;
	}
	
	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) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
		            "Unable to create new buffer");
		goto error;
	}
	
	assm->writer = xmlNewTextWriterMemory(assmcmd->buffer, 0);
	if (!assm->writer) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
		            "Unable to create new writer");
		goto error;
	}
	
	/* We start without the root node */
	if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_CMDID, cmd->cmdID, error))
		goto error;
	
	switch (cmd->type) {
		case SML_COMMAND_TYPE_ALERT:
			if (!smlAlertAssemble(assm, cmd, error))
				goto error;
			break;
		case SML_COMMAND_TYPE_SYNC:
			if (!smlSyncAssemble(assm, cmd, error))
				goto error;
			break;
		case SML_COMMAND_TYPE_ADD:
		case SML_COMMAND_TYPE_REPLACE:
		case SML_COMMAND_TYPE_DELETE:
			if (!smlChangeAssemble(assm, cmd, error))
				goto error;
			break;
		case SML_COMMAND_TYPE_PUT:
		case SML_COMMAND_TYPE_GET:
			if (!smlAccessAssemble(assm, cmd, error))
				goto error;
			break;
		case SML_COMMAND_TYPE_MAP:
			if (!smlMapAssemble(assm, cmd, error))
				goto error;
			break;
		case SML_COMMAND_TYPE_RESULTS:
			if (!smlResultsAssemble(assm, cmd, error))
				goto error;
			break;
		default:
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
			            "Unknown command type");
			goto error;
	}
	
	/* Now close the buffer */
	if (xmlTextWriterEndDocument(assm->writer) < 0) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unable to end writer");
		goto error;
	}
	
	xmlFreeTextWriter(assm->writer);
	assm->writer = NULL;
	
	/* Now we can append the buffer and the parent*/
	*appendto = g_list_append(*appendto, assmcmd);
	
	return TRUE;
error:
	if (assm->writer) {
		xmlFreeTextWriter(assm->writer);
		assm->writer = NULL;
	}
	if (assmcmd) {
		if (assmcmd->buffer) {
			xmlBufferFree(assmcmd->buffer);
			assmcmd->buffer = NULL;
		}
		smlSafeFree((gpointer *)&assmcmd);
	}
	return FALSE;
}

gboolean
smlXmlAssemblerEndCommand (SmlXmlAssembler *assm,
                           gsize parentID,
                           GError **error)
{
	CHECK_ERROR_REF
	smlAssert(assm);
	
	/* Lets see if there already was a header before */
	if (!assm->header_buffer) {
		g_set_error(error, SML_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;
}

gboolean
smlXmlAssemblerRemCommand (SmlXmlAssembler *assm,
                           gsize parentID,
                           GError **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) {
		g_set_error(error, SML_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) {
		g_set_error(error, SML_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__, (*error)->message);
	return FALSE;
}

gboolean
smlXmlAssemblerRemStatus (SmlXmlAssembler *assm,
                          GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, assm, error);
	CHECK_ERROR_REF
	smlAssert(assm);
	
	if (!assm->statuses) {
		g_set_error(error, SML_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__, (*error)->message);
	return FALSE;
}

gboolean
smlXmlAssemblerReserveStatus (SmlXmlAssembler *assm,
                              gsize cmdRef,
                              gsize msgRef,
                              gsize cmdID,
                              GError **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__, (*error)->message);
	return FALSE;
}

gboolean
smlXmlAssemblerAddStatus (SmlXmlAssembler *assm,
                          SmlStatus *status,
                          GError **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) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No msgref set");
		goto error;
	}
	
	if (status->type == SML_COMMAND_TYPE_UNKNOWN) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No cmd set");
		goto error;
	}
	
	/* Lets see if there already was a header before */
	if (!assm->header_buffer) {
		g_set_error(error, SML_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) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Status not reserved");
		goto error;
	}
	
	if (!res->cmdID) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No cmd ID set");
		goto error;
	}
	
	if (res->buffer) {
		g_set_error(error, SML_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) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unable to create new buffer");
		goto error;
	}
	
	assm->writer = xmlNewTextWriterMemory(res->buffer, 0);
	if (!assm->writer) {
		g_set_error(error, SML_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, sml_location_get_uri(status->targetRef), error))
			goto error;
	}
	
	/* SourceRef */
	if (status->sourceRef) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SOURCEREF, sml_location_get_uri(status->sourceRef), 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:
				g_set_error(error, SML_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) {
		g_set_error(error, SML_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__, (*error)->message);
	return FALSE;
}

gboolean
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 */
gsize
smlXmlAssemblerFlush (SmlXmlAssembler *assm)
{
	smlTrace(TRACE_ENTRY, "%s(%p)", __func__, assm);
	smlAssert(assm);
	
	unsigned int newid = 1;
	gboolean 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;
}

gboolean
smlXmlAssemblerStart (SmlXmlAssembler *assm,
                      SmlSession *session,
                      GError **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;
}


gboolean
smlXmlAssemblerEnd (SmlXmlAssembler *assm,
                    GError **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)) {
		g_set_error(error, SML_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__, (*error)->message);
	return FALSE;
}

gboolean
smlXmlAssemblerAddChildren (SmlXmlAssembler *assm,
                            GList *b,
                            GError **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");
	gboolean 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) {
					g_set_error(error, SML_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__, (*error)->message);
	return FALSE;
}

gboolean
smlXmlAssemblerRunFull (SmlXmlAssembler *assm,
                        gchar **data,
                        gsize *size,
                        gboolean *end,
                        gboolean final,
                        gboolean check,
                        gsize maxsize,
                        GError **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) {
		g_set_error(error, SML_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) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "No status/command available");
		goto error;
	}
	
	if (check && assm->statuses && final) {
		SmlXmlAssemblerStatus *status = assm->statuses->data;
		if (!status->buffer) {
			g_set_error(error, SML_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) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC, "Unable to create new buffer");
		goto error;
	}
	
	assm->writer = xmlNewTextWriterMemory(buffer, 0);
	if (!assm->writer) {
		g_set_error(error, SML_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) {
		g_set_error(error, SML_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:
			g_set_error(error, SML_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) {
		g_set_error(error, SML_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;
	gboolean 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) {
				g_set_error(error, SML_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) {
			g_set_error(error, SML_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) {
		g_set_error(error, SML_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) {
		g_set_error(error, SML_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__, (*error)->message);
	return FALSE;
}

static gsize calc_list(GList *list)
{
	gsize 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;
}

gboolean
smlXmlAssemblerRun (SmlXmlAssembler *assm,
                    gchar **data,
                    gsize *size,
                    gboolean *end,
                    gboolean final,
                    gsize maxsize,
                    GError **error)
{
	CHECK_ERROR_REF
	gboolean 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__, (*error)->message);

	return ans;
}

gsize
smlXmlAssemblerCheckSize (SmlXmlAssembler *assm,
                          gboolean headeronly,
                          GError **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;
}

gboolean
smlXmlAssemblerNextCmdRef (SmlXmlAssembler *assm,
                           gsize *cmdRef,
                           gsize *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,
                    GError **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);
	g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
	            "%s - Cannot create new mutex.", __func__);
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return NULL;
}

static gboolean
_smlXmlDevInfDataStoreAssembleRxTx (SmlXmlAssembler *assm,
                                    const gchar *element,
                                    const gchar *cttype,
                                    const gchar *version,
                                    GError **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__, (*error)->message);
	return FALSE;
}

static gboolean
_smlXmlDevInfDataStoreAssembleCTCap (SmlXmlAssembler *assm,
                                     SmlDevInfCTCap *ctcap,
                                     gboolean flat,
                                     GError **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, sml_dev_inf_content_type_get_cttype(sml_dev_inf_ctcap_get_content_type(ctcap)), 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, sml_dev_inf_content_type_get_verct(sml_dev_inf_ctcap_get_content_type(ctcap)), error))
			goto error;
	}

	// add properties
	gsize ctcapPropCount = sml_dev_inf_ctcap_num_properties(ctcap);
	gsize n;
	for (n = 0; n < ctcapPropCount; n++)
	{
		SmlDevInfProperty *property = sml_dev_inf_ctcap_get_nth_property(ctcap, n);

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

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

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

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

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

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

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

		// add values
		gsize propValEnumCount = sml_dev_inf_property_num_val_enums(property);
		guint n;
		for (n = 0; n < propValEnumCount; n++)
		{
			const char *valEnum = sml_dev_inf_property_get_nth_val_enum(property, n);

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

		// add parameters
		gsize propParamCount = sml_dev_inf_property_num_params(property);
		smlTrace(TRACE_INTERNAL, "%s: property params ::= %d", __func__, propParamCount);
		for (n = 0; n < propParamCount; n++)
		{
			smlTrace(TRACE_INTERNAL, "%s: property param ::= %d", __func__, n);
			SmlDevInfPropParam *param = sml_dev_inf_property_get_nth_param(property, n);
			smlAssert(param);

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

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

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

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

			// add values
			gsize propParamValEnumCount = sml_dev_inf_prop_param_num_val_enums(param);
			guint nn;
			for (nn=0; nn < propParamValEnumCount; nn++)
			{
				const char *valEnum = sml_dev_inf_prop_param_get_nth_val_enum(param, nn);

				// 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__, (*error)->message);
	return FALSE;
}

static gboolean
_smlXmlDevInfDataStoreAssemble (SmlXmlAssembler *assm,
                                SmlDevInfDataStore *datastore,
                                SmlDevInf *devinf,
                                GError **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 (!sml_dev_inf_data_store_is_compliant(datastore, error))
		goto error;
	
	// Datastore
	if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_DATASTORE, error))
		goto error;
	
	//SourceRef
	if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SOURCEREF, sml_dev_inf_data_store_get_source_ref(datastore), error))
		goto error;
	
	//displayname
	if (sml_dev_inf_data_store_get_display_name(datastore)) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_DISPLAYNAME, sml_dev_inf_data_store_get_display_name(datastore), error))
			goto error;
	}
	
	//maxguidsize
	if (sml_dev_inf_data_store_get_max_guid_size(datastore)) {
		if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_MAXGUIDSIZE, sml_dev_inf_data_store_get_max_guid_size(datastore), error))
			goto error;
	}
	
	//rx-pref
	SmlDevInfContentType *ct = sml_dev_inf_data_store_get_rx_pref(datastore);
	if (!_smlXmlDevInfDataStoreAssembleRxTx(assm, SML_ELEMENT_RXPREF, sml_dev_inf_content_type_get_cttype(ct), sml_dev_inf_content_type_get_verct(ct), error))
		goto error;
	g_object_ref(ct);
	contentTypes = g_list_append(contentTypes, ct);
			
	//rx
	gsize length = sml_dev_inf_data_store_num_rx(datastore);
	gsize i;
	for (i = 0; i < length; i++)
	{
		ct = sml_dev_inf_data_store_get_nth_rx(datastore, i);
		if (!_smlXmlDevInfDataStoreAssembleRxTx(assm, SML_ELEMENT_RX, sml_dev_inf_content_type_get_cttype(ct), sml_dev_inf_content_type_get_verct(ct), error))
			goto error;
		g_object_ref(ct);
		contentTypes = g_list_append(contentTypes, ct);
	}
	
	//tx-pref
	ct = sml_dev_inf_data_store_get_tx_pref(datastore);
	if (!_smlXmlDevInfDataStoreAssembleRxTx(assm, SML_ELEMENT_TXPREF, sml_dev_inf_content_type_get_cttype(ct), sml_dev_inf_content_type_get_verct(ct), error))
		goto error;
	g_object_ref(ct);
	contentTypes = g_list_append(contentTypes, ct);
			
	//tx
	length = sml_dev_inf_data_store_num_tx(datastore);
	for (i = 0; i < length; i++)
	{
		ct = sml_dev_inf_data_store_get_nth_tx(datastore, i);
		if (!_smlXmlDevInfDataStoreAssembleRxTx(assm, SML_ELEMENT_TX, sml_dev_inf_content_type_get_cttype(ct), sml_dev_inf_content_type_get_verct(ct), error))
			goto error;
		g_object_ref(ct);
		contentTypes = g_list_append(contentTypes, ct);
	}

	// CTCap (if SyncML version 1.2 device info)
	if (sml_dev_inf_get_ver_dtd(devinf) >= SML_DEVINF_VERSION_12)
	{
		GList *hct = NULL;
		for (hct = contentTypes; hct; hct = hct->next) {
			ct = hct->data;
			SmlDevInfCTCap *ctcap = sml_dev_inf_get_ctcap(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(sml_dev_inf_content_type_get_cttype(ct)), VA_STRING(sml_dev_inf_content_type_get_verct(ct)));
			}
		} // 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);
		g_object_unref(ct);
	}
	
	//Dsmem
	if (sml_dev_inf_data_store_get_max_mem(datastore) || sml_dev_inf_data_store_get_max_id(datastore)) {
		//Dsmem
		if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_DSMEM, error))
			goto error;
		
		//shared
		if (sml_dev_inf_data_store_get_shared_mem(datastore)) {
			if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SHAREDMEM, "", error))
				goto error;
		}
		
		//maxid
		if (sml_dev_inf_data_store_get_max_id(datastore)) {
			if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_MAXID, sml_dev_inf_data_store_get_max_id(datastore), error))
				goto error;
		}
		
		//maxmem
		if (sml_dev_inf_data_store_get_max_mem(datastore)) {
			if (!_smlXmlAssemblerAddID(assm, SML_ELEMENT_MAXMEM, sml_dev_inf_data_store_get_max_mem(datastore), error))
				goto error;
		}
		
		//DsMem
		if (!_smlXmlAssemblerEndNode(assm, error))
			goto error;
	}
	
	//SyncCap
	if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_SYNCCAP, error))
		goto error;
	
	//SyncTypes
	if (sml_dev_inf_data_store_get_sync_cap(datastore, SML_DEVINF_SYNCTYPE_TWO_WAY)) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "1", error))
			goto error;
	}
	if (sml_dev_inf_data_store_get_sync_cap(datastore, SML_DEVINF_SYNCTYPE_SLOW_SYNC)) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "2", error))
			goto error;
	}
	if (sml_dev_inf_data_store_get_sync_cap(datastore, SML_DEVINF_SYNCTYPE_ONE_WAY_FROM_CLIENT)) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "3", error))
			goto error;
	}
	if (sml_dev_inf_data_store_get_sync_cap(datastore, SML_DEVINF_SYNCTYPE_REFRESH_FROM_CLIENT)) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "4", error))
			goto error;
	}
	if (sml_dev_inf_data_store_get_sync_cap(datastore, SML_DEVINF_SYNCTYPE_ONE_WAY_FROM_SERVER)) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "5", error))
			goto error;
	}
	if (sml_dev_inf_data_store_get_sync_cap(datastore, SML_DEVINF_SYNCTYPE_REFRESH_FROM_SERVER)) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SYNCTYPE, "6", error))
			goto error;
	}
	if (sml_dev_inf_data_store_get_sync_cap(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__, (*error)->message);
	return FALSE;
}

gboolean
smlXmlDevInfAssemble (SmlDevInf *devinf,
                      SmlDevInfVersion version,
                      gchar **data,
                      gsize *size,
                      GError **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__, sml_dev_inf_get_ver_dtd(devinf), version);
	if (sml_dev_inf_get_ver_dtd(devinf) == SML_DEVINF_VERSION_UNKNOWN)
		sml_dev_inf_set_ver_dtd(devinf, version);
	if (version == SML_DEVINF_VERSION_UNKNOWN)
		version = sml_dev_inf_get_ver_dtd(devinf);
	smlAssert(sml_dev_inf_get_ver_dtd(devinf) == 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) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
		            "Unable to create new buffer");
		goto error_free_assm;
	}
	
	assm->writer = xmlNewTextWriterMemory(buffer, 0);
	if (!assm->writer) {
		g_set_error(error, SML_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:
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
			            "Unknown devinf version");
			goto error_free_writer;
			break;
	}
	
	//Man
	if (sml_dev_inf_get_man(devinf)) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_MAN, sml_dev_inf_get_man(devinf), error))
			goto error_free_writer;
	}
	
	//Mod
	if (sml_dev_inf_get_mod(devinf)) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_MOD, sml_dev_inf_get_mod(devinf), error))
			goto error_free_writer;
	}
	
	//OEM
	if (sml_dev_inf_get_oem(devinf)) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_OEM, sml_dev_inf_get_oem(devinf), error))
			goto error_free_writer;
	}
	
	//FwV
	if (sml_dev_inf_get_fwv(devinf)) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_FWV, sml_dev_inf_get_fwv(devinf), error))
			goto error_free_writer;
	}
	
	//SwV
	if (sml_dev_inf_get_swv(devinf)) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SWV, sml_dev_inf_get_swv(devinf), error))
			goto error_free_writer;
	}
	
	//HwV
	if (sml_dev_inf_get_hwv(devinf)) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_HWV, sml_dev_inf_get_hwv(devinf), error))
			goto error_free_writer;
	}
	
	//DevID
	if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_DEVID, sml_dev_inf_get_dev_id(devinf), error))
		goto error_free_writer;
		
	//Devtyp
	const char *devtype = _smlAssembleDevInfDevType(sml_dev_inf_get_dev_typ(devinf), error);
	if (!devtype)
		goto error_free_writer;
		
	if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_DEVTYP, devtype, error))
		goto error_free_writer;
	
	//UTC (>= 1.1)
	if (sml_dev_inf_get_support_utc(devinf) && version != SML_DEVINF_VERSION_10) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_UTC, "", error))
			goto error_free_writer;
	}
	
	//Large objs (>= 1.1)
	if (sml_dev_inf_get_support_large_objs(devinf) && version != SML_DEVINF_VERSION_10) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SUPPORTLARGEOBJS, "", error))
			goto error_free_writer;
	}
	
	//Number of changes (>= 1.1)
	if (sml_dev_inf_get_support_number_of_changes(devinf) && version != SML_DEVINF_VERSION_10) {
		if (!_smlXmlAssemblerAddString(assm, SML_ELEMENT_SUPPORTNUMBEROFCHANGES, "", error))
			goto error_free_writer;
	}

	//Add the datastores
	gsize length = sml_dev_inf_num_data_stores(devinf);
	gsize n;
	for (n = 0; n < length; n++) {
		SmlDevInfDataStore *datastore = sml_dev_inf_get_nth_data_store(devinf, n);
		if (!_smlXmlDevInfDataStoreAssemble(assm, datastore, devinf, error))
			goto error_free_writer;
	}

	// CTCap (only for SyncML 1.0 and 1.1)
	if (
	    sml_dev_inf_get_ver_dtd(devinf) < SML_DEVINF_VERSION_12 &&
	    sml_dev_inf_num_ctcaps(devinf) > 0) {
		if (!_smlXmlAssemblerStartNode(assm, SML_ELEMENT_CTCAP, error))
			goto error_free_writer;
		
		length = sml_dev_inf_num_ctcaps(devinf);
		for (n = 0; n < length; n++) {
			SmlDevInfCTCap *ctcap = sml_dev_inf_get_nth_ctcap(devinf, n);

			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) {
		g_set_error(error, SML_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__, (*error)->message);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	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__);
}

/*@}*/
