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

#include "syncml.h"

#include "syncml_internals.h"
#include "sml_parse_internals.h"
#include "sml_elements_internals.h"
#include "sml_command_internals.h"
#include "sml_error_internals.h"

SmlLocation *smlLocationNew(const char *locURI, const char *locName, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%s, %s, %p)", __func__, VA_STRING(locURI), VA_STRING(locName), error);
	CHECK_ERROR_REF
	
	if (!locURI) {
		smlErrorSet(error, SML_ERROR_GENERIC, "No locURI was given");
		goto error;
	}
	
	SmlLocation *location = smlTryMalloc0(sizeof(SmlLocation), error);
	if (!location)
		goto error;
	
	location->refCount = 1;
	location->locURI = g_strdup(locURI);
	location->locName = g_strdup(locName);
	
	smlTrace(TRACE_EXIT, "%s: %p", __func__, location);
	return location;

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

SmlLocation *smlLocationRef(SmlLocation *loc)
{
	smlTrace(TRACE_ENTRY, "%s(%p)", __func__, loc);
	smlAssert(loc);
	
	g_atomic_int_inc(&(loc->refCount));
	
	smlTrace(TRACE_EXIT, "%s: New refcount: %i", __func__, loc->refCount);
	return loc;
}

void smlLocationUnref(SmlLocation *loc)
{
	smlTrace(TRACE_ENTRY, "%s(%p)", __func__, loc);
	smlAssert(loc);
	
	if (g_atomic_int_dec_and_test(&(loc->refCount))) {
		smlTrace(TRACE_INTERNAL, "%s: Refcount == 0!", __func__);
		
		if (loc->locURI)
			smlSafeCFree(&(loc->locURI));
			
		if (loc->locName)
			smlSafeCFree(&(loc->locName));
			
		smlSafeFree((gpointer *)&loc);
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
}

const char *smlLocationGetURI(SmlLocation *loc)
{
	smlAssert(loc);
	return loc->locURI;
}

void smlLocationSetURI(SmlLocation *loc, const char *uri)
{
	smlAssert(loc);
	smlAssert(uri);
	if (loc->locURI)
		smlSafeCFree(&(loc->locURI));
	loc->locURI = g_strdup(uri);
}

const char *smlLocationGetName(SmlLocation *loc)
{
	smlAssert(loc);
	return loc->locName;
}

void smlLocationSetName(SmlLocation *loc, const char *name)
{
	smlAssert(loc);
	smlAssert(name);
	if (loc->locName)
		smlSafeCFree(&(loc->locName));
	loc->locName = g_strdup(name);
}

SmlLocation *smlLocationClone(SmlLocation *source, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, source, error);
	CHECK_ERROR_REF
	smlAssert(source);
	
	SmlLocation *location = smlTryMalloc0(sizeof(SmlLocation), error);
	if (!location)
		goto error;
	location->refCount = 1;
	
	smlLocationCopy(source, location);
	
	smlTrace(TRACE_EXIT, "%s: %p", __func__, location);
	return location;

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

void smlLocationCopy(SmlLocation *source, SmlLocation *target)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, source, target);
	smlAssert(source);
	smlAssert(target);
	
	if (target->locURI)
		smlSafeCFree(&(target->locURI));
		
	if (target->locName)
		smlSafeCFree(&(target->locName));
		
	target->locURI = g_strdup(source->locURI);
	target->locName = g_strdup(source->locName);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
}

#define URL_DELIMITER "/"

char *strreplace(const char *input, const char *needle, const char *replacement)
{
	char *output = g_strdup(input);
	
	while (g_strrstr(output, needle)) {
		char *buffer = g_strndup(output, g_strrstr(output, needle) - output);
		char *buffer2 = g_strconcat(buffer, replacement ? replacement : "", g_strrstr(output, needle) + strlen(needle), NULL);
		smlSafeCFree(&output);
		output = buffer2;
		smlSafeCFree(&buffer);
	}
	
	return output;
}

char *normalizeUrl(const char *url)
{
	smlTrace(TRACE_ENTRY, "%s(%s)", __func__, VA_STRING(url));
	
	if (!url)
		return NULL;
	
	char *buffer = strreplace(url, "./", "");
	char *buffer2 = strreplace(buffer, "//", "/");
	smlSafeCFree(&buffer);
	
	if (strlen(buffer2) > 0 && buffer2[strlen(buffer2) - 1] == '/')
		buffer2[strlen(buffer2) - 1] = 0;
	
	smlTrace(TRACE_EXIT, "%s: %s", __func__, VA_STRING(buffer2));
	return buffer2;
}

