/*
 * libsyncml - A syncml protocol implementation
 * Copyright (C) 2008-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 "data_sync_devinf.h"

#include "sml_data_sync_private.h"
#include "sml_data_sync_session_private.h"
#include "sml_data_sync_data_store_private.h"

#include "sml_data_sync_defines.h"

#include "libsyncml/sml_support.h"
#include "libsyncml/sml_error_internals.h"
#include "../sml_defines.h"

static SmlDevInfProperty*
_add_ctcap_property_by_name (SmlDevInfCTCap *ctcap,
                             const gchar *name,
                             GError **error)
{
	smlTrace(TRACE_ENTRY, "%s (%s)", __func__, VA_STRING(name));
	CHECK_ERROR_REF
	smlAssert(ctcap);
	smlAssert(name);

	SmlDevInfProperty *prop = sml_dev_inf_property_new();
	if (!prop) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
		            "Cannot create new instance of SmlDevInfProperty - out of memory.");
		goto error;
	}
	if (!sml_dev_inf_property_set_prop_name(prop, name, error))
		goto error;
	if (!sml_dev_inf_ctcap_add_property(ctcap, prop, error))
		goto error;

	smlTrace(TRACE_EXIT, "%s", __func__);
	return prop;
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return NULL;
}

static SmlDevInfProperty*
_add_ctcap_property_by_name_value (SmlDevInfCTCap *ctcap,
                                   const gchar* name,
                                   const gchar *value,
                                   GError **error)
{
	smlTrace(TRACE_ENTRY, "%s (%s ::= %s)", __func__, VA_STRING(name), VA_STRING(value));
	CHECK_ERROR_REF
	smlAssert(ctcap);
	smlAssert(name);
	smlAssert(value);

	SmlDevInfProperty *prop = _add_ctcap_property_by_name(ctcap, name, error);
	if (!prop)
		goto error;
	if (!sml_dev_inf_property_add_val_enum(prop, value, error))
		goto error;

	smlTrace(TRACE_EXIT, "%s", __func__);
	return prop;
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return NULL;
}

static SmlDevInfPropParam*
_add_property_param (SmlDevInfProperty *prop,
                     const gchar *name,
                     GError **error)
{
	smlTrace(TRACE_ENTRY, "%s (%s)", __func__, VA_STRING(name));
	CHECK_ERROR_REF
	smlAssert(prop);
	smlAssert(name);

	SmlDevInfPropParam *param = sml_dev_inf_prop_param_new();
	if (!param) {
		g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
		            "Cannot create new PropParam for SyncML device information.");
		goto error;
	}

	if (!sml_dev_inf_prop_param_set_param_name(param, name, error))
		goto error;
	if (!sml_dev_inf_property_add_param(prop, param, error))
		goto error;

	smlTrace(TRACE_EXIT, "%s", __func__);
	return param;
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return NULL;
}

#define _ADD_PROPERTY_VAL_ENUM(text) \
	if (!sml_dev_inf_property_add_val_enum(prop, text, error)) \
		goto error;
#define _ADD_PROPERTY_PARAM(text) \
	param = _add_property_param(prop, text, error); \
	if (!param) \
		goto error;
#define _ADD_PROP_PARAM_VAL_ENUM(text) \
	if (!sml_dev_inf_prop_param_add_val_enum(param, text, error)) \
		goto error;
#define _ADD_CTCAP_PROPERTY_BY_NAME(text) \
	prop = _add_ctcap_property_by_name(ctcap, text, error); \
	if (!prop) \
		goto error;
#define _ADD_CTCAP_PROPERTY_BY_NAME_VALUE(name,value) \
	if (!_add_ctcap_property_by_name_value(ctcap, name, value, error)) \
		goto error;

static gboolean
add_devinf_ctcap (SmlDevInf *devinf,
                  const gchar *cttype,
                  const gchar *verct,
                  GError **error)
{
	smlTrace(TRACE_ENTRY, "%s (%s %s)", __func__, VA_STRING(cttype), VA_STRING(verct));
	CHECK_ERROR_REF
	smlAssert(devinf);
	smlAssert(cttype);
	smlAssert(verct);

	// first we check for an already configure CTCap
	SmlDevInfContentType *ct = sml_dev_inf_content_type_new(cttype, verct, error);
	if (!ct) {
		goto error;
	}

	if (sml_dev_inf_get_ctcap(devinf, ct) != NULL)
	{
		g_object_unref(ct);
		smlTrace(TRACE_EXIT, "%s - content type already present in devinf", __func__);
		return TRUE;
	} else {
		g_object_unref(ct);
		smlTrace(TRACE_INTERNAL, "%s: new content type detected", __func__);
	}

	SmlDevInfCTCap *ctcap;
	SmlDevInfProperty *prop;
	SmlDevInfPropParam *param;
	if (!strcmp(cttype, SML_ELEMENT_TEXT_VCARD) &&
	    !strcmp(verct, "2.1"))
	{
        	smlTrace(TRACE_INTERNAL, "%s: vCard 2.1 detected", __func__);

		SmlDevInfContentType *ct = sml_dev_inf_content_type_new(SML_ELEMENT_TEXT_VCARD, "2.1", error);
		if (!ct)
			goto error;
		ctcap = sml_dev_inf_ctcap_new(ct, error);
		g_object_unref(ct);
		if (!ctcap)
			goto error;

		_ADD_CTCAP_PROPERTY_BY_NAME("ADR")
			_ADD_PROPERTY_PARAM("TYPE")
				_ADD_PROP_PARAM_VAL_ENUM("HOME")
				_ADD_PROP_PARAM_VAL_ENUM("WORK")
				_ADD_PROP_PARAM_VAL_ENUM("PARCEL")
				_ADD_PROP_PARAM_VAL_ENUM("POSTAL")
				_ADD_PROP_PARAM_VAL_ENUM("INTL")
				_ADD_PROP_PARAM_VAL_ENUM("DOM")
			_ADD_PROPERTY_PARAM("HOME");
			_ADD_PROPERTY_PARAM("WORK");
			_ADD_PROPERTY_PARAM("PARCEL");
			_ADD_PROPERTY_PARAM("POSTAL");
			_ADD_PROPERTY_PARAM("INTL");
			_ADD_PROPERTY_PARAM("DOM");
		_ADD_CTCAP_PROPERTY_BY_NAME("AGENT")
		_ADD_CTCAP_PROPERTY_BY_NAME("BDAY")
		_ADD_CTCAP_PROPERTY_BY_NAME_VALUE("BEGIN","VCARD")
 		_ADD_CTCAP_PROPERTY_BY_NAME_VALUE("END","VCARD")
		_ADD_CTCAP_PROPERTY_BY_NAME("EMAIL")
			_ADD_PROPERTY_PARAM("TYPE")
				_ADD_PROP_PARAM_VAL_ENUM("INTERNET")
			_ADD_PROPERTY_PARAM("INTERNET");
		_ADD_CTCAP_PROPERTY_BY_NAME("FN")
		_ADD_CTCAP_PROPERTY_BY_NAME("GEO")
		_ADD_CTCAP_PROPERTY_BY_NAME("KEY")
			_ADD_PROPERTY_PARAM("TYPE")
				_ADD_PROP_PARAM_VAL_ENUM("X509")
				_ADD_PROP_PARAM_VAL_ENUM("PGP")
			_ADD_PROPERTY_PARAM("X509");
			_ADD_PROPERTY_PARAM("PGP");
		_ADD_CTCAP_PROPERTY_BY_NAME("LABEL")
			_ADD_PROPERTY_PARAM("TYPE")
				_ADD_PROP_PARAM_VAL_ENUM("HOME")
				_ADD_PROP_PARAM_VAL_ENUM("WORK")
				_ADD_PROP_PARAM_VAL_ENUM("PARCEL")
				_ADD_PROP_PARAM_VAL_ENUM("POSTAL")
				_ADD_PROP_PARAM_VAL_ENUM("INTL")
 				_ADD_PROP_PARAM_VAL_ENUM("DOM")
			_ADD_PROPERTY_PARAM("HOME");
			_ADD_PROPERTY_PARAM("WORK");
			_ADD_PROPERTY_PARAM("PARCEL");
			_ADD_PROPERTY_PARAM("POSTAL");
			_ADD_PROPERTY_PARAM("INTL");
 			_ADD_PROPERTY_PARAM("DOM");
		_ADD_CTCAP_PROPERTY_BY_NAME("LOGO")
			_ADD_PROPERTY_PARAM("TYPE")
				_ADD_PROP_PARAM_VAL_ENUM("JPEG")
			_ADD_PROPERTY_PARAM("JPEG");
		_ADD_CTCAP_PROPERTY_BY_NAME("MAILER")
		_ADD_CTCAP_PROPERTY_BY_NAME("N")
		_ADD_CTCAP_PROPERTY_BY_NAME("NOTE")
		_ADD_CTCAP_PROPERTY_BY_NAME("ORG")
		_ADD_CTCAP_PROPERTY_BY_NAME("PHOTO")
			_ADD_PROPERTY_PARAM("TYPE")
				_ADD_PROP_PARAM_VAL_ENUM("JPEG")
			_ADD_PROPERTY_PARAM("JPEG");
		_ADD_CTCAP_PROPERTY_BY_NAME("REV")
		_ADD_CTCAP_PROPERTY_BY_NAME("ROLE")
		_ADD_CTCAP_PROPERTY_BY_NAME("SOUND")
			_ADD_PROPERTY_PARAM("TYPE")
				_ADD_PROP_PARAM_VAL_ENUM("AIFF")
				_ADD_PROP_PARAM_VAL_ENUM("PCM")
				_ADD_PROP_PARAM_VAL_ENUM("WAVE")
			_ADD_PROPERTY_PARAM("AIFF");
			_ADD_PROPERTY_PARAM("PCM");
			_ADD_PROPERTY_PARAM("WAVE");
		_ADD_CTCAP_PROPERTY_BY_NAME("TEL")
			_ADD_PROPERTY_PARAM("TYPE")
				_ADD_PROP_PARAM_VAL_ENUM("WORK")
				_ADD_PROP_PARAM_VAL_ENUM("VOICE")
				_ADD_PROP_PARAM_VAL_ENUM("PREF")
				_ADD_PROP_PARAM_VAL_ENUM("PAGER")
				_ADD_PROP_PARAM_VAL_ENUM("MSG")
				_ADD_PROP_PARAM_VAL_ENUM("MODEM")
				_ADD_PROP_PARAM_VAL_ENUM("ISDN")
				_ADD_PROP_PARAM_VAL_ENUM("HOME")
				_ADD_PROP_PARAM_VAL_ENUM("FAX")
				_ADD_PROP_PARAM_VAL_ENUM("CELL")
				_ADD_PROP_PARAM_VAL_ENUM("CAR")
				_ADD_PROP_PARAM_VAL_ENUM("BBS")
			_ADD_PROPERTY_PARAM("WORK");
			_ADD_PROPERTY_PARAM("VOICE");
			_ADD_PROPERTY_PARAM("PREF");
			_ADD_PROPERTY_PARAM("PAGER");
			_ADD_PROPERTY_PARAM("MSG");
			_ADD_PROPERTY_PARAM("MODEM");
			_ADD_PROPERTY_PARAM("ISDN");
			_ADD_PROPERTY_PARAM("HOME");
			_ADD_PROPERTY_PARAM("FAX");
			_ADD_PROPERTY_PARAM("CELL");
			_ADD_PROPERTY_PARAM("CAR");
			_ADD_PROPERTY_PARAM("BBS");
		_ADD_CTCAP_PROPERTY_BY_NAME("TITLE")
		_ADD_CTCAP_PROPERTY_BY_NAME("TZ")
		_ADD_CTCAP_PROPERTY_BY_NAME("UID")
		_ADD_CTCAP_PROPERTY_BY_NAME("URL")
			_ADD_PROPERTY_PARAM("TYPE")
				_ADD_PROP_PARAM_VAL_ENUM("WORK")
				_ADD_PROP_PARAM_VAL_ENUM("HOME")
			_ADD_PROPERTY_PARAM("WORK");
			_ADD_PROPERTY_PARAM("HOME");
		_ADD_CTCAP_PROPERTY_BY_NAME_VALUE("VERSION", "2.1")
  		if (!sml_dev_inf_add_ctcap(devinf, ctcap, error))
			goto error;
	}
	else if (!strcmp(cttype, SML_ELEMENT_TEXT_VCARD_30) &&
	         !strcmp(verct, "3.0"))
	{
		// FIXME: this is no vCard 3.0 spec
		// FIXME: this is in terms of vCard 3.0 a bug
		smlTrace(TRACE_INTERNAL, "%s: vCard 3.0 detected", __func__);

		SmlDevInfContentType *ct = sml_dev_inf_content_type_new(SML_ELEMENT_TEXT_VCARD_30, "3.0", error);
		if (!ct)
			goto error;
		ctcap = sml_dev_inf_ctcap_new(ct, error);
		g_object_unref(ct);
		if (!ctcap)
			goto error;

		_ADD_CTCAP_PROPERTY_BY_NAME_VALUE("BEGIN", "VCARD")
		_ADD_CTCAP_PROPERTY_BY_NAME_VALUE("END", "VCARD")
		_ADD_CTCAP_PROPERTY_BY_NAME_VALUE("VERSION", "3.0")
		_ADD_CTCAP_PROPERTY_BY_NAME("REV")
 		_ADD_CTCAP_PROPERTY_BY_NAME("N")
		_ADD_CTCAP_PROPERTY_BY_NAME("TITLE")
		_ADD_CTCAP_PROPERTY_BY_NAME("CATEGORIES")
		_ADD_CTCAP_PROPERTY_BY_NAME("CLASS")
		_ADD_CTCAP_PROPERTY_BY_NAME("ORG")
		_ADD_CTCAP_PROPERTY_BY_NAME("EMAIL")
		_ADD_CTCAP_PROPERTY_BY_NAME("URL")
		_ADD_CTCAP_PROPERTY_BY_NAME("TEL")
			_ADD_PROPERTY_PARAM("TYPE")
			_ADD_PROP_PARAM_VAL_ENUM("CELL")
			_ADD_PROP_PARAM_VAL_ENUM("HOME")
			_ADD_PROP_PARAM_VAL_ENUM("WORK")
			_ADD_PROP_PARAM_VAL_ENUM("FAX")
			_ADD_PROP_PARAM_VAL_ENUM("MODEM")
			_ADD_PROP_PARAM_VAL_ENUM("VOICE")
		_ADD_CTCAP_PROPERTY_BY_NAME("ADR")
			_ADD_PROPERTY_PARAM("TYPE")
			_ADD_PROP_PARAM_VAL_ENUM("HOME")
			_ADD_PROP_PARAM_VAL_ENUM("WORK")
		_ADD_CTCAP_PROPERTY_BY_NAME("BDAY")
 		_ADD_CTCAP_PROPERTY_BY_NAME("NOTE")
 		_ADD_CTCAP_PROPERTY_BY_NAME("PHOTO")
			_ADD_PROPERTY_PARAM("TYPE")
  		if (!sml_dev_inf_add_ctcap(devinf, ctcap, error))
			goto error;
	}
	/* Oracle collaboration Suite uses the content type to distinguish */
	/* the versions of vCalendar (and iCalendar)                       */
	/* text/x-vcalendar --> VERSION 1.0 (vCalendar)                    */
	/* text/calendar    --> VERSION 2.0 (iCalendar)                    */
	/* So be VERY VERY CAREFUL if you change something here.           */
	else if (!strcmp(cttype, SML_ELEMENT_TEXT_VCAL) &&
	         !strcmp(verct, "1.0"))
	{
		smlTrace(TRACE_INTERNAL, "%s: vCalendar 1.0 detected", __func__);

		SmlDevInfContentType *ct = sml_dev_inf_content_type_new(SML_ELEMENT_TEXT_VCAL, "1.0", error);
		if (!ct)
			goto error;
		ctcap = sml_dev_inf_ctcap_new(ct, error);
		g_object_unref(ct);
		if (!ctcap)
			goto error;

 		_ADD_CTCAP_PROPERTY_BY_NAME("AALARM")
		_ADD_CTCAP_PROPERTY_BY_NAME("ATTACH")
  		_ADD_CTCAP_PROPERTY_BY_NAME("ATTENDEE")
			_ADD_PROPERTY_PARAM("EXCEPT")
			_ADD_PROPERTY_PARAM("RSVP")
			_ADD_PROPERTY_PARAM("STATUS")
			_ADD_PROPERTY_PARAM("ROLE")
		_ADD_CTCAP_PROPERTY_BY_NAME("BEGIN")
			_ADD_PROPERTY_VAL_ENUM("VCALENDAR")
			_ADD_PROPERTY_VAL_ENUM("VEVENT")
			_ADD_PROPERTY_VAL_ENUM("VTODO")
		_ADD_CTCAP_PROPERTY_BY_NAME("CATEGORIES")
		_ADD_CTCAP_PROPERTY_BY_NAME("COMPLETED")
		_ADD_CTCAP_PROPERTY_BY_NAME("CLASS")
			_ADD_PROPERTY_VAL_ENUM("PUBLIC")
			_ADD_PROPERTY_VAL_ENUM("PRIVATE")
			_ADD_PROPERTY_VAL_ENUM("CONFIDENTIAL")
		_ADD_CTCAP_PROPERTY_BY_NAME("DALARM")
		_ADD_CTCAP_PROPERTY_BY_NAME("DAYLIGHT")
		_ADD_CTCAP_PROPERTY_BY_NAME("DCREATED")
		_ADD_CTCAP_PROPERTY_BY_NAME("DESCRIPTION")
		_ADD_CTCAP_PROPERTY_BY_NAME("DTSTART")
		_ADD_CTCAP_PROPERTY_BY_NAME("DTEND")
		_ADD_CTCAP_PROPERTY_BY_NAME("DUE")
		_ADD_CTCAP_PROPERTY_BY_NAME("END")
			_ADD_PROPERTY_VAL_ENUM("VCALENDAR")
			_ADD_PROPERTY_VAL_ENUM("VEVENT")
			_ADD_PROPERTY_VAL_ENUM("VTODO")
		_ADD_CTCAP_PROPERTY_BY_NAME("EXDATE")
		_ADD_CTCAP_PROPERTY_BY_NAME("LAST-MODIFIED")
		_ADD_CTCAP_PROPERTY_BY_NAME("LOCATION")
		_ADD_CTCAP_PROPERTY_BY_NAME("PRIORITY")
		_ADD_CTCAP_PROPERTY_BY_NAME("RRULE")
		_ADD_CTCAP_PROPERTY_BY_NAME("STATUS")
		_ADD_CTCAP_PROPERTY_BY_NAME("SUMMARY")
		_ADD_CTCAP_PROPERTY_BY_NAME("UID")
		_ADD_CTCAP_PROPERTY_BY_NAME_VALUE("VERSION", "1.0")
  		if (!sml_dev_inf_add_ctcap(devinf, ctcap, error))
			goto error;
	}
	else if (!strcmp(cttype, SML_ELEMENT_TEXT_ICAL) &&
	         !strcmp(verct, "2.0"))
	{
		// FIXME: this is no iCal spec !!!
		// FIXME: this is nearly a direct copy&paste from vCal
		// FIXME: this is a bug in terms of iCal
		smlTrace(TRACE_INTERNAL, "%s: iCalendar (vCalendar 2.0) detected", __func__);

		SmlDevInfContentType *ct = sml_dev_inf_content_type_new(SML_ELEMENT_TEXT_ICAL, "2.0", error);
		if (!ct)
			goto error;
		ctcap = sml_dev_inf_ctcap_new(ct, error);
		g_object_unref(ct);
		if (!ctcap)
			goto error;

		_ADD_CTCAP_PROPERTY_BY_NAME("AALARM")
		_ADD_CTCAP_PROPERTY_BY_NAME("ATTACH")
		_ADD_CTCAP_PROPERTY_BY_NAME("ATTENDEE")
			_ADD_PROPERTY_PARAM("RSVP")
			_ADD_PROPERTY_PARAM("PARTSTAT")
			_ADD_PROPERTY_PARAM("ROLE")
		_ADD_CTCAP_PROPERTY_BY_NAME("BEGIN")
			_ADD_PROPERTY_VAL_ENUM("VCALENDAR")
			_ADD_PROPERTY_VAL_ENUM("VEVENT")
			_ADD_PROPERTY_VAL_ENUM("VTODO")
		_ADD_CTCAP_PROPERTY_BY_NAME("CATEGORIES")
		_ADD_CTCAP_PROPERTY_BY_NAME("COMPLETED")
		_ADD_CTCAP_PROPERTY_BY_NAME("CLASS")
			_ADD_PROPERTY_VAL_ENUM("PUBLIC")
			_ADD_PROPERTY_VAL_ENUM("PRIVATE")
			_ADD_PROPERTY_VAL_ENUM("CONFIDENTIAL")
		_ADD_CTCAP_PROPERTY_BY_NAME("DALARM")
		_ADD_CTCAP_PROPERTY_BY_NAME("DAYLIGHT")
		_ADD_CTCAP_PROPERTY_BY_NAME("DCREATED")
		_ADD_CTCAP_PROPERTY_BY_NAME("DESCRIPTION")
		_ADD_CTCAP_PROPERTY_BY_NAME("DTSTART")
		_ADD_CTCAP_PROPERTY_BY_NAME("DTEND")
		_ADD_CTCAP_PROPERTY_BY_NAME("DUE")
		_ADD_CTCAP_PROPERTY_BY_NAME("END")
			_ADD_PROPERTY_VAL_ENUM("VCALENDAR")
			_ADD_PROPERTY_VAL_ENUM("VEVENT")
			_ADD_PROPERTY_VAL_ENUM("VTODO")
		_ADD_CTCAP_PROPERTY_BY_NAME("EXDATE")
		_ADD_CTCAP_PROPERTY_BY_NAME("LAST-MODIFIED")
		_ADD_CTCAP_PROPERTY_BY_NAME("LOCATION")
		_ADD_CTCAP_PROPERTY_BY_NAME("PRIORITY")
		_ADD_CTCAP_PROPERTY_BY_NAME("RRULE")
		_ADD_CTCAP_PROPERTY_BY_NAME("STATUS")
		_ADD_CTCAP_PROPERTY_BY_NAME("SUMMARY")
 		_ADD_CTCAP_PROPERTY_BY_NAME("UID")
		_ADD_CTCAP_PROPERTY_BY_NAME_VALUE("VERSION", "2.0")
  		if (!sml_dev_inf_add_ctcap(devinf, ctcap, error))
			goto error;
	}
	else
	{
		/* trace the missing stuff and create a minimal CTCap */
		smlTrace(TRACE_INTERNAL, "%s: unknown content type - %s %s", __func__, VA_STRING(cttype), VA_STRING(verct));

		SmlDevInfContentType *ct = sml_dev_inf_content_type_new(cttype, verct, error);
		if (!ct)
			goto error;
		ctcap = sml_dev_inf_ctcap_new(ct, error);
		g_object_unref(ct);
		if (!ctcap)
			goto error;

  		if (!sml_dev_inf_add_ctcap(devinf, ctcap, error))
			goto error;
	}
 
	smlTrace(TRACE_EXIT, "%s - content type newly added to devinf", __func__);
	return TRUE;
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}

