/*
 * libsyncml - A syncml protocol implementation
 * Copyright (C) 2005  Armin Bauer <armin.bauer@opensync.org>
 * Copyright (C) 2008  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 "tests/support.h"

#include <libsyncml/sml_session_internals.h>
#include <libsyncml/sml_elements_internals.h>
#include <libsyncml/sml_command_internals.h>
#include <libsyncml/sml_transport_internals.h>
#include <libsyncml/objects/sml_auth.c>

typedef struct SmlSessionThread {
	GMainContext *context;
	GSourceFuncs *functions;
	GSourceFuncs *data_functions;
	GSource *data_source;
	GSource *session_source;
	SmlThread *thread;
	SmlSession *session;
	SmlTransportData **data;
} SmlSessionThread;

gint current_msg_id = 1;
gint current_cmd_id = 1;

gint data_received = 0;

int start_commands_received = 0;
int end_commands_received = 0;
int finals_received = 0;
int end_received = 0;
int errors_received = 0;
int numflushes = 0;

int num_changes = 0;

SmlTransportData *to_session = NULL;
SmlTransportData *to_session2 = NULL;
gint numreplies = 0;

GMutex *mutex = NULL;

char *item_data = NULL;
unsigned int item_size = 0;

unsigned int defaultMaxMsgSize = 10240;
unsigned int defaultMaxObjSize = 1024000;

void init_testbed()
{
	current_msg_id = 1;
	current_cmd_id = 1;

	data_received = 0;

	start_commands_received = 0;
	end_commands_received = 0;
	finals_received = 0;
	end_received = 0;
	errors_received = 0;
	numflushes = 0;

	num_changes = 0;

	to_session = NULL;
	to_session2 = NULL;
	numreplies = 0;

	mutex = NULL;

	item_data = NULL;
	item_size = 0;

	// if (!g_thread_supported ()) g_thread_init (NULL);
	// g_type_init();

	setup_testbed(NULL);
}

void cleanup_testbed()
{
	if (to_session) {
		smlTransportDataDeref(to_session);
		to_session = NULL;
	}
	if (to_session2) {
		smlTransportDataDeref(to_session2);
		to_session2 = NULL;
	}
}

gboolean _session_prepare(GSource *source, gint *timeout_)
{
	smlTrace(TRACE_INTERNAL, "%s(%p, %p)", __func__, source, timeout_);
	*timeout_ = 1;
	return FALSE;
}

gboolean _session_check(GSource *source)
{
	SmlSession *session = *((SmlSession **)(source + 1));
	return smlSessionCheck(session);
}

gboolean _session_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
{
	smlTrace(TRACE_INTERNAL, "%s(%p, %p, %p)", __func__, source, callback, user_data);
	SmlSession *session = user_data;
	
	while (smlSessionCheck(session))
		smlSessionDispatch(session);
	
	return TRUE;
}
gboolean _data_prepare(GSource *source, gint *timeout_)
{
	smlTrace(TRACE_INTERNAL, "%s(%p, %p)", __func__, source, timeout_);
	*timeout_ = 1;
	return FALSE;
}

gboolean _data_check(GSource *source)
{
	SmlSessionThread *thread = *((SmlSessionThread **)(source + 1));
	smlAssert(thread != NULL);
	smlAssert(thread->data != NULL);
	
	if (*(thread->data))
		return TRUE;
	
	return FALSE;
}

gboolean _data_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
{
	smlTrace(TRACE_INTERNAL, "%s(%p. %p, %p)", __func__, source, callback, user_data);
	SmlSessionThread *check = user_data;
	SmlError *error = NULL;
	smlAssert(check != NULL);
	smlAssert(check->data != NULL);
	
	if (*(check->data)) {
		
		if (mutex)
			g_mutex_lock(mutex);
		
		SmlTransportData *data = *(check->data);
		*(check->data) = NULL;
		smlTrace(TRACE_ENTRY, "%s(data: %p, real %p, size %i)", __func__, data, data->data, data->size);
		
		SmlParser *parser = smlParserNew(data->type, 0, &error);
		smlAssert(parser != NULL);
		smlAssert(smlParserStart(parser, data->data, data->size, &error));
		
		SmlHeader *header = NULL;
		SmlCred *cred = NULL;
		smlAssert(smlParserGetHeader(parser, &header, &cred, &error));
		
		smlAssert(header != NULL);
		
		//smlAssert(header->messageID == current_msg_id);
		
		smlAssert(smlSessionReceiveHeader(check->session, header, &error));
		
		smlHeaderFree(header);
		
		SmlStatus *reply = smlStatusNew(SML_NO_ERROR, 0, check->session->lastReceivedMessageID, check->session->source, check->session->target, SML_COMMAND_TYPE_HEADER, &error);
		smlAssert(smlSessionSendReply(check->session, reply, &error));
		smlAssert(error == NULL);
		check->session->established = TRUE;
		
		smlStatusUnref(reply);
		
		g_atomic_int_inc(&current_msg_id);
		
		smlAssert(smlSessionReceiveBody(check->session, parser, &error));
		
		smlParserFree(parser);
		smlTransportDataDeref(data);
		*(check->data) = NULL; /* cleanup transport data reference */
		
		if (mutex)
			g_mutex_unlock(mutex);
		
		smlTrace(TRACE_EXIT, "%s", __func__);
	}
	return TRUE;
}

SmlSessionThread *smlSessionRunAsync(SmlSession *session, SmlTransportData **input)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, session, input);
	SmlError *error = NULL;
	
	SmlSessionThread *thread = g_malloc0(sizeof(SmlSessionThread));
	
	thread->context = g_main_context_new();
	g_main_context_ref(thread->context);
	
	thread->functions = g_malloc0(sizeof(GSourceFuncs));
	thread->functions->prepare = _session_prepare;
	thread->functions->check = _session_check;
	thread->functions->dispatch = _session_dispatch;
	thread->functions->finalize = NULL;

	thread->session_source = g_source_new(thread->functions, sizeof(GSource) + sizeof(SmlSession *));
	SmlSession **sessionptr = (SmlSession **)(thread->session_source + 1);
	*sessionptr = session;
	g_source_set_callback(thread->session_source, NULL, session, NULL);
	g_source_attach(thread->session_source, thread->context);
	
	thread->data_functions = g_malloc0(sizeof(GSourceFuncs));
	thread->data_functions->prepare = _data_prepare;
	thread->data_functions->check = _data_check;
	thread->data_functions->dispatch = _data_dispatch;
	thread->data_functions->finalize = NULL;
	thread->data = input;
	thread->session = session;
	
	thread->data_source = g_source_new(thread->data_functions, sizeof(GSource) + sizeof(SmlSession *));
	SmlSessionThread **threadptr = (SmlSessionThread **)(thread->data_source + 1);
	*threadptr = thread;
	g_source_set_callback(thread->data_source, NULL, thread, NULL);
	g_source_attach(thread->data_source, thread->context);
	
	thread->thread = smlThreadNew(thread->context, &error);
	sml_fail_unless(thread->thread != NULL, NULL);
	
	smlThreadStart(thread->thread);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return thread;
}

/** @brief Stops a asynchronously running transport
 * 
 * @param tsp The transport
 * 
 */
void smlSessionStop(SmlSessionThread *thread)
{
	smlTrace(TRACE_ENTRY, "%s(%p)", __func__, thread);
	
	smlAssert(thread);

	smlThreadStop(thread->thread);
	smlThreadFree(thread->thread);
	
	g_source_destroy(thread->data_source);
	g_source_unref(thread->data_source);
	g_source_destroy(thread->session_source);
	g_source_unref(thread->session_source);
	
	g_free(thread->functions);
	g_free(thread->data_functions);

	g_main_context_unref(thread->context);
	g_main_context_unref(thread->context);
	
	g_free(thread);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
}

/* If the notification is a normal SyncML message then a manager is
 * required. The easiest way are two function which create and destroy
 * a default manager - only for testing.
 */

SmlTransport *_tsp = NULL;

SmlManager* _start_manager()
{
	SmlError *error = NULL;
	_tsp = smlTransportNew(SML_TRANSPORT_HTTP_SERVER, &error);
	sml_fail_unless(_tsp != NULL, "%s", smlErrorPrint(&error));
	sml_fail_unless(error == NULL, "%s", smlErrorPrint(&error));
	
	SmlManager *manager = smlManagerNew(_tsp, &error);
	sml_fail_unless(manager != NULL, "%s", smlErrorPrint(&error));
	sml_fail_unless(error == NULL, "%s", smlErrorPrint(&error));

	smlManagerSetLocalMaxMsgSize(manager, defaultMaxMsgSize);
	smlManagerSetLocalMaxObjSize(manager, defaultMaxObjSize);

	return manager;
}

void _stop_manager(SmlManager *manager)
{
	smlManagerFree(manager);
	smlTransportFree(_tsp);
}


