/*
 * 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 "libsyncml/syncml_internals.h"
#include "libsyncml/sml_error_internals.h"

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

	SmlDevInfProperty *prop = smlDevInfNewProperty(error);
	if (!prop)
		goto error;
	smlDevInfPropertySetPropName(prop, name);
	smlDevInfCTCapAddProperty(ctcap, prop);

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

static SmlDevInfProperty *_add_ctcap_property_by_name_value(
				SmlDevInfCTCap *ctcap,
				const char*name,
				const char *value,
				SmlError **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;
	smlDevInfPropertyAddValEnum(prop, value);

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

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

	SmlDevInfPropParam *param = smlDevInfNewPropParam(error);
	if (!param)
		goto error;

	smlDevInfPropParamSetParamName(param, name);
	smlDevInfPropertyAddPropParam(prop, param);

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

#define _ADD_PROPERTY_PARAM(text) \
	param = _add_property_param(prop, text, error); \
	if (!param) \
		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 SmlBool add_devinf_ctcap(
			SmlDevInf *devinf,
			const char* cttype,
			const char *verct,
			SmlError **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 = smlDevInfNewContentType(cttype, verct, error);
	if (!ct)
		goto error;

	if (smlDevInfGetCTCap(devinf, ct) != NULL)
	{
		smlDevInfFreeContentType(ct);
		smlTrace(TRACE_EXIT, "%s - content type already present in devinf", __func__);
		return TRUE;
	} else {
		smlDevInfFreeContentType(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__);
		ctcap = smlDevInfNewCTCap(error);
		if (!ctcap)
			goto error;

		smlDevInfCTCapSetCTType(ctcap, SML_ELEMENT_TEXT_VCARD);
		smlDevInfCTCapSetVerCT(ctcap, "2.1");
		_ADD_CTCAP_PROPERTY_BY_NAME("ADR")
			_ADD_PROPERTY_PARAM("TYPE")
				smlDevInfPropParamAddValEnum(param, "HOME");
				smlDevInfPropParamAddValEnum(param, "WORK");
				smlDevInfPropParamAddValEnum(param, "PARCEL");
				smlDevInfPropParamAddValEnum(param, "POSTAL");
				smlDevInfPropParamAddValEnum(param, "INTL");
				smlDevInfPropParamAddValEnum(param, "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")
				smlDevInfPropParamAddValEnum(param, "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")
				smlDevInfPropParamAddValEnum(param, "X509");
				smlDevInfPropParamAddValEnum(param, "PGP");
			_ADD_PROPERTY_PARAM("X509");
			_ADD_PROPERTY_PARAM("PGP");
		_ADD_CTCAP_PROPERTY_BY_NAME("LABEL")
			_ADD_PROPERTY_PARAM("TYPE")
				smlDevInfPropParamAddValEnum(param, "HOME");
				smlDevInfPropParamAddValEnum(param, "WORK");
				smlDevInfPropParamAddValEnum(param, "PARCEL");
				smlDevInfPropParamAddValEnum(param, "POSTAL");
				smlDevInfPropParamAddValEnum(param, "INTL");
 				smlDevInfPropParamAddValEnum(param, "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")
				smlDevInfPropParamAddValEnum(param, "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")
				smlDevInfPropParamAddValEnum(param, "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")
				smlDevInfPropParamAddValEnum(param, "AIFF");
				smlDevInfPropParamAddValEnum(param, "PCM");
				smlDevInfPropParamAddValEnum(param, "WAVE");
			_ADD_PROPERTY_PARAM("AIFF");
			_ADD_PROPERTY_PARAM("PCM");
			_ADD_PROPERTY_PARAM("WAVE");
		_ADD_CTCAP_PROPERTY_BY_NAME("TEL")
			_ADD_PROPERTY_PARAM("TYPE")
				smlDevInfPropParamAddValEnum(param, "WORK");
				smlDevInfPropParamAddValEnum(param, "VOICE");
				smlDevInfPropParamAddValEnum(param, "PREF");
				smlDevInfPropParamAddValEnum(param, "PAGER");
				smlDevInfPropParamAddValEnum(param, "MSG");
				smlDevInfPropParamAddValEnum(param, "MODEM");
				smlDevInfPropParamAddValEnum(param, "ISDN");
				smlDevInfPropParamAddValEnum(param, "HOME");
				smlDevInfPropParamAddValEnum(param, "FAX");
				smlDevInfPropParamAddValEnum(param, "CELL");
				smlDevInfPropParamAddValEnum(param, "CAR");
				smlDevInfPropParamAddValEnum(param, "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")
				smlDevInfPropParamAddValEnum(param, "WORK");
				smlDevInfPropParamAddValEnum(param, "HOME");
			_ADD_PROPERTY_PARAM("WORK");
			_ADD_PROPERTY_PARAM("HOME");
		_ADD_CTCAP_PROPERTY_BY_NAME_VALUE("VERSION", "2.1")
 		smlDevInfAppendCTCap(devinf, ctcap);
	}
	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__);
		ctcap = smlDevInfNewCTCap(error);
		if (!ctcap)
			goto error;

		smlDevInfCTCapSetCTType(ctcap, SML_ELEMENT_TEXT_VCARD_30);
		smlDevInfCTCapSetVerCT(ctcap, "3.0");
		_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")
			smlDevInfPropParamAddValEnum(param, "CELL");
			smlDevInfPropParamAddValEnum(param, "HOME");
			smlDevInfPropParamAddValEnum(param, "WORK");
			smlDevInfPropParamAddValEnum(param, "FAX");
			smlDevInfPropParamAddValEnum(param, "MODEM");
			smlDevInfPropParamAddValEnum(param, "VOICE");
		_ADD_CTCAP_PROPERTY_BY_NAME("ADR")
			_ADD_PROPERTY_PARAM("TYPE")
			smlDevInfPropParamAddValEnum(param, "HOME");
			smlDevInfPropParamAddValEnum(param, "WORK");
		_ADD_CTCAP_PROPERTY_BY_NAME("BDAY")
 		_ADD_CTCAP_PROPERTY_BY_NAME("NOTE")
 		_ADD_CTCAP_PROPERTY_BY_NAME("PHOTO")
			_ADD_PROPERTY_PARAM("TYPE")
  		smlDevInfAppendCTCap(devinf, ctcap);
	}
	/* 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__);
		ctcap = smlDevInfNewCTCap(error);
		if (!ctcap)
			goto error;

		smlDevInfCTCapSetCTType(ctcap, SML_ELEMENT_TEXT_VCAL);
		smlDevInfCTCapSetVerCT(ctcap, "1.0");
 		_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")
			smlDevInfPropertyAddValEnum(prop, "VCALENDAR");
			smlDevInfPropertyAddValEnum(prop, "VEVENT");
			smlDevInfPropertyAddValEnum(prop, "VTODO");
		_ADD_CTCAP_PROPERTY_BY_NAME("CATEGORIES")
		_ADD_CTCAP_PROPERTY_BY_NAME("COMPLETED")
		_ADD_CTCAP_PROPERTY_BY_NAME("CLASS")
			smlDevInfPropertyAddValEnum(prop, "PUBLIC");
			smlDevInfPropertyAddValEnum(prop, "PRIVATE");
			smlDevInfPropertyAddValEnum(prop, "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")
			smlDevInfPropertyAddValEnum(prop, "VCALENDAR");
			smlDevInfPropertyAddValEnum(prop, "VEVENT");
			smlDevInfPropertyAddValEnum(prop, "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")
 		smlDevInfAppendCTCap(devinf, ctcap);
	}
	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__);
		ctcap = smlDevInfNewCTCap(error);
		if (!ctcap)
			goto error;

		smlDevInfCTCapSetCTType(ctcap, SML_ELEMENT_TEXT_ICAL);
		smlDevInfCTCapSetVerCT(ctcap, "2.0");
		_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")
			smlDevInfPropertyAddValEnum(prop, "VCALENDAR");
			smlDevInfPropertyAddValEnum(prop, "VEVENT");
			smlDevInfPropertyAddValEnum(prop, "VTODO");
		_ADD_CTCAP_PROPERTY_BY_NAME("CATEGORIES")
		_ADD_CTCAP_PROPERTY_BY_NAME("COMPLETED")
		_ADD_CTCAP_PROPERTY_BY_NAME("CLASS")
			smlDevInfPropertyAddValEnum(prop, "PUBLIC");
			smlDevInfPropertyAddValEnum(prop, "PRIVATE");
			smlDevInfPropertyAddValEnum(prop, "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")
			smlDevInfPropertyAddValEnum(prop, "VCALENDAR");
			smlDevInfPropertyAddValEnum(prop, "VEVENT");
			smlDevInfPropertyAddValEnum(prop, "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")
		smlDevInfAppendCTCap(devinf, ctcap);
	}
	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));
		ctcap = smlDevInfNewCTCap(error);
		if (!ctcap)
			goto error;
		smlDevInfCTCapSetCTType(ctcap, cttype);
		smlDevInfCTCapSetVerCT(ctcap, verct);
		smlDevInfAppendCTCap(devinf, ctcap);
	}
 
	smlTrace(TRACE_EXIT, "%s - content type newly added to devinf", __func__);
	return TRUE;
error:    
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

static void _update_session_config_from_devinf(SmlDataSyncObject *dsObject)
{
	smlTrace(TRACE_ENTRY, "%s called", __func__); 
	SmlDevInf *devinf = dsObject->remoteDevInf;
	SmlSession *session = dsObject->session;

	// direct session config

	smlSessionUseNumberOfChanges(session, smlDevInfSupportsNumberOfChanges(devinf));
	smlSessionUseLargeObjects(session, smlDevInfSupportsLargeObjs(devinf));

	// local device information

	if (smlDevInfSupportsUTC(devinf))
		dsObject->onlyLocaltime = FALSE;
	else
		dsObject->onlyLocaltime = TRUE;

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

/* here start the internal API functions */