static void
_update_session_config_from_devinf (SmlDataSyncSession *self)
{
	smlTrace(TRACE_ENTRY, "%s called", __func__); 
	SmlDevInf *devinf = self->priv->remote_dev_inf;
	SmlSession *session = self->priv->session;

	// direct session config

	smlSessionUseNumberOfChanges(session, sml_dev_inf_get_support_number_of_changes(devinf));
	smlSessionUseLargeObjects(session, sml_dev_inf_get_support_large_objs(devinf));

	// local device information

	if (sml_dev_inf_get_support_utc(devinf))
		self->priv->only_localtime = FALSE;
	else
		self->priv->only_localtime = TRUE;

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

/* here start the internal API functions */

#define _ADD_DATASTORE_RX(cttype,verct) \
		ctype = sml_dev_inf_content_type_new(cttype, verct, error); \
		if (!ctype) \
			goto error; \
		if (!sml_dev_inf_data_store_add_rx(ds, ctype, error)) \
			goto error; \
		g_object_unref(ctype); \
		ctype = NULL;
	
#define _ADD_DATASTORE_TX(cttype,verct) \
		ctype = sml_dev_inf_content_type_new(cttype, verct, error); \
		if (!ctype) \
			goto error; \
		if (!sml_dev_inf_data_store_add_tx(ds, ctype, error)) \
			goto error; \
		g_object_unref(ctype); \
		ctype = NULL;

#define _SET_DATASTORE_RX_PREF(cttype,verct) \
		ctype = sml_dev_inf_content_type_new(cttype, verct, error); \
		if (!ctype) \
			goto error; \
		if (!sml_dev_inf_data_store_set_rx_pref(ds, ctype, error)) \
			goto error; \
		g_object_unref(ctype); \
		ctype = NULL;

#define _SET_DATASTORE_TX_PREF(cttype,verct) \
		ctype = sml_dev_inf_content_type_new(cttype, verct, error); \
		if (!ctype) \
			goto error; \
		if (!sml_dev_inf_data_store_set_tx_pref(ds, ctype, error)) \
			goto error; \
		g_object_unref(ctype); \
		ctype = NULL;

gboolean
sml_data_sync_dev_inf_add_data_store (SmlDevInf *devinf,
                                      SmlDataSyncDataStore *datastore,
                                      GError **error)
{
	smlTrace(TRACE_ENTRY, "%s (%p, %p, %p)", __func__, devinf, datastore, error);
	CHECK_ERROR_REF
	smlAssert(datastore);
	smlAssert(datastore->priv->content_type);
	smlAssert(datastore->priv->local);
	smlAssert(sml_location_get_uri(datastore->priv->local));

	SmlDevInfDataStore *ds = sml_dev_inf_data_store_new(sml_location_get_uri(datastore->priv->local), error);
	if (!ds)
		goto error;

	const char *ct = datastore->priv->content_type;
	SmlDevInfContentType *ctype = NULL;

	if (!strcmp(ct, SML_ELEMENT_TEXT_VCARD))
	{
		// we prefer actually vCard 2.1
		// because the most cellphones support it

		_ADD_DATASTORE_RX(SML_ELEMENT_TEXT_VCARD_30, "3.0")
		_ADD_DATASTORE_TX(SML_ELEMENT_TEXT_VCARD_30, "3.0")
		_SET_DATASTORE_RX_PREF(SML_ELEMENT_TEXT_VCARD, "2.1")
		_SET_DATASTORE_TX_PREF(SML_ELEMENT_TEXT_VCARD, "2.1")

		if (!add_devinf_ctcap(devinf, SML_ELEMENT_TEXT_VCARD, "2.1", error))
			goto error;
		if (!add_devinf_ctcap(devinf, SML_ELEMENT_TEXT_VCARD_30, "3.0", error))
			goto error;
	}
	else if (!strcmp(ct, SML_ELEMENT_TEXT_VCARD_30))
	{
		_ADD_DATASTORE_RX(SML_ELEMENT_TEXT_VCARD, "2.1")
		_ADD_DATASTORE_TX(SML_ELEMENT_TEXT_VCARD, "2.1")
		_SET_DATASTORE_RX_PREF(SML_ELEMENT_TEXT_VCARD_30, "3.0")
		_SET_DATASTORE_TX_PREF(SML_ELEMENT_TEXT_VCARD_30, "3.0")

		if (!add_devinf_ctcap(devinf, SML_ELEMENT_TEXT_VCARD, "2.1", error))
			goto error;
		if (!add_devinf_ctcap(devinf, SML_ELEMENT_TEXT_VCARD_30, "3.0", error))
			goto error;
	}
	else if (!strcmp(ct, SML_ELEMENT_TEXT_VCAL))
	{

		_ADD_DATASTORE_RX(SML_ELEMENT_TEXT_ICAL, "2.0")
		_ADD_DATASTORE_TX(SML_ELEMENT_TEXT_ICAL, "2.0")
		_SET_DATASTORE_RX_PREF(SML_ELEMENT_TEXT_VCAL, "1.0")
		_SET_DATASTORE_TX_PREF(SML_ELEMENT_TEXT_VCAL, "1.0")

		if (!add_devinf_ctcap(devinf, SML_ELEMENT_TEXT_VCAL, "1.0", error))
			goto error;
		if (!add_devinf_ctcap(devinf, SML_ELEMENT_TEXT_ICAL, "2.0", error))
			goto error;
	}
	else if (!strcmp(ct, SML_ELEMENT_TEXT_ICAL))
	{

		_ADD_DATASTORE_RX(SML_ELEMENT_TEXT_VCAL, "1.0")
		_ADD_DATASTORE_TX(SML_ELEMENT_TEXT_VCAL, "1.0")
		_SET_DATASTORE_RX_PREF(SML_ELEMENT_TEXT_ICAL, "2.0")
		_SET_DATASTORE_TX_PREF(SML_ELEMENT_TEXT_ICAL, "2.0")

		if (!add_devinf_ctcap(devinf, SML_ELEMENT_TEXT_VCAL, "1.0", error))
			goto error;
		if (!add_devinf_ctcap(devinf, SML_ELEMENT_TEXT_ICAL, "2.0", error))
			goto error;
	}
	else if (!strcmp(ct, SML_ELEMENT_TEXT_PLAIN))
	{
		_SET_DATASTORE_RX_PREF(SML_ELEMENT_TEXT_PLAIN, "1.0")
		_SET_DATASTORE_TX_PREF(SML_ELEMENT_TEXT_PLAIN, "1.0")
		if (!add_devinf_ctcap(devinf, SML_ELEMENT_TEXT_PLAIN, "1.0", error))
			goto error;
	}
	else
	{
		smlTrace(TRACE_INTERNAL, "%s - unknown content type detected (%s)",
                    __func__, VA_STRING(ct));
		_SET_DATASTORE_RX_PREF(ct, "1.0")
		_SET_DATASTORE_TX_PREF(ct, "1.0")
		if (!add_devinf_ctcap(devinf, ct, "1.0", error))
			goto error;
	}

	// configure supported sync modes
	sml_dev_inf_data_store_set_sync_cap(ds, SML_DEVINF_SYNCTYPE_TWO_WAY, TRUE);
	sml_dev_inf_data_store_set_sync_cap(ds, SML_DEVINF_SYNCTYPE_SLOW_SYNC, TRUE);
	// server alerted sync means that the client has to interpret alerts !!!
	// FIXME: we receive alerts but we do nothing with it
	if (smlDsServerGetServerType(datastore->priv->data_store_server) == SML_DS_CLIENT) {
		// sml_dev_inf_data_store_add_sync_cap(ds, SML_DEVINF_SYNCTYPE_ONE_WAY_FROM_CLIENT, TRUE);
		smlTrace(TRACE_INTERNAL, "%s: SyncML clients only support SLOW and TWO WAY SYNC", __func__);
	} else {
		sml_dev_inf_data_store_set_sync_cap(ds, SML_DEVINF_SYNCTYPE_SERVER_ALERTED_SYNC, TRUE);
	}

	if (!sml_dev_inf_add_data_store(devinf, ds, error))
		goto error;
	smlTrace(TRACE_EXIT, "%s - content type newly added to devinf", __func__);
	return TRUE;
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return FALSE;
}

gboolean
sml_data_sync_dev_inf_load_remote (SmlDataSyncSession *self,
                                   gboolean sendGet,
                                   GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %i, %p)", __func__, self, sendGet, error);
	CHECK_ERROR_REF
	smlAssert(self);
	smlAssertMsg(self->priv->session, "an active session is needed to have a remote device");

	/* if there is already a remote device information
	 * then cleanup the reference.
	 */
	if (self->priv->remote_dev_inf)
		g_object_unref(self->priv->remote_dev_inf);

	self->priv->remote_dev_inf = smlDevInfAgentGetSessionDevInf(self->priv->data_sync->priv->agent, self->priv->session);
	if (self->priv->remote_dev_inf)
	{
		smlTrace(TRACE_INTERNAL, "%s: The remote DevInf was received.", __func__);
		g_object_ref(self->priv->remote_dev_inf);
		_update_session_config_from_devinf(self);
		/* DevInf caching is optional */
		if (self->priv->data_sync->priv->write_devinf_callback) {
			smlTrace(TRACE_EXIT, "%s - calling writeDevInfCallback", __func__);
			return self->priv->data_sync->priv->write_devinf_callback(
						self, self->priv->remote_dev_inf,
						self->priv->data_sync->priv->write_devinf_userdata,
						error);
		} else {
			smlTrace(TRACE_EXIT, "%s - remote DevInf available", __func__);
			return TRUE;
		}
	} else {
		smlTrace(TRACE_INTERNAL, "%s: The remote DevInf was not received.", __func__);
		/* DevInf caching is optional */
		if (self->priv->data_sync->priv->read_devinf_callback)
		{
			smlTrace(TRACE_INTERNAL, "%s: calling read DevInf callback", __func__);
			SmlDevInf *devinf = self->priv->data_sync->priv->read_devinf_callback(
						self,
						sml_location_get_uri(smlSessionGetTarget(self->priv->session)),
						self->priv->data_sync->priv->read_devinf_userdata, error);
			if (!devinf && *error)
				goto error;
			if (devinf) {
				if (!smlDevInfAgentSetSessionDevInf(
						self->priv->data_sync->priv->agent,
						self->priv->session,
						devinf,
						error))
				{
					goto error;
				}
				self->priv->remote_dev_inf = smlDevInfAgentGetSessionDevInf(
								self->priv->data_sync->priv->agent,
								self->priv->session);
				/* smlDevInfAgentSetDevInf consumes the DevInf object.
				 * So the reference counter must be incremented for
				 * remoteDevInf.
				 */
				g_object_ref(self->priv->remote_dev_inf);
				_update_session_config_from_devinf(self);
				smlTrace(TRACE_EXIT, "%s - cached DevInf", __func__);
				return TRUE;
			}
		}
		if (sendGet)
		{
			/* the local device information is only send for fairness ;) */
			smlDevInfAgentSendDevInf(
				self->priv->data_sync->priv->agent,
				self->priv->session,
				error);
			smlDevInfAgentRequestDevInf(
				self->priv->data_sync->priv->agent,
				self->priv->session,
				error);
		}
		smlTrace(TRACE_EXIT, "%s - no remote DevInf available at all", __func__);
		return FALSE;
	}
error:
	smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, (*error)->message);
	return FALSE;
}