SmlBool smlLocationCompare(SmlLocation *objectroot, SmlLocation *object, SmlLocation *urlroot, SmlLocation *url)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, objectroot, object, urlroot, url);
	if (objectroot)
		smlTrace(TRACE_INTERNAL, "%s: objectroot: %s", __func__, VA_STRING(objectroot->locURI));
	if (object)
		smlTrace(TRACE_INTERNAL, "%s: object: %s", __func__, VA_STRING(object->locURI));
	if (urlroot)
		smlTrace(TRACE_INTERNAL, "%s: urlroot: %s", __func__, VA_STRING(urlroot->locURI));
	if (url)
		smlTrace(TRACE_INTERNAL, "%s: url: %s", __func__, VA_STRING(url->locURI));
	char *cmpobjurl = NULL;
	char *cmpurl = NULL;
	char *buffer = NULL;
	char *buffer2 = NULL;
	SmlBool ret = FALSE;
	
	if (object && !url) {
		smlTrace(TRACE_EXIT, "%s: url not given but object: FALSE", __func__);
		return FALSE;
	}
	
	if (!object) {
		smlTrace(TRACE_EXIT, "%s: No object given: TRUE", __func__);
		return TRUE;
	}
	
	if (g_path_is_absolute(url->locURI) || !urlroot)
		cmpurl = normalizeUrl(url->locURI);
	else {
		buffer2 = normalizeUrl(url->locURI);
		buffer = g_strconcat(urlroot->locURI, "/", buffer2, NULL);
		cmpurl = normalizeUrl(buffer);
		smlSafeCFree(&buffer);
		smlSafeCFree(&buffer2);
	}
	
	if (g_path_is_absolute(object->locURI) || !objectroot)
		cmpobjurl = normalizeUrl(object->locURI);
	else {
		buffer2 = normalizeUrl(object->locURI);
		buffer = g_strconcat(objectroot->locURI, "/", buffer2, NULL);
		cmpobjurl = normalizeUrl(buffer);
		smlSafeCFree(&buffer);
		smlSafeCFree(&buffer2);
	}
	
	
	smlTrace(TRACE_INTERNAL, "%s: Comparing %s and %s", __func__, VA_STRING(cmpobjurl), VA_STRING(cmpurl));
	
	if (strcmp(cmpobjurl, cmpurl))
		ret = FALSE;
	else
		ret = TRUE;
	
	smlSafeCFree(&cmpobjurl);
	smlSafeCFree(&cmpurl);
	
	smlTrace(TRACE_EXIT, "%s: %i", __func__, ret);
	return ret;
}

SmlBool smlLocationIsRelative(SmlLocation *location)
{
	smlAssert(location);
	
	return g_path_is_absolute(location->locURI) ? FALSE : TRUE;
}

SmlAnchor *smlAnchorNew(const char *last, const char *next, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%s, %s, %p)", __func__, VA_STRING(last), VA_STRING(next), error);
	CHECK_ERROR_REF
	
	SmlAnchor *anchor = smlTryMalloc0(sizeof(SmlAnchor), error);
	if (!anchor)
		goto error;
	
	anchor->last = g_strdup(last);
	anchor->next = g_strdup(next);
	
	smlTrace(TRACE_EXIT, "%s: %p", __func__, anchor);
	return anchor;

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

void smlAnchorFree(SmlAnchor *anchor)
{
	smlTrace(TRACE_ENTRY, "%s(%p)", __func__, anchor);
	smlAssert(anchor);
	if (anchor->last)
		smlSafeCFree(&(anchor->last));
		
	if (anchor->next)
		smlSafeCFree(&(anchor->next));
		
	smlSafeFree((gpointer *)&anchor);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
}

void smlHeaderFree(SmlHeader *header)
{
	smlTrace(TRACE_ENTRY, "%s(%p)", __func__, header);
	
	if (header->sessionID)
		smlSafeCFree(&(header->sessionID));

	if (header->emi)
		smlSafeCFree(&(header->emi));

	if (header->source)
		smlLocationUnref(header->source);
		
	if (header->target)
		smlLocationUnref(header->target);
	
	if (header->responseURI)
		smlSafeCFree(&(header->responseURI));
	
	smlSafeFree((gpointer *)&header);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
}