SmlBool smlDataSyncDevInfAddDatastore(
		SmlDevInf *devinf, 
		SmlDataSyncDatastore *datastore, 
		SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s (%p, %p)", __func__, devinf, datastore);
	CHECK_ERROR_REF
	smlAssert(datastore);
	smlAssert(datastore->contentType);
	smlAssert(datastore->sourceUri);

	SmlDevInfDataStore *ds = smlDevInfDataStoreNew(datastore->sourceUri, error);
	if (!ds)
		goto error;

	const char *ct = datastore->contentType;
	SmlDevInfContentType *ctype;

	if (!strcmp(ct, SML_ELEMENT_TEXT_VCARD))
	{
		// we prefer actually vCard 2.1
		// because the most cellphones support it
		ctype = smlDevInfNewContentType(SML_ELEMENT_TEXT_VCARD_30, "3.0", error);
		if (!ctype)
			goto error;
		smlDevInfDataStoreAddRx(ds, ctype);
		ctype = smlDevInfNewContentType(SML_ELEMENT_TEXT_VCARD_30, "3.0", error);
		if (!ctype)
			goto error;
		smlDevInfDataStoreAddTx(ds, ctype);
		smlDevInfDataStoreSetRxPref(ds, SML_ELEMENT_TEXT_VCARD, "2.1");
		smlDevInfDataStoreSetTxPref(ds, 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))
	{
		ctype = smlDevInfNewContentType(SML_ELEMENT_TEXT_VCARD_30, "2.1", error);
		if (!ctype)
			goto error;
		smlDevInfDataStoreAddRx(ds, ctype);
		ctype = smlDevInfNewContentType(SML_ELEMENT_TEXT_VCARD_30, "2.1", error);
		if (!ctype)
			goto error;
		smlDevInfDataStoreAddTx(ds, ctype);
		smlDevInfDataStoreSetRxPref(ds, SML_ELEMENT_TEXT_VCARD_30, "3.0");
		smlDevInfDataStoreSetTxPref(ds, 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))
	{
		ctype = smlDevInfNewContentType(SML_ELEMENT_TEXT_ICAL, "2.0", error);
		if (!ctype)
			goto error;
		smlDevInfDataStoreAddRx(ds, ctype);
		ctype = smlDevInfNewContentType(SML_ELEMENT_TEXT_ICAL, "2.0", error);
		if (!ctype)
			goto error;
		smlDevInfDataStoreAddTx(ds, ctype);
		smlDevInfDataStoreSetRxPref(ds, SML_ELEMENT_TEXT_VCAL, "1.0");
		smlDevInfDataStoreSetTxPref(ds, 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))
	{
		ctype = smlDevInfNewContentType(SML_ELEMENT_TEXT_VCAL, "1.0", error);
		if (!ctype)
			goto error;
		smlDevInfDataStoreAddRx(ds, ctype);
		ctype = smlDevInfNewContentType(SML_ELEMENT_TEXT_VCAL, "1.0", error);
		if (!ctype)
			goto error;
		smlDevInfDataStoreAddTx(ds, ctype);
		smlDevInfDataStoreSetRxPref(ds, SML_ELEMENT_TEXT_ICAL, "2.0");
		smlDevInfDataStoreSetTxPref(ds, 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))
	{
		smlDevInfDataStoreSetRxPref(ds, SML_ELEMENT_TEXT_PLAIN, "1.0");
		smlDevInfDataStoreSetTxPref(ds, 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));
		smlDevInfDataStoreSetRxPref(ds, ct, "1.0");
		smlDevInfDataStoreSetTxPref(ds, ct, "1.0");
		if (!add_devinf_ctcap(devinf, ct, "1.0", error))
			goto error;
	}

	// configure supported sync modes
	smlDevInfDataStoreSetSyncCap(ds, SML_DEVINF_SYNCTYPE_TWO_WAY, TRUE);
	smlDevInfDataStoreSetSyncCap(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->server) == SML_DS_CLIENT)
		// smlDevInfDataStoreSetSyncCap(ds, SML_DEVINF_SYNCTYPE_ONE_WAY_FROM_CLIENT, TRUE);
		smlTrace(TRACE_INTERNAL, "%s: SyncML clients only support SLOW and TWO WAY SYNC", __func__);
	else
		smlDevInfDataStoreSetSyncCap(ds, SML_DEVINF_SYNCTYPE_SERVER_ALERTED_SYNC, TRUE);

	smlDevInfAddDataStore(devinf, ds);
	smlTrace(TRACE_EXIT, "%s - content type newly added to devinf", __func__);
	return TRUE;
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