START_TEST (session_new)
{
	init_testbed();
	
	SmlError *error = NULL;

	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_CLIENT, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	sml_fail_unless(session != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	smlSessionUnref(session);
	smlLocationUnref(loc);

	cleanup_testbed();
}
END_TEST

START_TEST (session_send)
{
	init_testbed();
	
	SmlError *error = NULL;
	
	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_CLIENT, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	
	SmlCommand *cmd = smlCommandNewAlert(SML_ALERT_TWO_WAY, loc, loc, "last", "next", NULL, &error);
	sml_fail_unless(cmd != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
		
	sml_fail_unless(smlSessionSendCommand(session, cmd, NULL, NULL, NULL, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlCommandUnref(cmd);
	smlSessionUnref(session);
	smlLocationUnref(loc);

	cleanup_testbed();
}
END_TEST

static void _event_callback(SmlSession *session, SmlSessionEventType type, SmlCommand *command, SmlCommand *parent, SmlStatus *reply, SmlError *error, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s: %d (%p)", __func__, type, parent);
	smlAssert(GPOINTER_TO_INT(userdata) <= 5);
	SmlError *locerror = NULL;
	
	switch (type) {
		case SML_SESSION_EVENT_HEADER_REPLY:
			// FIXME: Why should the msgRef be the current_msg_id minus 2?
			// FIXME: This is wrong even if you count all messages
			// FIXME: because it depends on the SyncML role (server or client).
			// smlAssert(reply->msgRef == g_atomic_int_get(&current_msg_id) - 2);
			smlAssert(reply->cmdRef == 0);
			g_atomic_int_inc(&current_cmd_id);
			break;
		case SML_SESSION_EVENT_CHILD_COMMAND:
		case SML_SESSION_EVENT_COMMAND_START:
			g_atomic_int_inc(&start_commands_received);
			g_atomic_int_inc(&num_changes);
			
			g_atomic_int_inc(&current_cmd_id);
			
			if (GPOINTER_TO_INT(userdata) == 3 && command->type == SML_COMMAND_TYPE_ADD) {
				smlAssert(command->private.change.items);
				smlAssert(g_list_length(command->private.change.items));
				SmlItem *item = g_list_nth_data(command->private.change.items, 0);
				smlAssert(item);
				smlAssert(smlItemCheck(item));
				
				char *data = NULL;
				unsigned int size = 0;
				smlAssert(smlItemGetData(item, &data, &size, &locerror));
				
				smlAssert(size == 8);
				smlAssert(!strncmp(data, "datadata", 8));
			}
			
			if (GPOINTER_TO_INT(userdata) == 4 && command->type == SML_COMMAND_TYPE_ADD) {
				smlAssert(command->private.change.items);
				smlAssert(g_list_length(command->private.change.items));
				SmlItem *item = g_list_nth_data(command->private.change.items, 0);
				smlAssert(item);
				smlAssert(smlItemCheck(item));

				char *data = NULL;
				unsigned int size = 0;
				smlAssert(smlItemGetData(item, &data, &size, &locerror));
				
				smlAssert(size == 20);
				smlAssert(!strncmp(data, "datadatadatadatatest", 20));
			}
			
			if (GPOINTER_TO_INT(userdata) == 5 && command->type == SML_COMMAND_TYPE_ADD) {
				smlAssert(command->private.change.items);
				smlAssert(g_list_length(command->private.change.items));
				SmlItem *item = g_list_nth_data(command->private.change.items, 0);
				smlAssert(item);
				smlAssert(smlItemCheck(item));
				
				char *data = NULL;
				unsigned int size = 0;
				smlAssert(smlItemGetData(item, &data, &size, &locerror));
				
				if (size != 4) {
					smlAssert(size == item_size);
					smlAssert(!strncmp(data, item_data, item_size));
				} else {
					smlAssert(size == 4);
					smlAssert(!strncmp(data, "data", 4));
				}
			}
			
			SmlStatus *status = smlCommandNewReply(command, SML_NO_ERROR, &locerror);
			smlAssert(status != NULL);
			smlAssert(locerror == NULL);
			
			smlAssert(smlSessionSendReply(session, status, &locerror));
			smlAssert(locerror == NULL);
			
			smlStatusUnref(status);
			break;
		case SML_SESSION_EVENT_COMMAND_END:
			g_atomic_int_inc(&end_commands_received);
			break;
		case SML_SESSION_EVENT_FINAL:
			g_atomic_int_inc(&finals_received);
			current_cmd_id = 1;
			break;
		case SML_SESSION_EVENT_END:
			g_atomic_int_inc(&end_received);
			break;
		case SML_SESSION_EVENT_FLUSH:
			current_cmd_id = 1;
			g_atomic_int_inc(&numflushes);
			break;
		case SML_SESSION_EVENT_ERROR:
			smlAssert(error != NULL);
			if (GPOINTER_TO_INT(userdata) == 2)
				g_atomic_int_inc(&errors_received);
			else {
				sml_fail_unless(FALSE, "%s", smlErrorPrint(&error));
				abort();
			}
			break;
		case SML_MANAGER_SESSION_ESTABLISHED:
		case SML_SESSION_EVENT_RESPONSE_URI:
			// not used here
			break;
	}
	smlTrace(TRACE_EXIT, "%s", __func__);
}

void _status_reply(SmlSession *session, SmlStatus *status, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, status, userdata);
	
	smlAssert(session != NULL);
	smlAssert(status != NULL);
	
	//smlAssert(status->msgRef == current_msg_id - 1);
	
	if (GPOINTER_TO_INT(userdata) == 2) {
		smlAssert(smlStatusGetCode(status) >= 500);
	} else if (GPOINTER_TO_INT(userdata) == 1) {
		smlAssert(smlStatusGetClass(status) == SML_ERRORCLASS_SUCCESS);
	} else
		abort();
	
	g_atomic_int_inc(&current_cmd_id);
			
	g_atomic_int_inc(&numreplies);

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

void _status_reply_conc(SmlSession *session, SmlStatus *status, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, status, userdata);
	
	smlAssert(session != NULL);
	smlAssert(status != NULL);
	
	smlAssert(smlStatusGetClass(status) == SML_ERRORCLASS_SUCCESS);
	
	g_atomic_int_inc(&current_cmd_id);
	g_atomic_int_inc(&numreplies);

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

static void _data_send_callback(SmlSession *session, SmlTransportData *data, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, data, userdata);
	
	smlTrace(TRACE_INTERNAL, "%s: setting data(data: %p, real %p, size %i)", __func__, data, data->data, data->size);
	
	SmlTransportData **dataptr = userdata;
	if (*dataptr != NULL)
		smlTransportDataDeref(*dataptr);

	smlTransportDataRef(data);
	*dataptr = data;
	g_atomic_int_inc(&data_received);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
}

START_TEST (session_flush)
{
	init_testbed();
	
	SmlError *error = NULL;
	
	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_CLIENT, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	SmlSessionThread *thread1 = smlSessionRunAsync(session, &to_session);
	
	smlSessionSetDataCallback(session, _data_send_callback, &to_session2);
	smlSessionSetEventCallback(session, _event_callback, GINT_TO_POINTER(1));
	
	SmlCommand *cmd = smlCommandNewAlert(SML_ALERT_TWO_WAY, loc, loc, "last", "next", NULL, &error);
	sml_fail_unless(cmd != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlLocationUnref(loc);
	
	sml_fail_unless(smlSessionSendCommand(session, cmd, NULL, NULL, NULL, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlCommandUnref(cmd);
	
	sml_fail_unless(smlSessionFlush(session, FALSE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	while (data_received != 1) {
		usleep(100);
	}
	
	smlSessionStop(thread1);
	smlSessionUnref(session);

	cleanup_testbed();
}
END_TEST

START_TEST (session_flush_final)
{
	init_testbed();
	
	SmlError *error = NULL;
	
	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_CLIENT, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	SmlSessionThread *thread1 = smlSessionRunAsync(session, &to_session);
	
	smlSessionSetDataCallback(session, _data_send_callback, &to_session2);
	smlSessionSetEventCallback(session, _event_callback, GINT_TO_POINTER(1));
	
	SmlCommand *cmd = smlCommandNewAlert(SML_ALERT_TWO_WAY, loc, loc, "last", "next", NULL, &error);
	sml_fail_unless(cmd != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
		
	smlLocationUnref(loc);
	
	sml_fail_unless(smlSessionSendCommand(session, cmd, NULL, NULL, NULL, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlCommandUnref(cmd);
	
	sml_fail_unless(smlSessionFlush(session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	while (data_received != 1) {
		usleep(100);
	}
	
	smlSessionStop(thread1);
	smlSessionUnref(session);

	cleanup_testbed();
}
END_TEST

/* Check that event callbacks are working */
START_TEST (session_recv)
{
	init_testbed();
	
	SmlError *error = NULL;
	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	const char *input = "<SyncML xmlns=\"SYNCML:SYNCML1.2\"><SyncHdr><VerProto>SyncML/1.1</VerProto><VerDTD>1.2</VerDTD><MsgID>1</MsgID><SessionID>1</SessionID><Target><LocURI>/test</LocURI></Target><Source><LocURI>/test</LocURI></Source></SyncHdr><SyncBody><Alert><CmdID>1</CmdID><Item><Target><LocURI>/test</LocURI></Target><Source><LocURI>/test</LocURI></Source><Meta><Anchor xmlns=\"syncml:metinf\"><Next>last</Next><Last>next</Last></Anchor></Meta></Item><Data>200</Data></Alert><Final></Final></SyncBody></SyncML>";
	
	SmlParser *parser = smlParserNew(SML_MIMETYPE_XML, 0, &error);
	sml_fail_unless(parser != NULL, NULL);
	sml_fail_unless(smlParserStart(parser, input, strlen(input), &error), NULL);
	
	SmlHeader *header = NULL;
	SmlCred *cred = NULL;
	sml_fail_unless(smlParserGetHeader(parser, &header, &cred, &error), NULL);
	sml_fail_unless(header != NULL, NULL);
	sml_fail_unless(cred == NULL, NULL);
	
	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_SERVER, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	smlSessionSetLocalMaxMsgSize(session, defaultMaxMsgSize);
	smlSessionSetLocalMaxObjSize(session, defaultMaxObjSize);
	
	smlLocationUnref(loc);
	
	smlSessionSetEventCallback(session, _event_callback, GINT_TO_POINTER(1));

	sml_fail_unless(smlSessionReceiveHeader(session, header, &error), NULL);

	SmlStatus *reply = smlStatusNew(SML_NO_ERROR, 0, 1, session->source, session->target, SML_COMMAND_TYPE_HEADER, &error);
	sml_fail_unless(smlSessionSendReply(session, reply, &error), NULL);
	smlStatusUnref(reply);
	session->established = TRUE;

	sml_fail_unless(smlSessionReceiveBody(session, parser, &error), NULL);
	
	smlParserFree(parser);
	smlHeaderFree(header);
	
	sml_fail_unless(start_commands_received == 1, NULL);
	sml_fail_unless(finals_received == 1, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	smlSessionUnref(session);

	cleanup_testbed();
}
END_TEST

/* Check that a session can parse the output of another session */
START_TEST (session_transmit)
{
	init_testbed();
	
	SmlError *error = NULL;
	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_CLIENT, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	SmlSessionThread *thread1 = smlSessionRunAsync(session, &to_session);
	
	smlSessionSetDataCallback(session, _data_send_callback, &to_session2);
	smlSessionSetEventCallback(session, _event_callback, GINT_TO_POINTER(1));
	
	SmlCommand *cmd = smlCommandNewAlert(SML_ALERT_TWO_WAY, loc, loc, "last", "next", NULL, &error);
	sml_fail_unless(cmd != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
		
	sml_fail_unless(smlSessionSendCommand(session, cmd, NULL, NULL, NULL, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlCommandUnref(cmd);
	
	sml_fail_unless(smlSessionFlush(session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	while (data_received != 1) {
		usleep(100);
	}
	
	smlSessionStop(thread1);
	smlSessionUnref(session);
	session = NULL;

	SmlParser *parser = smlParserNew(to_session2->type, 0, &error);
	sml_fail_unless(parser != NULL, NULL);
	sml_fail_unless(smlParserStart(parser, to_session2->data, to_session2->size, &error), NULL);
	
	SmlHeader *header = NULL;
	SmlCred *cred = NULL;
	sml_fail_unless(smlParserGetHeader(parser, &header, &cred, &error), NULL);

	SmlSession *session2 = smlSessionNew(SML_SESSION_TYPE_SERVER, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	smlSessionSetLocalMaxMsgSize(session2, defaultMaxMsgSize);
	smlSessionSetLocalMaxObjSize(session2, defaultMaxObjSize);
	
	smlLocationUnref(loc);
	
	smlSessionSetDataCallback(session2, _data_send_callback, &to_session);
	smlSessionSetEventCallback(session2, _event_callback, GINT_TO_POINTER(1));
	
	sml_fail_unless(smlSessionReceiveHeader(session2, header, &error), NULL);

	SmlStatus *reply = smlStatusNew(SML_NO_ERROR, 0, 1, session2->source, session2->target, SML_COMMAND_TYPE_HEADER, &error);
	sml_fail_unless(smlSessionSendReply(session2, reply, &error), NULL);
	smlStatusUnref(reply);
	session2->established = TRUE;

	sml_fail_unless(smlSessionReceiveBody(session2, parser, &error), NULL);
	
	smlHeaderFree(header);
	
	sml_fail_unless(start_commands_received == 1, NULL);
	sml_fail_unless(finals_received == 1, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	smlParserFree(parser);
	
	smlSessionUnref(session2);

	cleanup_testbed();
}
END_TEST

/* Check if statuses are working */
/* session --- Alert --> session2
 * session2 --- Status ---> Session
 */
START_TEST (session_reply)
{
	init_testbed();
	
	SmlError *error = NULL;
	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_CLIENT, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	SmlSessionThread *thread1 = smlSessionRunAsync(session, &to_session);
	SmlSession *session2 = smlSessionNew(SML_SESSION_TYPE_SERVER, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	SmlSessionThread *thread2 = smlSessionRunAsync(session2, &to_session2);
	smlSessionSetLocalMaxMsgSize(session2, defaultMaxMsgSize);
	smlSessionSetLocalMaxObjSize(session2, defaultMaxObjSize);
	
	smlSessionSetEventCallback(session2, _event_callback, GINT_TO_POINTER(1));
	smlSessionSetDataCallback(session2, _data_send_callback, &to_session);
	smlSessionSetEventCallback(session, _event_callback, GINT_TO_POINTER(1));
	smlSessionSetDataCallback(session, _data_send_callback, &to_session2);
	
	SmlCommand *cmd = smlCommandNewAlert(SML_ALERT_TWO_WAY, loc, loc, "last", "next", NULL, &error);
	sml_fail_unless(cmd != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
		
	smlLocationUnref(loc);
	
	sml_fail_unless(smlSessionSendCommand(session, cmd, NULL, _status_reply, GINT_TO_POINTER(1), &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlCommandUnref(cmd);
	
	sml_fail_unless(smlSessionFlush(session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	while (finals_received != 1) {
		usleep(100);
	}
	
	sml_fail_unless(data_received == 1, NULL);
	sml_fail_unless(numreplies == 0, NULL);
	sml_fail_unless(start_commands_received == 1, NULL);
	sml_fail_unless(finals_received == 1, NULL);
	sml_fail_unless(end_received == 0, NULL);
			
	sml_fail_unless(smlSessionFlush(session2, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);

	/* do not test on final because this is a race condition */
	while (end_received < 2) {
		usleep(100);
	}
	
	sml_fail_unless(data_received == 2, NULL);
	sml_fail_unless(numreplies == 1, NULL);
	sml_fail_unless(start_commands_received == 1, NULL);
	sml_fail_unless(finals_received == 2, NULL);
	sml_fail_unless(end_received == 2, NULL);

	smlSessionStop(thread1);
	smlSessionStop(thread2);
	smlSessionUnref(session2);
	smlSessionUnref(session);

	cleanup_testbed();
}
END_TEST

/* Check if sessions can talk to each other
 * session2 must reply with a empty message since
 * it is a server
 * 
 * session --- Alert --> session2
 * session2 --- Status, Alert ---> session
 * session --- Status ---> session2
 * session2 ---  ---> session
 */
START_TEST (session_talk)
{
	init_testbed();
	
	SmlError *error = NULL;
	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_CLIENT, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	SmlSessionThread *thread1 = smlSessionRunAsync(session, &to_session);
	SmlSession *session2 = smlSessionNew(SML_SESSION_TYPE_SERVER, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	SmlSessionThread *thread2 = smlSessionRunAsync(session2, &to_session2);
	smlSessionSetLocalMaxMsgSize(session2, defaultMaxMsgSize);
	smlSessionSetLocalMaxObjSize(session2, defaultMaxObjSize);
	
	smlSessionSetEventCallback(session2, _event_callback, GINT_TO_POINTER(1));
	smlSessionSetDataCallback(session2, _data_send_callback, &to_session);
	smlSessionSetEventCallback(session, _event_callback, GINT_TO_POINTER(1));
	smlSessionSetDataCallback(session, _data_send_callback, &to_session2);
	
	SmlCommand *cmd = smlCommandNewAlert(SML_ALERT_TWO_WAY, loc, loc, "last", "next", NULL, &error);
	sml_fail_unless(cmd != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlLocationUnref(loc);
	
	sml_fail_unless(smlSessionSendCommand(session, cmd, NULL, _status_reply, GINT_TO_POINTER(1), &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	sml_fail_unless(smlSessionFlush(session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	while (finals_received != 1) {
		usleep(100);
	}
	
	sml_fail_unless(data_received == 1, NULL);
	sml_fail_unless(numreplies == 0, NULL);
	sml_fail_unless(start_commands_received == 1, NULL);
	sml_fail_unless(finals_received == 1, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	sml_fail_unless(smlSessionSendCommand(session2, cmd, NULL, _status_reply, GINT_TO_POINTER(1), &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlCommandUnref(cmd);
	
	sml_fail_unless(smlSessionFlush(session2, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	
	while (finals_received != 2) {
		usleep(100);
	}
	
	sml_fail_unless(data_received == 2, NULL);
	sml_fail_unless(numreplies == 1, NULL);
	sml_fail_unless(start_commands_received == 2, NULL);
	sml_fail_unless(finals_received == 2, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	sml_fail_unless(smlSessionFlush(session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	
	while (finals_received != 4 || end_received != 2) {
		usleep(100);
	}
	
	sml_fail_unless(data_received == 4, NULL);
	sml_fail_unless(numreplies == 2, NULL);
	sml_fail_unless(start_commands_received == 2, NULL);
	sml_fail_unless(finals_received == 4, NULL);
	sml_fail_unless(end_received == 2, NULL);
	
	smlSessionStop(thread1);
	smlSessionStop(thread2);
	smlSessionUnref(session2);
	smlSessionUnref(session);

	cleanup_testbed();
}
END_TEST

/* Check if sessions can talk to each other
 * session2 must reply with a empty message since
 * it is a server
 * 
 * session --- num * Alert --> session2
 * session2 --- num * Status, num * Alert ---> session
 * session --- num * Status ---> session2
 * session2 ---  ---> session
 */
START_TEST (session_talk_alot)
{
	init_testbed();
	
	int num_commands = 1000;
	
	SmlError *error = NULL;
	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_CLIENT, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	SmlSessionThread *thread1 = smlSessionRunAsync(session, &to_session);
	SmlSession *session2 = smlSessionNew(SML_SESSION_TYPE_SERVER, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	SmlSessionThread *thread2 = smlSessionRunAsync(session2, &to_session2);

	/* The test is not designed for splitted packages or objects. */
	smlSessionSetLocalMaxMsgSize(session2, defaultMaxObjSize);
	smlSessionSetLocalMaxObjSize(session2, defaultMaxObjSize);
	
	smlSessionSetEventCallback(session2, _event_callback, GINT_TO_POINTER(1));
	smlSessionSetDataCallback(session2, _data_send_callback, &to_session);
	smlSessionSetEventCallback(session, _event_callback, GINT_TO_POINTER(1));
	smlSessionSetDataCallback(session, _data_send_callback, &to_session2);
	
	SmlCommand *cmd = smlCommandNewAlert(SML_ALERT_TWO_WAY, loc, loc, "last", "next", NULL, &error);
	sml_fail_unless(cmd != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlLocationUnref(loc);
	
	int i = 0;
	for (i = 0; i < num_commands; i++) {
		sml_fail_unless(smlSessionSendCommand(session, cmd, NULL, _status_reply, GINT_TO_POINTER(1), &error), NULL);
		sml_fail_unless(error == NULL, NULL);
	}
	
	sml_fail_unless(smlSessionFlush(session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	while (finals_received != 1) {
		usleep(100);
	}
	
	sml_fail_unless(data_received == 1, NULL);
	sml_fail_unless(numreplies == 0, NULL);
	sml_fail_unless(start_commands_received == num_commands, NULL);
	sml_fail_unless(finals_received == 1, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	for (i = 0; i < num_commands; i++) {
		sml_fail_unless(smlSessionSendCommand(session2, cmd, NULL, _status_reply, GINT_TO_POINTER(1), &error), NULL);
		sml_fail_unless(error == NULL, NULL);
	}
	
	smlCommandUnref(cmd);
	
	sml_fail_unless(smlSessionFlush(session2, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	
	while (finals_received != 2) {
		usleep(100);
	}
	
	sml_fail_unless(data_received == 2, NULL);
	sml_fail_unless(numreplies == num_commands, NULL);
	sml_fail_unless(start_commands_received == 2 * num_commands, NULL);
	sml_fail_unless(finals_received == 2, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	sml_fail_unless(smlSessionFlush(session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, "%s", smlErrorPrint(&error));
	
	/*while (finals_received != 3) {
		usleep(100);
	}
	
	sml_fail_unless(data_received == 3, NULL);
	sml_fail_unless(numreplies == 2 * num_commands, NULL);
	sml_fail_unless(start_commands_received == 2 * num_commands, NULL);
	sml_fail_unless(finals_received == 3, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	sml_fail_unless(smlSessionFlush(session2, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);*/
	
	while (finals_received != 4 || end_received != 2) {
		usleep(100);
	}
	
	sml_fail_unless(data_received == 4, NULL);
	sml_fail_unless(numreplies == 2 * num_commands, NULL);
	sml_fail_unless(start_commands_received == 2 * num_commands, NULL);
	sml_fail_unless(finals_received == 4, NULL);
	sml_fail_unless(end_received == 2, NULL);
	
	smlSessionStop(thread1);
	smlSessionStop(thread2);
	smlSessionUnref(session2);
	smlSessionUnref(session);

	cleanup_testbed();
}
END_TEST

/* Check if sessions can talk to each other
 * session2 must reply with a empty message since
 * it is a server
 * 
 * session --- num * Alert --> session2
 * session2 --- num * Status, num * Alert ---> session
 * session --- num * Status ---> session2
 * session2 ---  ---> session
 */
START_TEST (session_talk_alot_limit)
{
	init_testbed();
	
	int num_commands = 1000;
	int limit = 20000;
	
	mutex = g_mutex_new();
	
	SmlError *error = NULL;
	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_CLIENT, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	SmlSessionThread *thread1 = smlSessionRunAsync(session, &to_session);
	SmlSession *session2 = smlSessionNew(SML_SESSION_TYPE_SERVER, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	SmlSessionThread *thread2 = smlSessionRunAsync(session2, &to_session2);
	
	smlSessionSetLocalMaxMsgSize(session, limit);
	smlSessionSetRemoteMaxMsgSize(session, limit);
	smlSessionSetLocalMaxMsgSize(session2, limit);
	smlSessionSetLocalMaxObjSize(session2, defaultMaxObjSize);
	smlSessionSetRemoteMaxMsgSize(session2, limit);
	
	smlSessionSetEventCallback(session2, _event_callback, GINT_TO_POINTER(1));
	smlSessionSetDataCallback(session2, _data_send_callback, &to_session);
	smlSessionSetEventCallback(session, _event_callback, GINT_TO_POINTER(1));
	smlSessionSetDataCallback(session, _data_send_callback, &to_session2);
	
	SmlCommand *cmd = smlCommandNewAlert(SML_ALERT_TWO_WAY, loc, loc, "last", "next", NULL, &error);
	sml_fail_unless(cmd != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlLocationUnref(loc);
	
	int i = 0;
	for (i = 0; i < num_commands; i++) {
		sml_fail_unless(smlSessionSendCommand(session, cmd, NULL, _status_reply, GINT_TO_POINTER(1), &error), NULL);
		sml_fail_unless(error == NULL, NULL);
	}
	
	sml_fail_unless(smlSessionFlush(session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	while (finals_received != 1) {
		usleep(100);
	}
	
	sml_fail_unless(start_commands_received == num_commands, NULL);
	sml_fail_unless(finals_received == 1, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	for (i = 0; i < num_commands; i++) {
		sml_fail_unless(smlSessionSendCommand(session2, cmd, NULL, _status_reply, GINT_TO_POINTER(1), &error), NULL);
		sml_fail_unless(error == NULL, NULL);
	}
	
	smlCommandUnref(cmd);
	
	sml_fail_unless(smlSessionFlush(session2, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	
	while (finals_received != 2) {
		usleep(100);
	}
	
	sml_fail_unless(start_commands_received == 2 * num_commands, NULL);
	sml_fail_unless(finals_received == 2, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	sml_fail_unless(smlSessionFlush(session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	/*while (finals_received != 3) {
		usleep(100);
	}
	
	sml_fail_unless(numreplies == 2 * num_commands, NULL);
	sml_fail_unless(start_commands_received == 2 * num_commands, NULL);
	sml_fail_unless(finals_received == 3, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	sml_fail_unless(smlSessionFlush(session2, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);*/
	
	while (finals_received != 4 || end_received != 2) {
		usleep(100);
	}
	
	sml_fail_unless(numreplies == 2 * num_commands, NULL);
	sml_fail_unless(start_commands_received == 2 * num_commands, NULL);
	sml_fail_unless(finals_received == 4, NULL);
	sml_fail_unless(end_received == 2, NULL);
	
	smlSessionStop(thread1);
	smlSessionStop(thread2);
	smlSessionUnref(session2);
	smlSessionUnref(session);

	g_mutex_free(mutex);

	cleanup_testbed();
}
END_TEST

/* Check if the session obeys remote message
 * limits.
 * 
 * For this we setup a session with the remote limit set. The
 * expected behaviour is to see
 * - first the commands are added
 * - when the limit is reached, the data callback should be called
 * - more commands can be added
 * - as soon the session receives the reply from the remote side (a status in our case)
 *   the session should call the data callback again
 * 
 * Flushing while waiting for the remote reply should be ignored
 * 
 * session -- x Alert (not final) --> session2
 * session2 -- x Status (not final) --> session
 * session -- y Alert (final) --> session2
 * session2 -- y Alert (final) --> session
 * 
 */
START_TEST (session_limit_remote)
{
	init_testbed();
	
	int num_commands = 100;
	int limit = 5000;
	
	mutex = g_mutex_new();
	
	SmlError *error = NULL;
	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_CLIENT, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	SmlSessionThread *thread1 = smlSessionRunAsync(session, &to_session);
	SmlSession *session2 = smlSessionNew(SML_SESSION_TYPE_SERVER, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	SmlSessionThread *thread2 = smlSessionRunAsync(session2, &to_session2);
	
	smlSessionSetLocalMaxMsgSize(session, limit);
	smlSessionSetRemoteMaxMsgSize(session, limit);
	smlSessionSetLocalMaxMsgSize(session2, defaultMaxMsgSize);
	smlSessionSetLocalMaxObjSize(session2, defaultMaxObjSize);
	
	smlSessionSetEventCallback(session2, _event_callback, GINT_TO_POINTER(1));
	smlSessionSetDataCallback(session2, _data_send_callback, &to_session);
	smlSessionSetEventCallback(session, _event_callback, GINT_TO_POINTER(1));
	smlSessionSetDataCallback(session, _data_send_callback, &to_session2);
	
	SmlCommand *cmd = smlCommandNewAlert(SML_ALERT_TWO_WAY, loc, loc, "last", "next", NULL, &error);
	sml_fail_unless(cmd != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlLocationUnref(loc);
	
	int i = 0;
	for (i = 0; i < num_commands; i++) {
		sml_fail_unless(smlSessionSendCommand(session, cmd, NULL, _status_reply, GINT_TO_POINTER(1), &error), NULL);
		sml_fail_unless(error == NULL, NULL);
	}
	
	sml_fail_unless(smlSessionFlush(session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	while (finals_received < 1) {
		usleep(100);
	}
	
	sml_fail_unless(start_commands_received == num_commands, NULL);
	sml_fail_unless(finals_received == 1, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	int flush_before = numflushes;
	
	for (i = 0; i < num_commands; i++) {
		sml_fail_unless(smlSessionSendCommand(session2, cmd, NULL, _status_reply, GINT_TO_POINTER(1), &error), NULL);
		sml_fail_unless(error == NULL, NULL);
	}
	
	smlCommandUnref(cmd);
	
	sml_fail_unless(smlSessionFlush(session2, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	
	while (finals_received < 2) {
		usleep(100);
	}

	// fprintf(stderr, "numflushes: %i, flush_before: %i\n", numflushes, flush_before);
	sml_fail_unless(numflushes - flush_before > 5, "numflushes: %i, flush_before: %i", numflushes, flush_before);
	sml_fail_unless(start_commands_received == 2 * num_commands, NULL);
	sml_fail_unless(finals_received == 2, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	sml_fail_unless(smlSessionFlush(session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	while (finals_received != 4 || end_received != 2) {
		usleep(100);
	}
	
	sml_fail_unless(numreplies == 2 * num_commands, NULL);
	sml_fail_unless(start_commands_received == 2 * num_commands, NULL);
	sml_fail_unless(finals_received == 4, NULL);
	sml_fail_unless(end_received == 2, NULL);
	
	smlSessionStop(thread1);
	smlSessionStop(thread2);
	smlSessionUnref(session2);
	smlSessionUnref(session);

	g_mutex_free(mutex);

	cleanup_testbed();
}
END_TEST

START_TEST (session_split_child)
{
	init_testbed();
	
	int num_commands = 100;
	int limit = 5000;
	
	mutex = g_mutex_new();
	
	setup_testbed(NULL);
	
	SmlError *error = NULL;
	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_CLIENT, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	SmlSessionThread *thread1 = smlSessionRunAsync(session, &to_session);
	SmlSession *session2 = smlSessionNew(SML_SESSION_TYPE_SERVER, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	SmlSessionThread *thread2 = smlSessionRunAsync(session2, &to_session2);
	
	smlSessionSetLocalMaxMsgSize(session, limit);
	smlSessionSetRemoteMaxMsgSize(session, limit);
	smlSessionSetLocalMaxMsgSize(session2, limit);
	smlSessionSetRemoteMaxMsgSize(session2, limit);
	smlSessionSetLocalMaxObjSize(session2, defaultMaxObjSize);
	
	smlSessionSetEventCallback(session2, _event_callback, GINT_TO_POINTER(1));
	smlSessionSetDataCallback(session2, _data_send_callback, &to_session);
	smlSessionSetEventCallback(session, _event_callback, GINT_TO_POINTER(1));
	smlSessionSetDataCallback(session, _data_send_callback, &to_session2);
	
	SmlCommand *cmd1 = smlCommandNewSync(loc, loc, 0, &error);
	sml_fail_unless(cmd1 != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlLocationUnref(loc);
	
	SmlCommand *cmd2 = smlCommandNewChange(SML_CHANGE_ADD, "uid", "data", 5, SML_ELEMENT_TEXT_VCARD, &error);
	
	sml_fail_unless(smlSessionStartCommand(session, cmd1, NULL, _status_reply, GINT_TO_POINTER(1), &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	int i = 0;
	for (i = 0; i < num_commands; i++) {
		sml_fail_unless(smlSessionSendCommand(session, cmd2, NULL, _status_reply, GINT_TO_POINTER(1), &error), NULL);
		sml_fail_unless(error == NULL, NULL);
	}
	
	sml_fail_unless(smlSessionEndCommand(session, NULL, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	sml_fail_unless(smlSessionFlush(session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	while (finals_received != 1) {
		usleep(100);
	}
	
	sml_fail_unless(start_commands_received == num_commands + 1, NULL);
	sml_fail_unless(finals_received == 1, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	sml_fail_unless(smlSessionStartCommand(session2, cmd1, NULL, _status_reply, GINT_TO_POINTER(1), &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	for (i = 0; i < num_commands; i++) {
		sml_fail_unless(smlSessionSendCommand(session2, cmd2, NULL, _status_reply, GINT_TO_POINTER(1), &error), NULL);
		sml_fail_unless(error == NULL, NULL);
	}
	
	sml_fail_unless(smlSessionEndCommand(session2, NULL, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlCommandUnref(cmd1);
	smlCommandUnref(cmd2);
	
	sml_fail_unless(smlSessionFlush(session2, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	
	while (finals_received != 2) {
		usleep(100);
	}
	
	sml_fail_unless(start_commands_received == 2 * (num_commands + 1), NULL);
	sml_fail_unless(finals_received == 2, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	sml_fail_unless(smlSessionFlush(session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	/*while (finals_received != 3) {
		usleep(100);
	}
	
	sml_fail_unless(numreplies == 2 * (num_commands + 1), NULL);
	sml_fail_unless(start_commands_received == 2 * (num_commands + 1), NULL);
	sml_fail_unless(finals_received == 3, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	sml_fail_unless(smlSessionFlush(session2, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);*/
	
	while (finals_received != 4 || end_received != 2) {
		usleep(100);
	}
	
	sml_fail_unless(numreplies == 2 * (num_commands + 1), NULL);
	sml_fail_unless(start_commands_received == 2 * (num_commands + 1), NULL);
	sml_fail_unless(finals_received == 4, NULL);
	sml_fail_unless(end_received == 2, NULL);
	
	smlSessionStop(thread1);
	smlSessionStop(thread2);
	smlSessionUnref(session2);
	smlSessionUnref(session);

	g_mutex_free(mutex);

	cleanup_testbed();
}
END_TEST

START_TEST (session_recv_large_obj)
{
	init_testbed();
	
	SmlError *error = NULL;
	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	const char *input = "<SyncML xmlns=\"SYNCML:SYNCML1.1\"><SyncHdr><VerProto>SyncML/1.1</VerProto><VerDTD>1.1</VerDTD><MsgID>1</MsgID><SessionID>1</SessionID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source></SyncHdr><SyncBody><Sync><CmdID>1</CmdID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source><NumberOfChanges>1</NumberOfChanges><Add><CmdID>2</CmdID><Meta><Type xmlns=\"syncml:metinf\">text/x-vcard</Type><Size>8</Size></Meta><Item><Source><LocURI>uid</LocURI></Source><Data><![CDATA[data]]></Data><MoreData></MoreData></Item></Add></Sync></SyncBody></SyncML>";
	
	SmlParser *parser = smlParserNew(SML_MIMETYPE_XML, 0, &error);
	sml_fail_unless(parser != NULL, NULL);
	sml_fail_unless(smlParserStart(parser, input, strlen(input), &error), NULL);
	
	SmlHeader *header = NULL;
	SmlCred *cred = NULL;
	sml_fail_unless(smlParserGetHeader(parser, &header, &cred, &error), NULL);
	sml_fail_unless(header != NULL, NULL);
	sml_fail_unless(cred == NULL, NULL);
	
	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_SERVER, SML_MIMETYPE_XML, SML_VERSION_11, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	smlSessionSetLocalMaxMsgSize(session, defaultMaxMsgSize);
	smlSessionSetLocalMaxObjSize(session, defaultMaxObjSize);
	
	smlLocationUnref(loc);
	
	smlSessionSetEventCallback(session, _event_callback, GINT_TO_POINTER(3));
	smlSessionSetDataCallback(session, _data_send_callback, &to_session2);
	
	sml_fail_unless(smlSessionReceiveHeader(session, header, &error), NULL);
	
	SmlStatus *reply = smlStatusNew(SML_NO_ERROR, 0, 1, session->source, session->target, SML_COMMAND_TYPE_HEADER, &error);
	sml_fail_unless(smlSessionSendReply(session, reply, &error), NULL);
	smlStatusUnref(reply);
	session->established = TRUE;
	
	sml_fail_unless(smlSessionReceiveBody(session, parser, &error), NULL);
	
	smlParserFree(parser);
	smlHeaderFree(header);
	
	sml_fail_unless(start_commands_received == 1, NULL);
	sml_fail_unless(finals_received == 0, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	sml_fail_unless(to_session2 != NULL, NULL);
	smlTransportDataDeref(to_session2);
	to_session2 = NULL;
	
	const char *input2 = "<SyncML xmlns=\"SYNCML:SYNCML1.1\"><SyncHdr><VerProto>SyncML/1.1</VerProto><VerDTD>1.1</VerDTD><MsgID>2</MsgID><SessionID>1</SessionID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source></SyncHdr><SyncBody><Sync><CmdID>1</CmdID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source><Add><CmdID>2</CmdID><Meta><Type xmlns=\"syncml:metinf\">text/x-vcard</Type></Meta><Item><Source><LocURI>uid</LocURI></Source><Data><![CDATA[data]]></Data></Item></Add></Sync><Final></Final></SyncBody></SyncML>";
	
	parser = smlParserNew(SML_MIMETYPE_XML, 0, &error);
	sml_fail_unless(parser != NULL, NULL);
	sml_fail_unless(smlParserStart(parser, input2, strlen(input2), &error), NULL);
	
	sml_fail_unless(smlParserGetHeader(parser, &header, &cred, &error), NULL);
	sml_fail_unless(header != NULL, NULL);
	sml_fail_unless(cred == NULL, NULL);
	
	sml_fail_unless(smlSessionReceiveHeader(session, header, &error), NULL);
	
	reply = smlStatusNew(SML_NO_ERROR, 0, 2, session->source, session->target, SML_COMMAND_TYPE_HEADER, &error);
	sml_fail_unless(smlSessionSendReply(session, reply, &error), NULL);
	smlStatusUnref(reply);
	session->established = TRUE;
	
	sml_fail_unless(smlSessionReceiveBody(session, parser, &error), NULL);
	
	smlParserFree(parser);
	smlHeaderFree(header);
	
	sml_fail_unless(start_commands_received == 3, NULL);
	sml_fail_unless(finals_received == 1, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	smlSessionUnref(session);

	cleanup_testbed();
}
END_TEST

START_TEST (session_recv_large_obj2)
{
	init_testbed();
	
	SmlError *error = NULL;
	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	const char *input = "<SyncML xmlns=\"SYNCML:SYNCML1.1\"><SyncHdr><VerProto>SyncML/1.1</VerProto><VerDTD>1.1</VerDTD><MsgID>1</MsgID><SessionID>1</SessionID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source></SyncHdr><SyncBody><Sync><CmdID>1</CmdID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source><NumberOfChanges>1</NumberOfChanges><Add><CmdID>2</CmdID><Meta><Type xmlns=\"syncml:metinf\">text/x-vcard</Type><Size>20</Size></Meta><Item><Source><LocURI>uid</LocURI></Source><Data><![CDATA[data]]></Data><MoreData></MoreData></Item></Add></Sync></SyncBody></SyncML>";
	
	SmlParser *parser = smlParserNew(SML_MIMETYPE_XML, 0, &error);
	sml_fail_unless(parser != NULL, NULL);
	sml_fail_unless(smlParserStart(parser, input, strlen(input), &error), NULL);
	
	SmlHeader *header = NULL;
	SmlCred *cred = NULL;
	sml_fail_unless(smlParserGetHeader(parser, &header, &cred, &error), NULL);
	sml_fail_unless(header != NULL, NULL);
	sml_fail_unless(cred == NULL, NULL);
	
	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_SERVER, SML_MIMETYPE_XML, SML_VERSION_11, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	smlSessionSetLocalMaxMsgSize(session, defaultMaxMsgSize);
	smlSessionSetLocalMaxObjSize(session, defaultMaxObjSize);
	
	smlSessionSetEventCallback(session, _event_callback, GINT_TO_POINTER(4));
	smlSessionSetDataCallback(session, _data_send_callback, &to_session2);
	
	sml_fail_unless(smlSessionReceiveHeader(session, header, &error), NULL);
	
	SmlStatus *reply = smlStatusNew(SML_NO_ERROR, 0, 1, session->source, session->target, SML_COMMAND_TYPE_HEADER, &error);
	sml_fail_unless(smlSessionSendReply(session, reply, &error), NULL);
	smlStatusUnref(reply);
	session->established = TRUE;
	
	sml_fail_unless(smlSessionReceiveBody(session, parser, &error), NULL);
	
	smlParserFree(parser);
	smlHeaderFree(header);
	
	sml_fail_unless(start_commands_received == 1, NULL);
	sml_fail_unless(finals_received == 0, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	sml_fail_unless(to_session2 != NULL, NULL);
	smlTransportDataDeref(to_session2);
	to_session2 = NULL;
	
	const char *input2 = "<SyncML xmlns=\"SYNCML:SYNCML1.1\"><SyncHdr><VerProto>SyncML/1.1</VerProto><VerDTD>1.1</VerDTD><MsgID>2</MsgID><SessionID>1</SessionID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source></SyncHdr><SyncBody><Sync><CmdID>1</CmdID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source><Add><CmdID>2</CmdID><Meta><Type xmlns=\"syncml:metinf\">text/x-vcard</Type></Meta><Item><Source><LocURI>uid</LocURI></Source><Data><![CDATA[data]]></Data><MoreData/></Item></Add></Sync></SyncBody></SyncML>";
	
	parser = smlParserNew(SML_MIMETYPE_XML, 0, &error);
	sml_fail_unless(parser != NULL, NULL);
	sml_fail_unless(smlParserStart(parser, input2, strlen(input2), &error), NULL);
	
	sml_fail_unless(smlParserGetHeader(parser, &header, &cred, &error), NULL);
	sml_fail_unless(header != NULL, NULL);
	sml_fail_unless(cred == NULL, NULL);
	
	sml_fail_unless(smlSessionReceiveHeader(session, header, &error), NULL);
	
	reply = smlStatusNew(SML_NO_ERROR, 0, 2, session->source, session->target, SML_COMMAND_TYPE_HEADER, &error);
	sml_fail_unless(smlSessionSendReply(session, reply, &error), NULL);
	smlStatusUnref(reply);
	session->established = TRUE;
	
	sml_fail_unless(smlSessionReceiveBody(session, parser, &error), NULL);
	
	smlParserFree(parser);
	smlHeaderFree(header);
	
	sml_fail_unless(start_commands_received == 2, NULL);
	sml_fail_unless(finals_received == 0, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	sml_fail_unless(to_session2 != NULL, NULL);
	smlTransportDataDeref(to_session2);
	to_session2 = NULL;
	
	input2 = "<SyncML xmlns=\"SYNCML:SYNCML1.1\"><SyncHdr><VerProto>SyncML/1.1</VerProto><VerDTD>1.1</VerDTD><MsgID>3</MsgID><SessionID>1</SessionID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source></SyncHdr><SyncBody><Sync><CmdID>1</CmdID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source><Add><CmdID>2</CmdID><Meta><Type xmlns=\"syncml:metinf\">text/x-vcard</Type></Meta><Item><Source><LocURI>uid</LocURI></Source><Data><![CDATA[data]]></Data><MoreData/></Item></Add></Sync></SyncBody></SyncML>";
	
	parser = smlParserNew(SML_MIMETYPE_XML, 0, &error);
	sml_fail_unless(parser != NULL, NULL);
	sml_fail_unless(smlParserStart(parser, input2, strlen(input2), &error), NULL);
	
	sml_fail_unless(smlParserGetHeader(parser, &header, &cred, &error), NULL);
	sml_fail_unless(header != NULL, NULL);
	sml_fail_unless(cred == NULL, NULL);
	
	sml_fail_unless(smlSessionReceiveHeader(session, header, &error), NULL);
	
	reply = smlStatusNew(SML_NO_ERROR, 0, 3, session->source, session->target, SML_COMMAND_TYPE_HEADER, &error);
	sml_fail_unless(smlSessionSendReply(session, reply, &error), NULL);
	smlStatusUnref(reply);
	session->established = TRUE;
	
	sml_fail_unless(smlSessionReceiveBody(session, parser, &error), NULL);
	
	smlParserFree(parser);
	smlHeaderFree(header);
	
	sml_fail_unless(start_commands_received == 3, NULL);
	sml_fail_unless(finals_received == 0, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	sml_fail_unless(to_session2 != NULL, NULL);
	smlTransportDataDeref(to_session2);
	to_session2 = NULL;
	
	input2 = "<SyncML xmlns=\"SYNCML:SYNCML1.1\"><SyncHdr><VerProto>SyncML/1.1</VerProto><VerDTD>1.1</VerDTD><MsgID>4</MsgID><SessionID>1</SessionID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source></SyncHdr><SyncBody><Sync><CmdID>1</CmdID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source><Add><CmdID>2</CmdID><Meta><Type xmlns=\"syncml:metinf\">text/x-vcard</Type></Meta><Item><Source><LocURI>uid</LocURI></Source><Data><![CDATA[datatest]]></Data></Item></Add></Sync><Final></Final></SyncBody></SyncML>";
	
	parser = smlParserNew(SML_MIMETYPE_XML, 0, &error);
	sml_fail_unless(parser != NULL, NULL);
	sml_fail_unless(smlParserStart(parser, input2, strlen(input2), &error), NULL);
	
	sml_fail_unless(smlParserGetHeader(parser, &header, &cred, &error), NULL);
	sml_fail_unless(header != NULL, NULL);
	sml_fail_unless(cred == NULL, NULL);
	
	sml_fail_unless(smlSessionReceiveHeader(session, header, &error), NULL);
	
	reply = smlStatusNew(SML_NO_ERROR, 0, 4, session->source, session->target, SML_COMMAND_TYPE_HEADER, &error);
	sml_fail_unless(smlSessionSendReply(session, reply, &error), NULL);
	smlStatusUnref(reply);
	session->established = TRUE;
	
	sml_fail_unless(smlSessionReceiveBody(session, parser, &error), NULL);
	
	SmlCommand *cmd = smlCommandNewAlert(SML_ALERT_TWO_WAY, loc, loc, "last", "next", NULL, &error);
	sml_fail_unless(cmd != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlLocationUnref(loc);
	
	sml_fail_unless(smlSessionSendCommand(session, cmd, NULL, _status_reply, GINT_TO_POINTER(1), &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlCommandUnref(cmd);
	
	smlSessionDispatch(session);
	smlSessionDispatch(session);
	
	sml_fail_unless(smlSessionFlush(session, TRUE, &error), NULL);
	
	smlSessionDispatch(session);
	
	smlParserFree(parser);
	smlHeaderFree(header);
	
	sml_fail_unless(start_commands_received == 5, NULL);
	sml_fail_unless(finals_received == 1, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	sml_fail_unless(to_session2 != NULL, NULL);
	smlTransportDataDeref(to_session2);
	to_session2 = NULL;

	/* libsyncml does not reset the CmdID during a session.
	 * So the CmdID must be the total number of all send commands.
	 */
	input2 = "<SyncML xmlns=\"SYNCML:SYNCML1.1\"><SyncHdr><VerProto>SyncML/1.1</VerProto><VerDTD>1.1</VerDTD><MsgID>5</MsgID><SessionID>1</SessionID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source></SyncHdr><SyncBody><Status><CmdID>1</CmdID><MsgRef>4</MsgRef><CmdRef>16</CmdRef><Cmd>Alert</Cmd><Data>200</Data><SourceRef>/vcards</SourceRef><TargetRef>/vcards</TargetRef></Status><Sync><CmdID>2</CmdID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source><Add><CmdID>3</CmdID><Meta><Type xmlns=\"syncml:metinf\">text/x-vcard</Type></Meta><Item><Source><LocURI>uid</LocURI></Source><Data><![CDATA[datadatadatadatatest]]></Data></Item></Add></Sync><Final></Final></SyncBody></SyncML>";
	
	parser = smlParserNew(SML_MIMETYPE_XML, 0, &error);
	sml_fail_unless(parser != NULL, NULL);
	sml_fail_unless(smlParserStart(parser, input2, strlen(input2), &error), NULL);
	
	sml_fail_unless(smlParserGetHeader(parser, &header, &cred, &error), NULL);
	sml_fail_unless(header != NULL, NULL);
	sml_fail_unless(cred == NULL, NULL);
	
	sml_fail_unless(smlSessionReceiveHeader(session, header, &error), NULL);
	
	reply = smlStatusNew(SML_NO_ERROR, 0, 5, session->source, session->target, SML_COMMAND_TYPE_HEADER, &error);
	sml_fail_unless(smlSessionSendReply(session, reply, &error), NULL);
	smlStatusUnref(reply);
	session->established = TRUE;
	
	sml_fail_unless(smlSessionReceiveBody(session, parser, &error), NULL);
	
	smlParserFree(parser);
	smlHeaderFree(header);
	
	sml_fail_unless(start_commands_received == 7, NULL);
	sml_fail_unless(finals_received == 2, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	smlSessionUnref(session);

	cleanup_testbed();
}
END_TEST

START_TEST (session_recv_wrong_order)
{
	init_testbed();
	
	SmlError *error = NULL;
	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	const char *input = "<SyncML xmlns=\"SYNCML:SYNCML1.1\"><SyncHdr><VerProto>SyncML/1.1</VerProto><VerDTD>1.1</VerDTD><MsgID>1</MsgID><SessionID>1</SessionID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source></SyncHdr><SyncBody><Sync><CmdID>1</CmdID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source><NumberOfChanges>1</NumberOfChanges><Add><CmdID>2</CmdID><Meta><Type xmlns=\"syncml:metinf\">text/x-vcard</Type><Size>8</Size></Meta><Item><Source><LocURI>uid</LocURI></Source><Data><![CDATA[data]]></Data><MoreData></MoreData></Item></Add><Add><CmdID>3</CmdID><Meta><Type xmlns=\"syncml:metinf\">text/x-vcard</Type></Meta><Item><Source><LocURI>uid</LocURI></Source><Data><![CDATA[data]]></Data></Item></Add></Sync></SyncBody></SyncML>";
	
	SmlParser *parser = smlParserNew(SML_MIMETYPE_XML, 0, &error);
	sml_fail_unless(parser != NULL, NULL);
	sml_fail_unless(smlParserStart(parser, input, strlen(input), &error), NULL);
	
	SmlHeader *header = NULL;
	SmlCred *cred = NULL;
	sml_fail_unless(smlParserGetHeader(parser, &header, &cred, &error), NULL);
	sml_fail_unless(header != NULL, NULL);
	sml_fail_unless(cred == NULL, NULL);
	
	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_SERVER, SML_MIMETYPE_XML, SML_VERSION_11, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	smlSessionSetLocalMaxMsgSize(session, defaultMaxMsgSize);
	smlSessionSetLocalMaxObjSize(session, defaultMaxObjSize);
	
	smlLocationUnref(loc);
	
	smlSessionSetEventCallback(session, _event_callback, GINT_TO_POINTER(2));
	smlSessionSetDataCallback(session, _data_send_callback, &to_session2);
	
	sml_fail_unless(smlSessionReceiveHeader(session, header, &error), NULL);
	session->established = TRUE;
	sml_fail_unless(!smlSessionReceiveBody(session, parser, &error), NULL);
	sml_fail_unless(error != NULL, NULL);
	smlErrorDeref(&error);
	
	smlParserFree(parser);
	smlHeaderFree(header);

	sml_fail_unless(start_commands_received == 1, "%d != 1", start_commands_received);
	sml_fail_unless(finals_received == 0, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	smlSessionUnref(session);

	cleanup_testbed();
}
END_TEST

START_TEST (session_large_obj_10)
{
	init_testbed();
	
	SmlError *error = NULL;
	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	const char *input = "<SyncML xmlns=\"SYNCML:SYNCML1.0\"><SyncHdr><VerProto>SyncML/1.0</VerProto><VerDTD>1.0</VerDTD><MsgID>1</MsgID><SessionID>1</SessionID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source></SyncHdr><SyncBody><Sync><CmdID>1</CmdID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source><NumberOfChanges>1</NumberOfChanges><Add><CmdID>2</CmdID><Meta><Type xmlns=\"syncml:metinf\">text/x-vcard</Type><Size>8</Size></Meta><Item><Source><LocURI>uid</LocURI></Source><Data><![CDATA[data]]></Data><MoreData></MoreData></Item></Add></Sync></SyncBody></SyncML>";
	
	SmlParser *parser = smlParserNew(SML_MIMETYPE_XML, 0, &error);
	sml_fail_unless(parser != NULL, NULL);
	sml_fail_unless(smlParserStart(parser, input, strlen(input), &error), NULL);
	
	SmlHeader *header = NULL;
	SmlCred *cred = NULL;
	sml_fail_unless(smlParserGetHeader(parser, &header, &cred, &error), NULL);
	sml_fail_unless(header != NULL, NULL);
	sml_fail_unless(cred == NULL, NULL);
	
	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_SERVER, SML_MIMETYPE_XML, SML_VERSION_10, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	smlSessionSetLocalMaxMsgSize(session, defaultMaxMsgSize);
	smlSessionSetLocalMaxObjSize(session, defaultMaxObjSize);
	
	smlSessionSetEventCallback(session, _event_callback, GINT_TO_POINTER(2));
	smlSessionSetDataCallback(session, _data_send_callback, &to_session2);
	
	sml_fail_unless(smlSessionReceiveHeader(session, header, &error), NULL);
	
	SmlStatus *reply = smlStatusNew(SML_NO_ERROR, 0, 1, session->source, session->target, SML_COMMAND_TYPE_HEADER, &error);
	sml_fail_unless(smlSessionSendReply(session, reply, &error), NULL);
	smlStatusUnref(reply);
	session->established = TRUE;
	
	sml_fail_unless(!smlSessionReceiveBody(session, parser, &error), NULL);
	sml_fail_unless(error != NULL, NULL);
	smlErrorDeref(&error);
	
	smlParserFree(parser);
	smlHeaderFree(header);
	
	sml_fail_unless(start_commands_received == 1, NULL);
	sml_fail_unless(finals_received == 0, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	smlSessionUnref(session);
	
	current_cmd_id = 1;
	
	input = "<SyncML xmlns=\"SYNCML:SYNCML1.1\"><SyncHdr><VerProto>SyncML/1.1</VerProto><VerDTD>1.1</VerDTD><MsgID>1</MsgID><SessionID>1</SessionID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source></SyncHdr><SyncBody><Sync><CmdID>1</CmdID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source><NumberOfChanges>1</NumberOfChanges><Add><CmdID>2</CmdID><Meta><Type xmlns=\"syncml:metinf\">text/x-vcard</Type><Size>8</Size></Meta><Item><Source><LocURI>uid</LocURI></Source><Data><![CDATA[data]]></Data><MoreData></MoreData></Item></Add></Sync></SyncBody></SyncML>";
	
	parser = smlParserNew(SML_MIMETYPE_XML, 0, &error);
	sml_fail_unless(parser != NULL, NULL);
	sml_fail_unless(smlParserStart(parser, input, strlen(input), &error), NULL);
	
	sml_fail_unless(smlParserGetHeader(parser, &header, &cred, &error), NULL);
	sml_fail_unless(header != NULL, NULL);
	sml_fail_unless(cred == NULL, NULL);
	
	session = smlSessionNew(SML_SESSION_TYPE_SERVER, SML_MIMETYPE_XML, SML_VERSION_11, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	smlSessionSetLocalMaxMsgSize(session, defaultMaxMsgSize);
	smlSessionSetLocalMaxObjSize(session, defaultMaxObjSize);
	
	smlLocationUnref(loc);
	
	smlSessionSetEventCallback(session, _event_callback, GINT_TO_POINTER(2));
	smlSessionSetDataCallback(session, _data_send_callback, &to_session2);
	
	sml_fail_unless(smlSessionReceiveHeader(session, header, &error), NULL);
	
	reply = smlStatusNew(SML_NO_ERROR, 0, 1, session->source, session->target, SML_COMMAND_TYPE_HEADER, &error);
	sml_fail_unless(smlSessionSendReply(session, reply, &error), NULL);
	smlStatusUnref(reply);
	session->established = TRUE;
	
	sml_fail_unless(smlSessionReceiveBody(session, parser, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlParserFree(parser);
	smlHeaderFree(header);
	
	sml_fail_unless(start_commands_received == 2, NULL);
	sml_fail_unless(finals_received == 0, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	sml_fail_unless(to_session2 != NULL, NULL);
	smlTransportDataDeref(to_session2);
	to_session2 = NULL;
	
	smlSessionUnref(session);

	cleanup_testbed();
}
END_TEST

START_TEST (session_large_obj_open)
{
	init_testbed();
	
	SmlError *error = NULL;
	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	const char *input = "<SyncML xmlns=\"SYNCML:SYNCML1.1\"><SyncHdr><VerProto>SyncML/1.1</VerProto><VerDTD>1.1</VerDTD><MsgID>1</MsgID><SessionID>1</SessionID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source></SyncHdr><SyncBody><Sync><CmdID>1</CmdID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source><NumberOfChanges>1</NumberOfChanges><Add><CmdID>2</CmdID><Meta><Type xmlns=\"syncml:metinf\">text/x-vcard</Type><Size>8</Size></Meta><Item><Source><LocURI>uid</LocURI></Source><Data><![CDATA[data]]></Data><MoreData></MoreData></Item></Add></Sync><Final/></SyncBody></SyncML>";
	
	SmlParser *parser = smlParserNew(SML_MIMETYPE_XML, 0, &error);
	sml_fail_unless(parser != NULL, NULL);
	sml_fail_unless(smlParserStart(parser, input, strlen(input), &error), NULL);
	
	SmlHeader *header = NULL;
	SmlCred *cred = NULL;
	sml_fail_unless(smlParserGetHeader(parser, &header, &cred, &error), NULL);
	sml_fail_unless(header != NULL, NULL);
	sml_fail_unless(cred == NULL, NULL);
	
	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_SERVER, SML_MIMETYPE_XML, SML_VERSION_10, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	smlSessionSetLocalMaxMsgSize(session, defaultMaxMsgSize);
	smlSessionSetLocalMaxObjSize(session, defaultMaxObjSize);
	
	smlSessionSetEventCallback(session, _event_callback, GINT_TO_POINTER(2));
	smlSessionSetDataCallback(session, _data_send_callback, &to_session2);
	
	sml_fail_unless(smlSessionReceiveHeader(session, header, &error), NULL);
	
	SmlStatus *reply = smlStatusNew(SML_NO_ERROR, 0, 1, session->source, session->target, SML_COMMAND_TYPE_HEADER, &error);
	sml_fail_unless(smlSessionSendReply(session, reply, &error), NULL);
	smlStatusUnref(reply);
	session->established = TRUE;
	
	sml_fail_unless(!smlSessionReceiveBody(session, parser, &error), NULL);
	sml_fail_unless(error != NULL, NULL);
	smlErrorDeref(&error);
	
	smlParserFree(parser);
	smlHeaderFree(header);

	/* Even if MoreData and Final is impossible in the same message
	 * then this is no reason why the received final should not be
	 * signaled. If the syntax of the message is wrong then only
	 * the syntax handler (ReceiveBody) should fail. Any other
	 * stuff is undefinite.
	 */
	sml_fail_unless(start_commands_received == 1, NULL);
	sml_fail_unless(finals_received == 1, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	smlLocationUnref(loc);
	
	smlSessionUnref(session);

	cleanup_testbed();
}
END_TEST

START_TEST (session_large_obj_no_size)
{
	init_testbed();
	
	SmlError *error = NULL;
	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	const char *input = "<SyncML xmlns=\"SYNCML:SYNCML1.1\"><SyncHdr><VerProto>SyncML/1.1</VerProto><VerDTD>1.1</VerDTD><MsgID>1</MsgID><SessionID>1</SessionID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source></SyncHdr><SyncBody><Sync><CmdID>1</CmdID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source><NumberOfChanges>1</NumberOfChanges><Add><CmdID>2</CmdID><Meta><Type xmlns=\"syncml:metinf\">text/x-vcard</Type></Meta><Item><Source><LocURI>uid</LocURI></Source><Data><![CDATA[data]]></Data><MoreData></MoreData></Item></Add></Sync></SyncBody></SyncML>";
	
	SmlParser *parser = smlParserNew(SML_MIMETYPE_XML, 0, &error);
	sml_fail_unless(parser != NULL, NULL);
	sml_fail_unless(smlParserStart(parser, input, strlen(input), &error), NULL);
	
	SmlHeader *header = NULL;
	SmlCred *cred = NULL;
	sml_fail_unless(smlParserGetHeader(parser, &header, &cred, &error), NULL);
	sml_fail_unless(header != NULL, NULL);
	sml_fail_unless(cred == NULL, NULL);
	
	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_SERVER, SML_MIMETYPE_XML, SML_VERSION_11, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	smlSessionSetLocalMaxMsgSize(session, defaultMaxMsgSize);
	smlSessionSetLocalMaxObjSize(session, defaultMaxObjSize);
	
	smlSessionSetEventCallback(session, _event_callback, GINT_TO_POINTER(2));
	smlSessionSetDataCallback(session, _data_send_callback, &to_session2);
	
	sml_fail_unless(smlSessionReceiveHeader(session, header, &error), NULL);
	
	SmlStatus *reply = smlStatusNew(SML_NO_ERROR, 0, 1, session->source, session->target, SML_COMMAND_TYPE_HEADER, &error);
	sml_fail_unless(smlSessionSendReply(session, reply, &error), NULL);
	smlStatusUnref(reply);
	session->established = TRUE;
	
	sml_fail_unless(!smlSessionReceiveBody(session, parser, &error), NULL);
	sml_fail_unless(error != NULL, NULL);
	smlErrorDeref(&error);
	
	smlParserFree(parser);
	smlHeaderFree(header);
	
	sml_fail_unless(start_commands_received == 1, NULL);
	sml_fail_unless(finals_received == 0, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	smlLocationUnref(loc);
	
	smlSessionUnref(session);

	cleanup_testbed();
}
END_TEST

START_TEST (session_large_obj_several_size)
{
	init_testbed();
	
	SmlError *error = NULL;
	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	const char *input = "<SyncML xmlns=\"SYNCML:SYNCML1.1\"><SyncHdr><VerProto>SyncML/1.1</VerProto><VerDTD>1.1</VerDTD><MsgID>1</MsgID><SessionID>1</SessionID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source></SyncHdr><SyncBody><Sync><CmdID>1</CmdID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source><NumberOfChanges>1</NumberOfChanges><Add><CmdID>2</CmdID><Meta><Type xmlns=\"syncml:metinf\">text/x-vcard</Type><Size>8</Size></Meta><Item><Source><LocURI>uid</LocURI></Source><Data><![CDATA[data]]></Data><MoreData></MoreData></Item></Add></Sync></SyncBody></SyncML>";
	
	SmlParser *parser = smlParserNew(SML_MIMETYPE_XML, 0, &error);
	sml_fail_unless(parser != NULL, NULL);
	sml_fail_unless(smlParserStart(parser, input, strlen(input), &error), NULL);
	
	SmlHeader *header = NULL;
	SmlCred *cred = NULL;
	sml_fail_unless(smlParserGetHeader(parser, &header, &cred, &error), NULL);
	sml_fail_unless(header != NULL, NULL);
	sml_fail_unless(cred == NULL, NULL);
	
	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_SERVER, SML_MIMETYPE_XML, SML_VERSION_11, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	smlSessionSetLocalMaxMsgSize(session, defaultMaxMsgSize);
	smlSessionSetLocalMaxObjSize(session, defaultMaxObjSize);
	
	smlLocationUnref(loc);
	
	smlSessionSetEventCallback(session, _event_callback, GINT_TO_POINTER(3));
	smlSessionSetDataCallback(session, _data_send_callback, &to_session2);
	
	sml_fail_unless(smlSessionReceiveHeader(session, header, &error), NULL);
	
	SmlStatus *reply = smlStatusNew(SML_NO_ERROR, 0, 1, session->source, session->target, SML_COMMAND_TYPE_HEADER, &error);
	sml_fail_unless(smlSessionSendReply(session, reply, &error), NULL);
	smlStatusUnref(reply);
	session->established = TRUE;
	
	sml_fail_unless(smlSessionReceiveBody(session, parser, &error), NULL);
	
	smlParserFree(parser);
	smlHeaderFree(header);
	
	sml_fail_unless(start_commands_received == 1, NULL);
	sml_fail_unless(finals_received == 0, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	sml_fail_unless(to_session2 != NULL, NULL);
	smlTransportDataDeref(to_session2);
	to_session2 = NULL;
	
	const char *input2 = "<SyncML xmlns=\"SYNCML:SYNCML1.1\"><SyncHdr><VerProto>SyncML/1.1</VerProto><VerDTD>1.1</VerDTD><MsgID>2</MsgID><SessionID>1</SessionID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source></SyncHdr><SyncBody><Sync><CmdID>1</CmdID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source><Add><CmdID>2</CmdID><Meta><Type xmlns=\"syncml:metinf\">text/x-vcard</Type><Size>8</Size></Meta><Item><Source><LocURI>uid</LocURI></Source><Data><![CDATA[data]]></Data></Item></Add></Sync><Final></Final></SyncBody></SyncML>";
	
	parser = smlParserNew(SML_MIMETYPE_XML, 0, &error);
	sml_fail_unless(parser != NULL, NULL);
	sml_fail_unless(smlParserStart(parser, input2, strlen(input2), &error), NULL);
	
	sml_fail_unless(smlParserGetHeader(parser, &header, &cred, &error), NULL);
	sml_fail_unless(header != NULL, NULL);
	sml_fail_unless(cred == NULL, NULL);
	
	sml_fail_unless(smlSessionReceiveHeader(session, header, &error), NULL);
	
	reply = smlStatusNew(SML_NO_ERROR, 0, 1, session->source, session->target, SML_COMMAND_TYPE_HEADER, &error);
	sml_fail_unless(smlSessionSendReply(session, reply, &error), NULL);
	smlStatusUnref(reply);
	session->established = TRUE;
	
	sml_fail_unless(!smlSessionReceiveBody(session, parser, &error), NULL);
	sml_fail_unless(error != NULL, NULL);
	smlErrorDeref(&error);
	
	smlParserFree(parser);
	smlHeaderFree(header);
	
	sml_fail_unless(start_commands_received == 2, NULL);
	sml_fail_unless(finals_received == 0, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	smlSessionUnref(session);

	cleanup_testbed();
}
END_TEST

START_TEST (session_recv_large_wrong_size)
{
	init_testbed();
	
	SmlError *error = NULL;
	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	const char *input = "<SyncML xmlns=\"SYNCML:SYNCML1.1\"><SyncHdr><VerProto>SyncML/1.1</VerProto><VerDTD>1.1</VerDTD><MsgID>1</MsgID><SessionID>1</SessionID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source></SyncHdr><SyncBody><Sync><CmdID>1</CmdID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source><NumberOfChanges>1</NumberOfChanges><Add><CmdID>2</CmdID><Meta><Type xmlns=\"syncml:metinf\">text/x-vcard</Type><Size>9</Size></Meta><Item><Source><LocURI>uid</LocURI></Source><Data><![CDATA[data]]></Data><MoreData></MoreData></Item></Add></Sync></SyncBody></SyncML>";
	
	SmlParser *parser = smlParserNew(SML_MIMETYPE_XML, 0, &error);
	sml_fail_unless(parser != NULL, NULL);
	sml_fail_unless(smlParserStart(parser, input, strlen(input), &error), NULL);
	
	SmlHeader *header = NULL;
	SmlCred *cred = NULL;
	sml_fail_unless(smlParserGetHeader(parser, &header, &cred, &error), NULL);
	sml_fail_unless(header != NULL, NULL);
	sml_fail_unless(cred == NULL, NULL);
	
	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_SERVER, SML_MIMETYPE_XML, SML_VERSION_11, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	smlSessionSetLocalMaxMsgSize(session, defaultMaxMsgSize);
	smlSessionSetLocalMaxObjSize(session, defaultMaxObjSize);
	
	smlLocationUnref(loc);
	
	smlSessionSetEventCallback(session, _event_callback, GINT_TO_POINTER(3));
	smlSessionSetDataCallback(session, _data_send_callback, &to_session2);
	
	sml_fail_unless(smlSessionReceiveHeader(session, header, &error), NULL);
	
	SmlStatus *reply = smlStatusNew(SML_NO_ERROR, 0, 1, session->source, session->target, SML_COMMAND_TYPE_HEADER, &error);
	sml_fail_unless(smlSessionSendReply(session, reply, &error), NULL);
	smlStatusUnref(reply);
	session->established = TRUE;
	
	sml_fail_unless(smlSessionReceiveBody(session, parser, &error), NULL);
	
	smlParserFree(parser);
	smlHeaderFree(header);
	
	sml_fail_unless(start_commands_received == 1, NULL);
	sml_fail_unless(finals_received == 0, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	sml_fail_unless(to_session2 != NULL, NULL);
	smlTransportDataDeref(to_session2);
	to_session2 = NULL;
	
	const char *input2 = "<SyncML xmlns=\"SYNCML:SYNCML1.1\"><SyncHdr><VerProto>SyncML/1.1</VerProto><VerDTD>1.1</VerDTD><MsgID>2</MsgID><SessionID>1</SessionID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source></SyncHdr><SyncBody><Sync><CmdID>1</CmdID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source><Add><CmdID>2</CmdID><Meta><Type xmlns=\"syncml:metinf\">text/x-vcard</Type></Meta><Item><Source><LocURI>uid</LocURI></Source><Data><![CDATA[data]]></Data></Item></Add></Sync><Final></Final></SyncBody></SyncML>";
	
	parser = smlParserNew(SML_MIMETYPE_XML, 0, &error);
	sml_fail_unless(parser != NULL, NULL);
	sml_fail_unless(smlParserStart(parser, input2, strlen(input2), &error), NULL);
	
	sml_fail_unless(smlParserGetHeader(parser, &header, &cred, &error), NULL);
	sml_fail_unless(header != NULL, NULL);
	sml_fail_unless(cred == NULL, NULL);
	
	sml_fail_unless(smlSessionReceiveHeader(session, header, &error), NULL);
	
	reply = smlStatusNew(SML_NO_ERROR, 0, 1, session->source, session->target, SML_COMMAND_TYPE_HEADER, &error);
	sml_fail_unless(smlSessionSendReply(session, reply, &error), NULL);
	smlStatusUnref(reply);
	session->established = TRUE;
	
	sml_fail_unless(!smlSessionReceiveBody(session, parser, &error), NULL);
	sml_fail_unless(error != NULL, NULL);
	smlErrorDeref(&error);
	
	smlParserFree(parser);
	smlHeaderFree(header);
	
	sml_fail_unless(start_commands_received == 2, NULL);
	sml_fail_unless(finals_received == 0, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	smlSessionUnref(session);

	cleanup_testbed();
}
END_TEST

START_TEST (session_recv_large_wrong_size2)
{
	init_testbed();
	
	SmlError *error = NULL;
	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	const char *input = "<SyncML xmlns=\"SYNCML:SYNCML1.1\"><SyncHdr><VerProto>SyncML/1.1</VerProto><VerDTD>1.1</VerDTD><MsgID>1</MsgID><SessionID>1</SessionID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source></SyncHdr><SyncBody><Sync><CmdID>1</CmdID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source><NumberOfChanges>1</NumberOfChanges><Add><CmdID>2</CmdID><Meta><Type xmlns=\"syncml:metinf\">text/x-vcard</Type><Size>7</Size></Meta><Item><Source><LocURI>uid</LocURI></Source><Data><![CDATA[data]]></Data><MoreData></MoreData></Item></Add></Sync></SyncBody></SyncML>";
	
	SmlParser *parser = smlParserNew(SML_MIMETYPE_XML, 0, &error);
	sml_fail_unless(parser != NULL, NULL);
	sml_fail_unless(smlParserStart(parser, input, strlen(input), &error), NULL);
	
	SmlHeader *header = NULL;
	SmlCred *cred = NULL;
	sml_fail_unless(smlParserGetHeader(parser, &header, &cred, &error), NULL);
	sml_fail_unless(header != NULL, NULL);
	sml_fail_unless(cred == NULL, NULL);
	
	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_SERVER, SML_MIMETYPE_XML, SML_VERSION_11, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	smlSessionSetLocalMaxMsgSize(session, defaultMaxMsgSize);
	smlSessionSetLocalMaxObjSize(session, defaultMaxObjSize);
	
	smlLocationUnref(loc);
	
	smlSessionSetEventCallback(session, _event_callback, GINT_TO_POINTER(3));
	smlSessionSetDataCallback(session, _data_send_callback, &to_session2);
	
	sml_fail_unless(smlSessionReceiveHeader(session, header, &error), NULL);
	
	SmlStatus *reply = smlStatusNew(SML_NO_ERROR, 0, 1, session->source, session->target, SML_COMMAND_TYPE_HEADER, &error);
	sml_fail_unless(smlSessionSendReply(session, reply, &error), NULL);
	smlStatusUnref(reply);
	session->established = TRUE;
	
	sml_fail_unless(smlSessionReceiveBody(session, parser, &error), NULL);
	
	smlParserFree(parser);
	smlHeaderFree(header);
	
	sml_fail_unless(start_commands_received == 1, NULL);
	sml_fail_unless(finals_received == 0, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	sml_fail_unless(to_session2 != NULL, NULL);
	smlTransportDataDeref(to_session2);
	to_session2 = NULL;
	
	const char *input2 = "<SyncML xmlns=\"SYNCML:SYNCML1.1\"><SyncHdr><VerProto>SyncML/1.1</VerProto><VerDTD>1.1</VerDTD><MsgID>2</MsgID><SessionID>1</SessionID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source></SyncHdr><SyncBody><Sync><CmdID>1</CmdID><Target><LocURI>/vcards</LocURI></Target><Source><LocURI>/vcards</LocURI></Source><Add><CmdID>2</CmdID><Meta><Type xmlns=\"syncml:metinf\">text/x-vcard</Type></Meta><Item><Source><LocURI>uid</LocURI></Source><Data><![CDATA[data]]></Data></Item></Add></Sync><Final></Final></SyncBody></SyncML>";
	
	parser = smlParserNew(SML_MIMETYPE_XML, 0, &error);
	sml_fail_unless(parser != NULL, NULL);
	sml_fail_unless(smlParserStart(parser, input2, strlen(input2), &error), NULL);
	
	sml_fail_unless(smlParserGetHeader(parser, &header, &cred, &error), NULL);
	sml_fail_unless(header != NULL, NULL);
	sml_fail_unless(cred == NULL, NULL);
	
	sml_fail_unless(smlSessionReceiveHeader(session, header, &error), NULL);
	
	reply = smlStatusNew(SML_NO_ERROR, 0, 1, session->source, session->target, SML_COMMAND_TYPE_HEADER, &error);
	sml_fail_unless(smlSessionSendReply(session, reply, &error), NULL);
	smlStatusUnref(reply);
	session->established = TRUE;
	
	sml_fail_unless(!smlSessionReceiveBody(session, parser, &error), NULL);
	sml_fail_unless(error != NULL, NULL);
	smlErrorDeref(&error);
	
	smlParserFree(parser);
	smlHeaderFree(header);
	
	sml_fail_unless(start_commands_received == 2, NULL);
	sml_fail_unless(finals_received == 0, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	smlSessionUnref(session);

	cleanup_testbed();
}
END_TEST

START_TEST (session_send_large_obj)
{
	init_testbed();

	int limit = 1000;
	int objlength = 600;
	
	SmlError *error = NULL;
	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_CLIENT, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	SmlSessionThread *thread1 = smlSessionRunAsync(session, &to_session);
	SmlSession *session2 = smlSessionNew(SML_SESSION_TYPE_SERVER, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	SmlSessionThread *thread2 = smlSessionRunAsync(session2, &to_session2);
	
	smlSessionSetEventCallback(session2, _event_callback, GINT_TO_POINTER(5));
	smlSessionSetDataCallback(session2, _data_send_callback, &to_session);
	smlSessionSetEventCallback(session, _event_callback, GINT_TO_POINTER(5));
	smlSessionSetDataCallback(session, _data_send_callback, &to_session2);
	
	// smlSessionSetLocalMaxMsgSize(session, limit);
	smlSessionSetRemoteMaxMsgSize(session, limit);
	smlSessionSetRemoteMaxObjSize(session, defaultMaxObjSize);
	smlSessionSetLocalMaxMsgSize(session2, defaultMaxMsgSize);
	smlSessionSetLocalMaxObjSize(session2, defaultMaxObjSize);
	
	SmlCommand *cmd1 = smlCommandNewSync(loc, loc, 0, &error);
	sml_fail_unless(cmd1 != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlLocationUnref(loc);
	
	sml_fail_unless(smlSessionStartCommand(session, cmd1, NULL, _status_reply, GINT_TO_POINTER(1), &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	item_data = smlRandStr(objlength, TRUE);
	item_size = objlength;
	
	SmlCommand *cmd2 = smlCommandNewChange(SML_CHANGE_ADD, "uid", item_data, item_size, SML_ELEMENT_TEXT_VCARD, &error);
	
	sml_fail_unless(smlSessionSendCommand(session, cmd2, cmd1, _status_reply, GINT_TO_POINTER(1), &error), NULL);
	sml_fail_unless(error == NULL, NULL);

	sml_fail_unless(smlSessionEndCommand(session, cmd1, &error), NULL);
	sml_fail_unless(error == NULL, NULL);

	smlCommandUnref(cmd1);
	smlCommandUnref(cmd2);

	sml_fail_unless(smlSessionEndCommand(session, NULL, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	sml_fail_unless(smlSessionFlush(session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);

	while (finals_received < 1) {
		usleep(100);
	}

	/* The data is additionally set to test the size of the
	 * resulting message. So the variable name data_received
	 * is not fully correct.
	 */
	sml_fail_unless(data_received >= 3, NULL);
	sml_fail_unless(numreplies == 1, NULL);
	/* 3x sync (in each of the three messages) and 1x add (large obj) */
	sml_fail_unless(start_commands_received == 4, NULL);
	sml_fail_unless(finals_received == 1, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	g_free(item_data);
	
	smlSessionStop(thread1);
	smlSessionStop(thread2);
	smlSessionUnref(session2);
	smlSessionUnref(session);

	cleanup_testbed();
}
END_TEST

START_TEST (session_send_large_obj2)
{
	init_testbed();
	
	int limit = 1000;
	int objlength = 4000;
	
	SmlError *error = NULL;
	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_CLIENT, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	SmlSessionThread *thread1 = smlSessionRunAsync(session, &to_session);
	SmlSession *session2 = smlSessionNew(SML_SESSION_TYPE_SERVER, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	SmlSessionThread *thread2 = smlSessionRunAsync(session2, &to_session2);
	
	smlSessionSetEventCallback(session2, _event_callback, GINT_TO_POINTER(5));
	smlSessionSetDataCallback(session2, _data_send_callback, &to_session);
	smlSessionSetEventCallback(session, _event_callback, GINT_TO_POINTER(5));
	smlSessionSetDataCallback(session, _data_send_callback, &to_session2);
	
	smlSessionSetLocalMaxMsgSize(session, limit);
	smlSessionSetRemoteMaxMsgSize(session, limit);
	smlSessionSetLocalMaxMsgSize(session2, defaultMaxMsgSize);
	smlSessionSetLocalMaxObjSize(session2, defaultMaxObjSize);
	
	SmlCommand *cmd1 = smlCommandNewSync(loc, loc, 0, &error);
	sml_fail_unless(cmd1 != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlLocationUnref(loc);
	
	sml_fail_unless(smlSessionStartCommand(session, cmd1, NULL, _status_reply, GINT_TO_POINTER(1), &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	item_data = smlRandStr(objlength, TRUE);
	item_size = objlength;
	
	SmlCommand *cmd2 = smlCommandNewChange(SML_CHANGE_ADD, "uid", item_data, item_size, SML_ELEMENT_TEXT_VCARD, &error);
	
	sml_fail_unless(smlSessionSendCommand(session, cmd2, cmd1, _status_reply, GINT_TO_POINTER(1), &error), NULL);
	sml_fail_unless(error == NULL, NULL);

	sml_fail_unless(smlSessionEndCommand(session, cmd1, &error), NULL);
	sml_fail_unless(error == NULL, NULL);

	smlCommandUnref(cmd1);
	smlCommandUnref(cmd2);

	sml_fail_unless(smlSessionEndCommand(session, NULL, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	sml_fail_unless(smlSessionFlush(session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	while (finals_received != 1) {
		usleep(100);
	}
	
	sml_fail_unless(finals_received == 1, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	g_free(item_data);
	
	smlSessionStop(thread1);
	smlSessionStop(thread2);
	smlSessionUnref(session2);
	smlSessionUnref(session);

	cleanup_testbed();
}
END_TEST

START_TEST (session_send_multi_large_obj)
{
	init_testbed();
	
	int limit = 1000;
	int objlength = 1500;
	int num_obj = 10;
	
	SmlError *error = NULL;
	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_CLIENT, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	SmlSessionThread *thread1 = smlSessionRunAsync(session, &to_session);
	SmlSession *session2 = smlSessionNew(SML_SESSION_TYPE_SERVER, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	SmlSessionThread *thread2 = smlSessionRunAsync(session2, &to_session2);
	
	smlSessionSetEventCallback(session2, _event_callback, GINT_TO_POINTER(5));
	smlSessionSetDataCallback(session2, _data_send_callback, &to_session);
	smlSessionSetEventCallback(session, _event_callback, GINT_TO_POINTER(5));
	smlSessionSetDataCallback(session, _data_send_callback, &to_session2);
	
	smlSessionSetLocalMaxMsgSize(session, limit);
	smlSessionSetRemoteMaxMsgSize(session, limit);
	smlSessionSetLocalMaxMsgSize(session2, defaultMaxMsgSize);
	smlSessionSetLocalMaxObjSize(session2, defaultMaxObjSize);
	
	SmlCommand *cmd1 = smlCommandNewSync(loc, loc, 0, &error);
	sml_fail_unless(cmd1 != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlLocationUnref(loc);
	
	sml_fail_unless(smlSessionStartCommand(session, cmd1, NULL, _status_reply, GINT_TO_POINTER(1), &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	item_data = smlRandStr(objlength, TRUE);
	item_size = objlength;
	
	SmlCommand *cmd2 = NULL;
	
	int i = 0;
	for (i = 0; i < num_obj; i++) {
		cmd2 = smlCommandNewChange(SML_CHANGE_ADD, "uid", item_data, item_size, SML_ELEMENT_TEXT_VCARD, &error);
		
		sml_fail_unless(smlSessionSendCommand(session, cmd2, cmd1, _status_reply, GINT_TO_POINTER(1), &error), NULL);
		sml_fail_unless(error == NULL, NULL);
	
		smlCommandUnref(cmd2);
	}
	
	smlCommandUnref(cmd1);

	sml_fail_unless(smlSessionEndCommand(session, NULL, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	sml_fail_unless(smlSessionFlush(session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	while (finals_received < 1) {
		usleep(100);
	}
	
	sml_fail_unless(finals_received == 1, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	g_free(item_data);
	
	smlSessionStop(thread1);
	smlSessionStop(thread2);
	smlSessionUnref(session2);
	smlSessionUnref(session);

	cleanup_testbed();
}
END_TEST

START_TEST (session_send_multi_large_obj_alter)
{
	init_testbed();
	
	int limit = 1000;
	int objlength = 1500;
	int num_obj = 5;
	
	SmlError *error = NULL;
	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_CLIENT, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	SmlSessionThread *thread1 = smlSessionRunAsync(session, &to_session);
	SmlSession *session2 = smlSessionNew(SML_SESSION_TYPE_SERVER, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	SmlSessionThread *thread2 = smlSessionRunAsync(session2, &to_session2);
	
	smlSessionSetEventCallback(session2, _event_callback, GINT_TO_POINTER(5));
	smlSessionSetDataCallback(session2, _data_send_callback, &to_session);
	smlSessionSetEventCallback(session, _event_callback, GINT_TO_POINTER(5));
	smlSessionSetDataCallback(session, _data_send_callback, &to_session2);
	
	smlSessionSetLocalMaxMsgSize(session, limit);
	smlSessionSetRemoteMaxMsgSize(session, limit);
	smlSessionSetLocalMaxMsgSize(session2, defaultMaxMsgSize);
	smlSessionSetLocalMaxObjSize(session2, defaultMaxObjSize);
	
	SmlCommand *cmd1 = smlCommandNewSync(loc, loc, 0, &error);
	sml_fail_unless(cmd1 != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlLocationUnref(loc);
	
	sml_fail_unless(smlSessionStartCommand(session, cmd1, NULL, _status_reply, GINT_TO_POINTER(1), &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	item_data = smlRandStr(objlength, TRUE);
	item_size = objlength;
	
	SmlCommand *cmd2 = NULL;
	
	int i = 0;
	for (i = 0; i < num_obj; i++) {
		cmd2 = smlCommandNewChange(SML_CHANGE_ADD, "uid", item_data, item_size, SML_ELEMENT_TEXT_VCARD, &error);
		
		sml_fail_unless(smlSessionSendCommand(session, cmd2, cmd1, _status_reply, GINT_TO_POINTER(1), &error), NULL);
		sml_fail_unless(error == NULL, NULL);
	
		smlCommandUnref(cmd2);
		
		cmd2 = smlCommandNewChange(SML_CHANGE_ADD, "uid", "data", 4, SML_ELEMENT_TEXT_VCARD, &error);
		
		sml_fail_unless(smlSessionSendCommand(session, cmd2, cmd1, _status_reply, GINT_TO_POINTER(1), &error), NULL);
		sml_fail_unless(error == NULL, NULL);
	
		smlCommandUnref(cmd2);
	}
	
	smlCommandUnref(cmd1);

	sml_fail_unless(smlSessionEndCommand(session, NULL, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	sml_fail_unless(smlSessionFlush(session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	while (finals_received < 1) {
		usleep(100);
	}
	
	sml_fail_unless(num_changes = num_obj * 2, NULL);
	sml_fail_unless(finals_received == 1, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	g_free(item_data);
	
	smlSessionStop(thread1);
	smlSessionStop(thread2);
	smlSessionUnref(session2);
	smlSessionUnref(session);

	cleanup_testbed();
}
END_TEST

START_TEST (session_san)
{
	init_testbed();
	
	SmlError *error = NULL;
	SmlLocation *loc = smlLocationNew("/test", NULL, &error);
	sml_fail_unless(loc != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);

	SmlSession *session = smlSessionNew(SML_SESSION_TYPE_CLIENT, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	SmlSessionThread *thread1 = smlSessionRunAsync(session, &to_session);
	SmlSession *session2 = smlSessionNew(SML_SESSION_TYPE_SERVER, SML_MIMETYPE_XML, SML_VERSION_12, SML_PROTOCOL_SYNCML, loc, loc, "1", 0, &error);
	SmlSessionThread *thread2 = smlSessionRunAsync(session2, &to_session2);

	smlSessionSetLocalMaxMsgSize(session2, defaultMaxMsgSize);
	smlSessionSetLocalMaxObjSize(session2, defaultMaxObjSize);
	
	smlSessionSetEventCallback(session2, _event_callback, GINT_TO_POINTER(1));
	smlSessionSetDataCallback(session2, _data_send_callback, &to_session);
	smlSessionSetEventCallback(session, _event_callback, GINT_TO_POINTER(1));
	smlSessionSetDataCallback(session, _data_send_callback, &to_session2);
	
	SmlNotification *san = smlNotificationNew(SML_SAN_VERSION_11, SML_SAN_UIMODE_USER, SML_SAN_INITIATOR_SERVER, 65535, "test", "/", SML_MIMETYPE_XML, &error);
	sml_fail_unless(san != NULL, "%s", smlErrorPrint(&error));
	sml_fail_unless(error == NULL, "%s", smlErrorPrint(&error));
	SmlManager *manager = _start_manager();
	smlNotificationSetManager(san, manager);
	
	sml_fail_unless(smlNotificationNewAlert(san, SML_ALERT_TWO_WAY_BY_SERVER, SML_ELEMENT_TEXT_VCARD, "tttt", &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	char *buffer = NULL;
	unsigned int size = 0;
	sml_fail_unless(smlNotificationAssemble(san, &buffer, &size, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	smlNotificationFree(san);
	
	to_session = smlTransportDataNew(buffer, size, SML_MIMETYPE_XML, TRUE, &error);
	sml_fail_unless(to_session != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	while (finals_received != 1) {
		usleep(100);
	}
	
	sml_fail_unless(data_received == 0, NULL);
	sml_fail_unless(numreplies == 0, NULL);
	sml_fail_unless(start_commands_received == 1, NULL);
	sml_fail_unless(finals_received == 1, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	SmlCommand *cmd = smlCommandNewAlert(SML_ALERT_TWO_WAY, loc, loc, "last", "next", NULL, &error);
	sml_fail_unless(cmd != NULL, NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlLocationUnref(loc);
	
	sml_fail_unless(smlSessionSendCommand(session, cmd, NULL, _status_reply, GINT_TO_POINTER(1), &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	sml_fail_unless(smlSessionFlush(session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	while (finals_received != 2) {
		usleep(100);
	}
	
	sml_fail_unless(data_received == 1, NULL);
	sml_fail_unless(numreplies == 0, NULL);
	sml_fail_unless(start_commands_received == 2, NULL);
	sml_fail_unless(finals_received == 2, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	sml_fail_unless(smlSessionSendCommand(session2, cmd, NULL, _status_reply, GINT_TO_POINTER(1), &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	smlCommandUnref(cmd);
	
	sml_fail_unless(smlSessionFlush(session2, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	
	while (finals_received != 3) {
		usleep(100);
	}
	
	sml_fail_unless(data_received == 2, NULL);
	sml_fail_unless(numreplies == 1, NULL);
	sml_fail_unless(start_commands_received == 3, NULL);
	sml_fail_unless(finals_received == 3, NULL);
	sml_fail_unless(end_received == 0, NULL);
	
	sml_fail_unless(smlSessionFlush(session, TRUE, &error), NULL);
	sml_fail_unless(error == NULL, NULL);
	
	
	while (finals_received != 5 || end_received != 2) {
		usleep(100);
	}
	
	sml_fail_unless(data_received == 4, NULL);
	sml_fail_unless(numreplies == 2, NULL);
	sml_fail_unless(start_commands_received == 3, NULL);
	sml_fail_unless(finals_received == 5, NULL);
	sml_fail_unless(end_received == 2, NULL);
	
	smlSessionStop(thread1);
	smlSessionStop(thread2);
	smlSessionUnref(session2);
	smlSessionUnref(session);

	_stop_manager(manager);

	cleanup_testbed();
}
END_TEST

@SML_TESTCASE_CODE@