SmlItem *smlItemNew(unsigned int size, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%i, %p)", __func__, size, error);
	CHECK_ERROR_REF
		
	SmlItem *item = smlTryMalloc0(sizeof(SmlItem), error);
	if (!item)
		goto error;
	
	item->refCount = 1;
	item->size = size;
	
	smlTrace(TRACE_EXIT, "%s: %p", __func__, item);
	return item;

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

/* If data is NULL, this call is the same if smlItemNew */
SmlItem *smlItemNewForData(const char *data, unsigned int size, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %i, %p)", __func__, data, size, error);
	CHECK_ERROR_REF
	
	SmlItem *item = smlItemNew(size, error);
	if (!item)
		goto error;
	
	if (data) {
		if (!smlItemAddData(item, data, size, error))
			goto error_free_item;
	}
	
	smlTrace(TRACE_EXIT, "%s: %p", __func__, item);
	return item;

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

SmlItem *smlItemRef(SmlItem *item)
{
	smlTrace(TRACE_ENTRY, "%s(%p)", __func__, item);
	smlAssert(item);
	
	g_atomic_int_inc(&(item->refCount));
	
	smlTrace(TRACE_EXIT, "%s: New refcount: %i", __func__, item->refCount);
	return item;
}

void smlItemUnref(SmlItem *item)
{
	smlTrace(TRACE_ENTRY, "%s(%p)", __func__, item);
	smlAssert(item);
	
	if (g_atomic_int_dec_and_test(&(item->refCount))) {
		smlTrace(TRACE_INTERNAL, "%s: Refcount == 0!", __func__);
		
		if (item->source)
			smlLocationUnref(item->source);
			
		if (item->target)
			smlLocationUnref(item->target);
		
		if (item->sourceParent)
			smlLocationUnref(item->sourceParent);
			
		if (item->targetParent)
			smlLocationUnref(item->targetParent);
		
		if (item->anchor)
			smlAnchorFree(item->anchor);
		
		if (item->buffer)
			xmlBufferFree(item->buffer);
		
		if (item->contenttype)
			smlSafeCFree(&(item->contenttype));
		
		smlSafeFree((gpointer *)&item);
	}
	
	smlTrace(TRACE_EXIT, "%s: %i", __func__, item ? item->refCount : 0);
}

SmlBool smlItemAddData(SmlItem *item, const char *data, unsigned int size, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %i, %p)", __func__, item, data, size, error);
	CHECK_ERROR_REF
	
	if (item->size && xmlBufferLength(item->buffer) + size > item->size) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unable to add data. size limit reached");
		goto error;
	}
	
	if (data) {
		if (!item->buffer) {
			if (item->size)
				item->buffer = xmlBufferCreateSize(item->size);
			else
				item->buffer = xmlBufferCreateSize(size);
		}
		
		if (xmlBufferAdd(item->buffer, (xmlChar *)data, size) != 0) {
			smlErrorSet(error, SML_ERROR_GENERIC, "Unable to add data.");
			goto error;
		}
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

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

/** Checks if the item is complete */
SmlBool smlItemCheck(SmlItem *item)
{
	smlAssert(xmlBufferLength(item->buffer) >= 0);
	smlAssert(item);
	if (!item->size)
		return TRUE;
		
	if ((unsigned int)xmlBufferLength(item->buffer) != item->size)
	{
		smlTrace(TRACE_INTERNAL, "%s: failed because size (%d != %d) does not match (%s).",
			__func__, item->size,
			xmlBufferLength(item->buffer), VA_STRING((char *)xmlBufferContent(item->buffer)));
		return FALSE;
	}
		
	return TRUE;
}

SmlBool smlItemHasData(SmlItem *item)
{
	smlAssert(item);
	return item->buffer ? TRUE : FALSE;
}

/** Returns the data of the item. The data will not be freed when the item is unrefd. After
 * this call, smlItemHasData will report FALSE */
SmlBool smlItemStealData(SmlItem *item, char **data, unsigned int *size, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, item, data, size, error);
	CHECK_ERROR_REF
	smlAssert(size);
	
	if (!smlItemCheck(item)) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Item check failed");
		goto error;
	}
	
	*size = xmlBufferLength(item->buffer);
	*data = g_strndup((const char *) xmlBufferContent(item->buffer), *size);
	xmlBufferFree(item->buffer);
	item->buffer = NULL;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

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