SmlBool smlDataSyncDevInfLoadRemote(
		SmlDataSyncObject *dsObject,
		SmlBool sendGet,
		SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %i, %p)", __func__, dsObject, sendGet, error);
	CHECK_ERROR_REF
	smlAssert(dsObject);
	smlAssertMsg(dsObject->session, "an active session is needed to have a remote device");

	/* if there is already a remote device information
	 * then cleanup the reference.
	 */
	if (dsObject->remoteDevInf)
		smlDevInfUnref(dsObject->remoteDevInf);

	dsObject->remoteDevInf = smlDevInfAgentGetSessionDevInf(dsObject->agent, dsObject->session);
	if (dsObject->remoteDevInf)
	{
		smlTrace(TRACE_INTERNAL, "%s: The remote DevInf was received.", __func__);
		smlDevInfRef(dsObject->remoteDevInf);
		_update_session_config_from_devinf(dsObject);
		/* DevInf caching is optional */
		if (dsObject->writeDevInfCallback) {
			smlTrace(TRACE_EXIT, "%s - calling writeDevInfCallback", __func__);
			return dsObject->writeDevInfCallback(
						dsObject, dsObject->remoteDevInf,
						dsObject->writeDevInfUserdata,
						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 (dsObject->readDevInfCallback)
		{
			smlTrace(TRACE_INTERNAL, "%s: calling read DevInf callback", __func__);
			SmlDevInf *devinf = dsObject->readDevInfCallback(
						dsObject,
						smlLocationGetURI(smlSessionGetTarget(dsObject->session)),
						dsObject->readDevInfUserdata, error);
			if (!devinf && *error)
				goto error;
			if (devinf) {
				if (!smlDevInfAgentSetSessionDevInf(
						dsObject->agent,
						dsObject->session,
						devinf,
						error))
				{
					goto error;
				}
				dsObject->remoteDevInf = smlDevInfAgentGetSessionDevInf(
								dsObject->agent,
								dsObject->session);
				/* smlDevInfAgentSetDevInf consumes the DevInf object.
				 * So the reference counter must be incremented for
				 * remoteDevInf.
				 */
				smlDevInfRef(dsObject->remoteDevInf);
				_update_session_config_from_devinf(dsObject);
				smlTrace(TRACE_EXIT, "%s - cached DevInf", __func__);
				return TRUE;
			}
		}
		if (sendGet)
		{
			/* the local device information is only send for fairness ;) */
			smlDevInfAgentSendDevInf(
				dsObject->agent,
				dsObject->session,
				error);
			smlDevInfAgentRequestDevInf(
				dsObject->agent,
				dsObject->session,
				error);
		}
		smlTrace(TRACE_EXIT, "%s - no remote DevInf available at all", __func__);
		return FALSE;
	}
error:
	smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
	return FALSE;
}

char *smlDataSyncDevInfGetIdentifier()
{
    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;
}

SmlBool smlDataSyncDevInfInit(
		SmlDataSyncObject *dsObject,
		SmlDevInfDevTyp type,
		SmlError **error)
{
	CHECK_ERROR_REF
	SmlDevInf *devinf = NULL;

	/* fix missing identifier */
	if (!dsObject->identifier)
		dsObject->identifier = smlDataSyncDevInfGetIdentifier();

	if (dsObject->fakeDevice)
	{
		smlTrace(TRACE_INTERNAL, "%s: faking devinf", __func__);
		devinf = smlDevInfNew(dsObject->identifier, SML_DEVINF_DEVTYPE_SMARTPHONE, error);
		if (!devinf)
			goto error;

		smlDevInfSetManufacturer(devinf, dsObject->fakeManufacturer);
		smlDevInfSetModel(devinf, dsObject->fakeModel);
		smlDevInfSetSoftwareVersion(devinf, dsObject->fakeSoftwareVersion);
	} else {
		smlTrace(TRACE_INTERNAL, "%s: not faking devinf", __func__);
		devinf = smlDevInfNew(dsObject->identifier, type, error);
		if (!devinf)
			goto error;

		smlDevInfSetSoftwareVersion(devinf, dsObject->fakeSoftwareVersion);
	}

	smlDevInfSetSupportsNumberOfChanges(devinf, TRUE);
	smlDevInfSetSupportsLargeObjs(devinf, TRUE);
	if (!dsObject->onlyLocaltime)
        smlDevInfSetSupportsUTC(devinf, TRUE);
	smlAssert(dsObject->maxMsgSize);
	smlAssert(dsObject->maxObjSize);

	dsObject->localDevInf = devinf;

	dsObject->agent = smlDevInfAgentNew(dsObject->localDevInf, error);
	if (!dsObject->agent)
		goto error;
	smlDevInfRef(dsObject->localDevInf); /* the agent consumes the object */
	
	if (!smlDevInfAgentRegister(dsObject->agent, dsObject->manager, error))
		goto error;

	return TRUE;
error:
	if (devinf)
		smlDevInfUnref(devinf);
	if (dsObject->agent)
		smlDevInfAgentFree(dsObject->agent);
	dsObject->localDevInf = NULL;
	dsObject->agent = NULL;
	return FALSE;
}

SmlBool smlDataSyncManageDevInf(
		SmlDataSyncObject *dsObject,
		SmlBool sendGet,
		SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %i, %p)", __func__, dsObject, sendGet, error);
	CHECK_ERROR_REF
	smlAssertMsg(dsObject->session, "an active session is needed to manage device informations");

	/* If the callback is available
	 * then we dump the local device information first.
	 */
	if (dsObject->writeDevInfCallback)
	{
		/* store device info */
		smlTrace(TRACE_INTERNAL, "%s: calling write DevInf callback", __func__);
		if (!dsObject->writeDevInfCallback(
				dsObject, dsObject->localDevInf,
				dsObject->writeDevInfUserdata, error))
			goto error;
	}

	/* handle remote device information */
	if (smlDataSyncDevInfLoadRemote(dsObject, sendGet, error) &&
	    dsObject->remoteDevInf &&
	    dsObject->handleRemoteDevInfCallback &&
	    !dsObject->handleRemoteDevInfCallback(
				dsObject,
				dsObject->remoteDevInf,
				dsObject->handleRemoteDevInfUserdata,
				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 (dsObject->remoteDevInf &&
		    !smlDevInfSupportsUTC(dsObject->remoteDevInf))
		{
			smlTrace(TRACE_INTERNAL,
				"%s: enforcing localtime because of remote DevInf",
				__func__);
			dsObject->onlyLocaltime = TRUE;
		}
	}

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