gchar*
sml_data_sync_dev_inf_get_identifier ()
{
    smlTrace(TRACE_ENTRY, "%s", __func__);
    const char *user = g_get_user_name();
    const char *host = g_get_host_name();
    char *id = g_strjoin("@", user, host, NULL);
    smlTrace(TRACE_EXIT, "%s - %s", __func__, VA_STRING(id));
    return id;
    // smlTrace(TRACE_INTERNAL, "%s - %s", __func__, VA_STRING(id));
    // char *b64 = g_base64_encode(id, strlen(id));
    // smlSafeCFree(&id);
    // smlTrace(TRACE_EXIT, "%s - %s", __func__, VA_STRING(b64));
    // return b64;
}

gboolean
sml_data_sync_dev_inf_init (SmlDataSync *self,
                            SmlDevInfDevTyp type,
                            GError **error)
{
	CHECK_ERROR_REF
	SmlDevInf *devinf = NULL;

	/* fix missing identifier */
	if (!self->priv->local) {
		self->priv->local = sml_location_new();
		const gchar *ident = sml_data_sync_dev_inf_get_identifier();
		sml_location_set_uri(self->priv->local, ident);
	}

	if (self->priv->fake_device)
	{
		smlTrace(TRACE_INTERNAL, "%s: faking devinf", __func__);
		devinf = sml_dev_inf_new();
		if (!devinf) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
			            "Cannot create new SmlDevInf object - out of memory.");
			goto error;
		}
		if (!sml_dev_inf_set_dev_id(devinf, sml_location_get_uri(self->priv->local), error))
			goto error;
		sml_dev_inf_set_dev_typ(devinf, SML_DEVINF_DEVTYP_SMARTPHONE);

		if (!sml_dev_inf_set_man(devinf, self->priv->fake_manufacturer, error))
			goto error;
		if (!sml_dev_inf_set_mod(devinf, self->priv->fake_model, error))
			goto error;
		if (!sml_dev_inf_set_swv(devinf, self->priv->fake_software_version, error))
			goto error;
	} else {
		smlTrace(TRACE_INTERNAL, "%s: not faking devinf", __func__);
		devinf = sml_dev_inf_new();
		if (!devinf) {
			g_set_error(error, SML_ERROR, SML_ERROR_GENERIC,
			            "Cannot create new SmlDevInf object - out of memory.");
			goto error;
		}
		if (!sml_dev_inf_set_dev_id(devinf, sml_location_get_uri(self->priv->local), error))
			goto error;
		sml_dev_inf_set_dev_typ(devinf, type);

		if (!sml_dev_inf_set_swv(devinf, self->priv->fake_software_version, error))
			goto error;
	}

	sml_dev_inf_set_support_number_of_changes(devinf, TRUE);
	sml_dev_inf_set_support_large_objs(devinf, TRUE);
	if (!self->priv->only_localtime)
        	sml_dev_inf_set_support_utc(devinf, TRUE);
	smlAssert(self->priv->max_msg_size);
	smlAssert(self->priv->max_obj_size);

	self->priv->local_dev_inf = devinf;

	self->priv->agent = smlDevInfAgentNew(self->priv->local_dev_inf, error);
	if (!self->priv->agent)
		goto error;
	g_object_ref(self->priv->local_dev_inf); /* the agent consumes the object */
	
	if (!smlDevInfAgentRegister(self->priv->agent, self->priv->manager, error))
		goto error;

	return TRUE;