/** Returns a const pointer to the data of the item. the data will disappear when the data is derefd */
SmlBool smlItemGetData(SmlItem *item, char **data, unsigned int *size, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, item, data, size, error);
	CHECK_ERROR_REF
	
	if (!smlItemCheck(item)) {
		smlErrorSet(error, SML_ERROR_GENERIC, "Item check failed");
		goto error;
	}
	
	*data = (char *)xmlBufferContent(item->buffer);
	*size = xmlBufferLength(item->buffer);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

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

void smlItemSetSource(SmlItem *item, SmlLocation *source)
{
	smlAssert(item);
	smlAssert(source);
	
	item->source = source;
	smlLocationRef(source);
}

SmlLocation *smlItemGetSource(SmlItem *item)
{
	smlAssert(item);
	
	return item->source;
}

void smlItemSetTarget(SmlItem *item, SmlLocation *target)
{
	smlAssert(item);
	smlAssert(target);
	
	item->target = target;
	smlLocationRef(target);
}

SmlLocation *smlItemGetTarget(SmlItem *item)
{
	smlAssert(item);
	
	return item->target;
}

void smlItemSetSourceParent(SmlItem *item, SmlLocation *sourceParent)
{
	smlAssert(item);
	smlAssert(sourceParent);
	
	item->sourceParent = sourceParent;
	smlLocationRef(sourceParent);
}

SmlLocation *smlItemGetSourceParent(SmlItem *item)
{
	smlAssert(item);
	
	return item->sourceParent;
}

void smlItemSetTargetParent(SmlItem *item, SmlLocation *targetParent)
{
	smlAssert(item);
	smlAssert(targetParent);
	
	item->targetParent = targetParent;
	smlLocationRef(targetParent);
}

SmlLocation *smlItemGetTargetParent(SmlItem *item)
{
	smlAssert(item);
	
	return item->targetParent;
}

void smlItemSetRaw(SmlItem *item, SmlBool raw)
{
	smlAssert(item);
	
	item->raw = raw;
}

SmlCred *smlCredNewFromString(const char *type,
			 const char *format,
			 const char *data,
			 SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%s, %s, %s, %p)", __func__, VA_STRING(data), VA_STRING(type), VA_STRING(format), error);
	CHECK_ERROR_REF

	SmlAuthType smlType = SML_AUTH_TYPE_UNKNOWN;
	SmlFormatType smlFormat = SML_FORMAT_TYPE_UNKNOWN;
	
	if (!type || !strcmp(type, SML_AUTH_BASIC)) {
		smlType = SML_AUTH_TYPE_BASIC;
	} else if (!strcmp(type, SML_AUTH_MD5)) {
		smlType = SML_AUTH_TYPE_MD5;
	} else {
		smlErrorSet(error, SML_ERROR_GENERIC, "Unknown type - %s.", type);
		goto error;
	}

	if (!format || !strcmp(format, SML_BASE64)) {
		smlFormat = SML_FORMAT_TYPE_BASE64;
	} else {
		smlErrorSet(error, SML_ERROR_GENERIC, "SyncML credential: Unknown format - %s.", format);
		goto error;
	}

	if (!data)  {
		smlErrorSet(error, SML_ERROR_GENERIC, "Data is missing in %s.", __func__);
		goto error;
	}

	smlTrace(TRACE_EXIT, "%s", __func__);
	return smlCredNew(smlType, smlFormat, data, NULL, error);
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return NULL;	
}

SmlCred *smlCredNewAuth(SmlAuthType type,
			const char *username,
			const char *password,
			SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%d, %s, %p)", __func__, type, VA_STRING(username), error);
	CHECK_ERROR_REF

	SmlCred *cred = NULL;

	if (username ==  NULL || !strlen(username)) {
		smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION,
			"If authentication should be used then the username must be set.");
		goto error;
	}
	if (password ==  NULL || !strlen(password)) {
		smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION,
			"If authentication should be used then the password must be set.");
		goto error;
	}

        cred = smlTryMalloc0(sizeof(SmlCred), error);
        if (!cred)
                goto error;

	cred->refCount = 1;
	cred->format = SML_FORMAT_TYPE_BASE64;
	cred->type = type;
	cred->username = g_strdup(username);
	cred->password = g_strdup(password);

	smlTrace(TRACE_EXIT, "%s", __func__);
	return cred;
error:
	if (cred)
		smlSafeFree((gpointer *)&cred);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return NULL;	
}

SmlCred *smlCredNew(SmlAuthType type,
		SmlFormatType format,
		const char *data,
		const char *username,
		SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%s, %d, %d, %p)", __func__, VA_STRING(data), type, format, error);
	CHECK_ERROR_REF

        SmlCred *cred = smlTryMalloc0(sizeof(SmlCred), error);
        if (!cred)
                goto error;

	cred->type = type;
	cred->format = format;
	cred->data = g_strdup(data);
	if (username)
		cred->username = g_strdup(username);
	else
		cred->username = NULL;
	cred->refCount = 1;

	smlTrace(TRACE_EXIT, "%s: %p", __func__, cred);
	return cred;
error:
	if (cred->data)
		smlSafeCFree(&(cred->data));
	if (cred->username)
		smlSafeCFree(&(cred->username));
	if (cred)
		smlSafeFree((gpointer *)&cred);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return NULL;
}

void smlCredRef(SmlCred *cred)
{
	smlTrace(TRACE_ENTRY, "%s(%p)", __func__, cred);
	smlAssert(cred);
	
	g_atomic_int_inc(&(cred->refCount));
	
	smlTrace(TRACE_EXIT, "%s: New refcount: %i", __func__, cred->refCount);
}

void smlCredUnref(SmlCred *cred)
{
	smlTrace(TRACE_ENTRY, "%s(%p)", __func__, cred);
	smlAssert(cred);
	
	if (g_atomic_int_dec_and_test(&(cred->refCount))) {
		smlTrace(TRACE_INTERNAL, "%s: Refcount == 0!", __func__);
		
		if (cred->data)
			smlSafeCFree(&(cred->data));
		if (cred->username)
			smlSafeCFree(&(cred->username));
		if (cred->password)
			smlSafeCFree(&(cred->password));
			
		smlSafeFree((gpointer *)&cred);
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
}

/* FIXME: DEPRECATED */
void smlCredFree(SmlCred *cred)
{
	smlCredUnref(cred);
}

SmlChal *smlChalNew(SmlAuthType type, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%u, %p)", __func__, type, error);
	CHECK_ERROR_REF
	SmlChal *chal = NULL;

	/* allocate memory */
	smlAssert(type != SML_AUTH_TYPE_UNKNOWN);
	chal = smlTryMalloc0(sizeof(SmlChal), error);
	if (!chal)
		goto error;
	chal->refCount = 1;
	chal->type = type;
	chal->format = SML_FORMAT_TYPE_BASE64;

	if (type == SML_AUTH_TYPE_MD5)
	{
		/* A nonce must be generated for this type.
		 *     minimum strength:  2^128
		 *     strength per byte: 2^6 - 2 > 2^5
		 *     needed bytes:      128 / 5 < 130 / 5 = 26
		 */
		chal->nonce_plain = smlRandStr(26, TRUE);
		chal->nonce_length = 26;
		chal->nonce_b64 = g_base64_encode(
						(const unsigned char *) chal->nonce_plain,
						chal->nonce_length);
		if (!chal->nonce_b64) {
			smlErrorSet(error, SML_ERROR_GENERIC,
				"The nonce of the challenge cannot be base64 encoded.");
			goto error;
		}
	}

	smlTrace(TRACE_EXIT, "%s", __func__);
	return chal;
error:
	if (chal->nonce_plain)
		smlSafeCFree(&(chal->nonce_plain));
	if (chal->nonce_b64)
		smlSafeCFree(&(chal->nonce_b64));
	if (chal)
		smlSafeFree((gpointer *)&chal);
	smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
	return NULL;
}

SmlChal *smlChalNewFromBinary(
		SmlAuthType type,
		const char *nonce,
		size_t length,
		SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s", __func__);
	CHECK_ERROR_REF
	SmlChal *chal = NULL;

	/* only syncml:auth-md5 needs a nonce */
	smlAssert(type == SML_AUTH_TYPE_MD5);

	/* allocate memory */
	chal = smlTryMalloc0(sizeof(SmlChal), error);
	if (!chal)
		goto error;
	chal->refCount = 1;

	/* copy nonce */
	chal->type = SML_AUTH_TYPE_MD5;
	chal->format = SML_FORMAT_TYPE_BASE64;
	chal->nonce_plain = g_strndup(nonce, length);
	chal->nonce_length = length;

	/* create base64 nonce */
	chal->nonce_b64 = g_base64_encode((const unsigned char *) nonce, length);
	if (!chal->nonce_b64) {
		smlErrorSet(error, SML_ERROR_GENERIC,
			"The base64 encoding of the nonce failed.");
		goto error;
	}

	smlTrace(TRACE_EXIT, "%s", __func__);
	return chal;
error:
	smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
	return NULL;
}