error:
	if (devinf)
		g_object_unref(devinf);
	if (self->priv->agent)
		smlDevInfAgentFree(self->priv->agent);
	self->priv->local_dev_inf = NULL;
	self->priv->agent = NULL;
	return FALSE;
}

gboolean
sml_data_sync_dev_inf_manage_session (SmlDataSyncSession *self,
                                      gboolean sendGet,
                                      GError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %i, %p)", __func__, self, sendGet, error);
	CHECK_ERROR_REF
	smlAssertMsg(self->priv->session, "an active session is needed to manage device informations");

	/* If the callback is available
	 * then we dump the local device information first.
	 */
	if (self->priv->data_sync->priv->write_devinf_callback)
	{
		/* store device info */
		smlTrace(TRACE_INTERNAL, "%s: calling write DevInf callback", __func__);
		if (!self->priv->data_sync->priv->write_devinf_callback(
				self, self->priv->data_sync->priv->local_dev_inf,
				self->priv->data_sync->priv->write_devinf_userdata, error))
			goto error;
	}

	/* handle remote device information */
	if (sml_data_sync_dev_inf_load_remote(self, sendGet, error) &&
	    self->priv->remote_dev_inf &&
	    self->priv->data_sync->priv->handle_remote_devinf_callback &&
	    !self->priv->data_sync->priv->handle_remote_devinf_callback(
				self,
				self->priv->remote_dev_inf,
				self->priv->data_sync->priv->handle_remote_devinf_userdata,
				error))
	{
		/* the remote device information
		 * and the callback are available
		 * but the callback failed.
		 */
		goto error;
	} else {
		/* check if there was an error during DevInfLoadRemote
		 * or dsObject->handleRemoteDevInfCallback
		 */
		if (*error != NULL)
			goto error;
		/* if the remote peer does not support UTC
		 * then dsObject must be configured properly
		 */
		if (self->priv->remote_dev_inf &&
		    !sml_dev_inf_get_support_utc(self->priv->remote_dev_inf))
		{
			smlTrace(TRACE_INTERNAL,
				"%s: enforcing localtime because of remote DevInf",
				__func__);
			self->priv->only_localtime = TRUE;
		}
	}

	smlTrace(TRACE_EXIT, "%s - TRUE", __func__);
	return TRUE;
error:
	smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, (*error)->message);
	return FALSE;
}