SmlChal *smlChalNewFromBase64(
		SmlAuthType type,
		const char *nonce,
		SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s", __func__);
	CHECK_ERROR_REF
	SmlChal *chal = NULL;

	/* only syncml:auth-md5 needs a nonce */
	smlAssert(type == SML_AUTH_TYPE_MD5);

	/* allocate memory */
	chal = smlTryMalloc0(sizeof(SmlChal), error);
	if (!chal)
		goto error;
	chal->refCount = 1;

	/* copy nonce */
	chal->type = SML_AUTH_TYPE_MD5;
	chal->format = SML_FORMAT_TYPE_BASE64;
	chal->nonce_b64 = g_strdup(nonce);

	/* create binary nonce */
	size_t length = 0;
	chal->nonce_plain = (char *) g_base64_decode(nonce, &length);
	if (!chal->nonce_plain || length < 1) {
		smlErrorSet(error, SML_ERROR_GENERIC,
			"The base64 encoded nonce cannot be decoded.");
		goto error;
	}
	chal->nonce_length = length;

	smlTrace(TRACE_EXIT, "%s", __func__);
	return chal;
error:
	smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
	return NULL;
}

void smlChalRef(SmlChal *chal)
{
	smlTrace(TRACE_ENTRY, "%s", __func__);
	smlAssert(chal);
	
	g_atomic_int_inc(&(chal->refCount));
	
	smlTrace(TRACE_EXIT, "%s: New refcount: %i", __func__, chal->refCount);
}

void smlChalUnref(SmlChal *chal)
{
	smlTrace(TRACE_ENTRY, "%s", __func__);
	smlAssert(chal);
	
	if (g_atomic_int_dec_and_test(&(chal->refCount))) {
		smlTrace(TRACE_INTERNAL, "%s: Refcount == 0!", __func__);
		
		if (chal->nonce_plain)
			smlSafeCFree(&(chal->nonce_plain));

		if (chal->nonce_b64)
			smlSafeCFree(&(chal->nonce_b64));

		smlSafeFree((gpointer *)&chal);
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
}

/* FIXME: DEPRECATED */
void smlChalFree(SmlChal *chal)
{
	smlChalUnref(chal);
}

SmlMapItem *smlMapItemNew(const char *uid, const char *newuid, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%s, %s, %p)", __func__, VA_STRING(uid), VA_STRING(newuid), error);
	CHECK_ERROR_REF
	smlAssert(uid);
	smlAssert(newuid);
	
	SmlMapItem *item = smlTryMalloc0(sizeof(SmlMapItem), error);
	if (!item)
		goto error;
	item->refCount = 1;
	
	item->source = smlLocationNew(newuid, NULL, error);
	if (!item->source)
		goto error_free_item;
	
	item->target = smlLocationNew(uid, NULL, error);
	if (!item->target)
		goto error_free_source;
	
	smlTrace(TRACE_EXIT, "%s: %p", __func__, item);
	return item;
	
error_free_source:
	smlLocationUnref(item->source);
error_free_item:
	smlSafeFree((gpointer *)&item);
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return NULL;	
}

SmlMapItem *smlMapItemRef(SmlMapItem *item)
{
	smlTrace(TRACE_ENTRY, "%s(%p)", __func__, item);
	smlAssert(item);
	
	g_atomic_int_inc(&(item->refCount));
	
	smlTrace(TRACE_EXIT, "%s: New refcount: %i", __func__, item->refCount);
	return item;
}

void smlMapItemUnref(SmlMapItem *item)
{
	smlTrace(TRACE_ENTRY, "%s(%p)", __func__, item);
	smlAssert(item);
	
	if (g_atomic_int_dec_and_test(&(item->refCount))) {
		smlTrace(TRACE_INTERNAL, "%s: Refcount == 0!", __func__);
		
		if (item->source)
		{
			smlLocationUnref(item->source);
			item->source = NULL;
		}
		
		if (item->target)
		{
			smlLocationUnref(item->target);
			item->target = NULL;
		}
			
		smlSafeFree((gpointer *)&item);
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
}

