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

#include <libsyncml/syncml.h>
#include "sml_devinf_obj.h"

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

#ifdef WIN32
#include <windef.h>
#else
#include<sys/utsname.h>
#endif

/* Design notice
 * 
 * There is only one SmlDevInfAgent per SmlManager
 * because there is only one local device information.
 * Therefore the agent must be able to manage one
 * device information per session because every remote
 * device can have different capabilities.
 */

/* functions to manage SmlDevInfAgentSession */

static SmlDevInfAgentSession* _new_session(
				SmlDevInfAgent *agent,
				SmlSession *session,
				SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, agent, session, error);

	CHECK_ERROR_REF
	smlAssert(agent);
	smlAssert(agent->sessions);
	smlAssert(session);

	SmlDevInfAgentSession *as = smlTryMalloc0(sizeof(SmlDevInfAgentSession), error);
	if (!as)
		goto error;

	g_hash_table_insert(agent->sessions, session, as);

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

static SmlDevInfAgentSession* _get_session(
				SmlDevInfAgent *agent,
				SmlSession *session)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, agent, session);

	smlAssert(agent);
	smlAssert(agent->sessions);
	smlAssert(session);

	SmlDevInfAgentSession *as = g_hash_table_lookup(agent->sessions, session);
	smlTrace(TRACE_EXIT, "%s - %p", __func__, as);
	return as;
}

static void _free_session (gpointer data)
{
	smlTrace(TRACE_ENTRY, "%s(%p)", __func__, data);

	SmlDevInfAgentSession *as = data;
	if (as->recvDevInf)
		smlDevInfUnref(as->recvDevInf);
	smlSafeFree((gpointer *) &as);

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

/* functions to manage SmlDevInfAgent */

static void _get_devinf_reply(SmlSession *session, SmlStatus *status, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, status, userdata);
	SmlError *error = NULL;
	SmlDevInfAgent *agent = userdata;
	
	if (smlStatusIsResult(status)) {
		SmlCommand *result = smlStatusGetResult(status);
		
		/* get cached session or add session to agent cache */

		SmlDevInfAgentSession *as = _get_session(agent, session);
		if (!as) {
			as = _new_session(agent, session, &error);
			if (!as)
				goto error;
		}

		/* cache device information */

		as->recvDevInf = smlDevInfFromResult(result, &error);
		if (!as->recvDevInf)
			goto error;

		/* send answer */
		
		SmlStatus *reply = smlCommandNewReply(result, SML_NO_ERROR, &error);
		if (!reply)
			goto error;
	
		if (!smlSessionSendReply(session, reply, &error)) {
			smlStatusUnref(reply);
			goto error;
		}
		
		smlStatusUnref(reply);
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return;

error:
	smlSessionDispatchEvent(session, SML_SESSION_EVENT_ERROR, NULL, NULL, NULL, error);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
	smlErrorDeref(&error);
}

static void _devinf_reply(SmlSession *session, SmlStatus *status, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, status, userdata);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
}

static SmlBool _send_devinf(SmlDevInfAgent *agent, SmlSession *session, SmlCommand *get, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, agent, session, get, error);
	CHECK_ERROR_REF
	SmlCommand *result = NULL;
	SmlCommand *cmd = NULL;

	/* It is a good idea to perform some sanity checks on the
	 * configuration before the device information is sent.
	 */

	/* If this is SyncML 1.1 or higher then large object support is
	 * required for OMA DS servers.
	 */
	if (smlSessionGetVersion(session) >= SML_VERSION_11 &&
	    session->sessionType == SML_SESSION_TYPE_SERVER &&
	    !smlDevInfSupportsLargeObjs(agent->devinf))
	{
		smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION,
			"OMA DS Server must support large object handling if SyncML 1.1 or higher is used.");
		goto error;
	}

	/* If large object support is enabled then MaxMsgSize and
	 * MaxObjSize must be set.
	 */
	if (smlDevInfSupportsLargeObjs(agent->devinf))
	{
		if (smlSessionGetLocalMaxMsgSize(session) < 1)
		{
			smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION,
				"If large object support is enabled then MaxMsgSize must be set.");
			goto error;
		}
		if (smlSessionGetLocalMaxObjSize(session) < 1)
		{
			smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION,
				"If large object support is enabled then MaxObjSize must be set.");
			goto error;
		}
	}

	/* get cached session or add session to agent cache */

	SmlDevInfAgentSession *as = _get_session(agent, session);
	if (!as) {
		as = _new_session(agent, session, error);
		if (!as)
			goto error;
	}

	/* Sanity checks complete. Sending device information ... */

	if (!as->devinfSent) {
		if (get) {
			if (smlSessionGetVersion(session) == SML_VERSION_10)
				result = smlDevInfNewResult(get, agent->devinf, SML_DEVINF_VERSION_10, error);
			else if (smlSessionGetVersion(session) == SML_VERSION_12)
				result = smlDevInfNewResult(get, agent->devinf, SML_DEVINF_VERSION_12, error);
			else
				result = smlDevInfNewResult(get, agent->devinf, SML_DEVINF_VERSION_11, error);
			
			if (!result)
				goto error;
			
			if (!smlSessionSendCommand(session, result, NULL, _devinf_reply, agent, error)) {
				smlCommandUnref(result);
				goto error;
			}
			
			smlCommandUnref(result);
			
			SmlStatus *reply = smlCommandNewReply(get, SML_NO_ERROR, error);
			if (!reply)
				goto error;
			
			if (!smlSessionSendReply(session, reply, error)) {
				smlStatusUnref(reply);
				goto error;
			}
			
			smlStatusUnref(reply);
		} else {
			if (smlSessionGetVersion(session) == SML_VERSION_10)
				cmd = smlDevInfNewPut(agent->devinf, SML_DEVINF_VERSION_10, error);
			else if (smlSessionGetVersion(session) == SML_VERSION_12)
				cmd = smlDevInfNewPut(agent->devinf, SML_DEVINF_VERSION_12, error);
			else
				cmd = smlDevInfNewPut(agent->devinf, SML_DEVINF_VERSION_11, error);
			
			if (!cmd)
				goto error;
			
			if (!smlSessionSendCommand(session, cmd, NULL, _devinf_reply, agent, error)) {
				smlCommandUnref(cmd);
				goto error;
			}

			smlCommandUnref(cmd);
		}
		as->devinfSent = TRUE;
	} else {
		smlTrace(TRACE_INTERNAL, "%s: Already sent the devinf!", __func__);

		if (get) {
			/* We return a generic error if we dont want to send the devinf to a get
			 * request. Hope that all devices like this */
			SmlStatus *reply = smlCommandNewReply(get, SML_ERROR_GENERIC, error);
			if (!reply)
				goto error;

			if (!smlSessionSendReply(session, reply, error)) {
				smlStatusUnref(reply);
				goto error;
			}

			smlStatusUnref(reply);
		} else {
			/* This means that a software component tries to
			 * send the device information but it was already
			 * sent. This can happen if the local library
			 * user is missing the remote device information
			 * and wants to send the get request together
			 * with its own device information. Nevertheless
			 * this is an error - but it can be ignored.
			 */
			smlTrace(TRACE_INTERNAL,
				"%s: libsyncml does not send local device information twice.",
				__func__);
		}
	}
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
	
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

static void _recv_devinf(SmlSession *session, SmlCommand *cmd, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, cmd, userdata);
	SmlDevInfAgent *agent = userdata;
	SmlError *error = NULL;
	char *data = NULL;
	unsigned int size = 0;
	
	if (!smlItemGetData(cmd->private.access.item, &data, &size, &error))
		goto error;

	SmlDevInfAgentSession *as = _get_session(agent, session);
	if (!as) {
		as = _new_session(agent, session, &error);
		if (!as)
			goto error;
	}

	as->recvDevInf = smlDevInfParse(data, size, &error);
	if (!as->recvDevInf)
		goto error;

	SmlStatus *reply = smlCommandNewReply(cmd, SML_NO_ERROR, &error);
	if (!reply)
		goto error;

	if (!smlSessionSendReply(session, reply, &error)) {
		smlStatusUnref(reply);
		goto error;
	}
	
	smlStatusUnref(reply);
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return;
	
error:
	smlSessionDispatchEvent(session, SML_SESSION_EVENT_ERROR, NULL, NULL, NULL, error);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
	smlErrorDeref(&error);
}

static void _request_devinf(SmlSession *session, SmlCommand *cmd, void *userdata)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, cmd, userdata);
	SmlDevInfAgent *agent = userdata;
	SmlError *error = NULL;
	
	if (!_send_devinf(agent, session, cmd, &error))
		goto error;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return;

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

SmlDevInfAgent *smlDevInfAgentNew(SmlDevInf *devinf, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, devinf, error);
	CHECK_ERROR_REF
	smlAssert(devinf);
	
	SmlDevInfAgent *agent = smlTryMalloc0(sizeof(SmlDevInfAgent), error);
	if (!agent)
		goto error;

	agent->devinf = devinf;
	agent->sessions = g_hash_table_new_full(NULL, NULL, NULL, _free_session);
	if (!agent->sessions) {
		smlErrorSet(error, SML_ERROR_INTERNAL_NO_MEMORY, "Cannot create new hash table.");
		goto error;
	}

	if (!smlDevInfGetManufacturer(devinf))
		smlDevInfSetManufacturer(devinf, "OpenSync");
	if (!smlDevInfGetModel(devinf))
		smlDevInfSetModel(devinf, "libsyncml");
	if (!smlDevInfGetOEM(devinf))
	{
#ifdef WIN32
		smlDevInfSetOEM(devinf, "Windows");
		DWORD dwVersion = GetVersion();
		DWORD dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
		DWORD dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion))); 
		DWORD dwBuild = 0;
		if (dwVersion < 0x80000000) dwBuild = (DWORD)(HIWORD(dwVersion));
		
		char szVersion[1024];
		sprintf(szVersion, "%d.%d (%d)", dwMajorVersion, dwMinorVersion, dwBuild);
		smlDevInfSetFirmwareVersion(devinf, szVersion);
#else
		struct utsname *buf = malloc(sizeof(struct utsname));
		if (uname(buf) == 0)
		{
			smlDevInfSetOEM(devinf, buf->sysname);
			smlDevInfSetFirmwareVersion(devinf, buf->release);
		}
		smlSafeFree((gpointer *)&buf);
#endif
	}
	if (!smlDevInfGetSoftwareVersion(devinf))
		smlDevInfSetSoftwareVersion(devinf, VERSION);

	smlTrace(TRACE_EXIT, "%s: %p", __func__, agent);
	return agent;

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


void smlDevInfAgentFree(SmlDevInfAgent *agent)
{
	smlTrace(TRACE_ENTRY, "%s(%p)", __func__, agent);
	smlAssert(agent);
	
	if (agent->devinf)
		smlDevInfUnref(agent->devinf);
	g_hash_table_destroy(agent->sessions);
	smlSafeFree((gpointer *)&agent);
	
	smlTrace(TRACE_EXIT, "%s", __func__);	
}

/* FIXME: DEPRECATED */
void smlDevInfAgentSetDevInf(SmlDevInfAgent *agent, SmlDevInf *devinf)
{
	smlTrace(TRACE_ERROR, "%s(%p, %p)", __func__, agent, devinf);
	smlAssertMsg(NULL, "This function is a design bug.");
}

/* FIXME: DEPRECATED */
SmlDevInf *smlDevInfAgentGetDevInf(SmlDevInfAgent *agent)
{
	smlTrace(TRACE_ERROR, "%s(%p, %p)", __func__, agent);
	smlAssertMsg(NULL, "This function is a design bug.");
	return NULL;
}

/* Set the devinf of the remote peer. */
SmlBool smlDevInfAgentSetSessionDevInf(
		SmlDevInfAgent *agent,
		SmlSession *session,
		SmlDevInf *devinf,
		SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, agent, session, devinf, error);

	CHECK_ERROR_REF
	smlAssert(agent);
	smlAssert(agent->sessions);
	smlAssert(session);
	smlAssert(devinf);

	SmlDevInfAgentSession *as = _get_session(agent, session);
	if (!as) {
		as = _new_session(agent, session, error);
		if (!as)
			goto error;
	}

	as->recvDevInf = devinf;

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

/* Get the devinf that was sent in the session. Returns FALSE if no devinf was received yet. */
SmlDevInf *smlDevInfAgentGetSessionDevInf(SmlDevInfAgent *agent, SmlSession *session)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, agent, session);

	smlAssert(agent);
	smlAssert(agent->sessions);
	smlAssert(session);

	SmlDevInfAgentSession *as = _get_session(agent, session);
	if (!as)
	{
		smlTrace(TRACE_EXIT, "%s - the session is not cached until now", __func__);
		return NULL;
	}

	smlTrace(TRACE_EXIT, "%s - %p", __func__, as->recvDevInf);
	return as->recvDevInf;
}

/** Issues a put request on the session if needed */
SmlBool smlDevInfAgentSendDevInf(SmlDevInfAgent *agent, SmlSession *session, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, agent, session, error);
	CHECK_ERROR_REF
	smlAssert(agent);
	
	if (!_send_devinf(agent, session, NULL, error))
		goto error;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
	
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

/** Issues a Get request for the devinf on the session if needed */
SmlBool smlDevInfAgentRequestDevInf(SmlDevInfAgent *agent, SmlSession *session, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, agent, session, error);
	CHECK_ERROR_REF
	smlAssert(agent);
	SmlCommand *get = NULL;

	SmlDevInfAgentSession *as = _get_session(agent, session);
	if (!as) {
		as = _new_session(agent, session, error);
		if (!as)
			goto error;
	}
	
	if (as->recvDevInf) {
		smlTrace(TRACE_EXIT, "%s: Already have the devinf", __func__);
		return TRUE;
	}
	
	if (!as->devinfRequested)
	{
		if (smlSessionGetVersion(session) == SML_VERSION_10)
			get = smlDevInfNewGet(SML_DEVINF_VERSION_10, error);
		else if (smlSessionGetVersion(session) == SML_VERSION_12)
			get = smlDevInfNewGet(SML_DEVINF_VERSION_12, error);
		else
			get = smlDevInfNewGet(SML_DEVINF_VERSION_11, error);

		if (!get)
			goto error;

		if (!smlSessionSendCommand(session, get, NULL, _get_devinf_reply, agent, error)) {
			smlCommandUnref(get);
			goto error;
		}

		smlCommandUnref(get);

		as->devinfRequested = TRUE;
	} else {
		smlTrace(TRACE_INTERNAL, "%s: Already requested the devinf!", __func__);
	}
			
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;
	
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

SmlBool smlDevInfAgentRegisterSession(SmlDevInfAgent *agent, SmlManager *manager, SmlSession *session, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, agent, manager, session, error);
	CHECK_ERROR_REF
	smlAssert(agent);
	smlAssert(manager);

	SmlLocation *devinf12 = smlLocationNew("./devinf12", NULL, error);
	SmlLocation *devinf11 = smlLocationNew("./devinf11", NULL, error);
	SmlLocation *devinf10 = smlLocationNew("./devinf10", NULL, error);

	if (!devinf12 || !devinf11 || !devinf10)
		goto error;

	/* PUT callbacks
	 * 
	 * This command is used to actively send device information without
	 * being asked for. Usually a client do this if it is the first
	 * connection with the server or the client has no problems with
	 * its bandwith.
	 *
	 * Sometimes a server is missing the client's device information.
	 * In this situation the server can send a PUT and a GET to force
	 * the client to send the device information. This can happen if
	 * the client know that server should have its device information
	 * because of an earlier synchronization but the server does not
	 * have the device information. Sometimes server databases (incl.
	 * the device information database) are lost or it is simply a
	 * new server but with the old server address (IP) or name (DNS).
	 *
	 * Please note that a client can ignore a PUT from a server.
	 * Nevertheless it is recommended that a client does not ignore
	 * the device information of a server.
	 */

	smlTrace(TRACE_INTERNAL, "%s: register callbacks for PUT command", __func__);
	if (!smlManagerObjectRegister(
			manager, SML_COMMAND_TYPE_PUT, session, 
			NULL, devinf10, NULL, _recv_devinf, NULL, agent, 
			error))
		goto error_free_loc;
	if (!smlManagerObjectRegister(
			manager, SML_COMMAND_TYPE_PUT, session, 
			NULL, devinf11, NULL, _recv_devinf, NULL, agent, 
			error))
		goto error_free_loc;
	if (!smlManagerObjectRegister(
			manager, SML_COMMAND_TYPE_PUT, session, 
			NULL, devinf12, NULL, _recv_devinf, NULL, agent, 
			error))
		goto error_free_loc;

	/* GET callbacks
	 * 
	 * This command is used to request the device information of a
	 * remote peer. Usually the client do this if it is the first
	 * connection with this server or the client does not remember
	 * the server's device information. The most mobiles only do
	 * this for the first successful connection. Data costs usually
	 * high amounts of money in the mobile world and the bandwith is
	 * small in the mobile world.
	 *
	 * Nevertheless if a mobile does not perform the PUT command and
	 * the server does not know the device information of the client
	 * then the server can send a GET command and the client has to
	 * react on it. Please see above why this can happen.
	 *
	 * Please note that client and server must be able to handle the
	 * GET command. A RESULTS command must be send with the local
	 * device information.
	 */

	smlTrace(TRACE_INTERNAL, "%s: register callbacks for GET command", __func__);
	if (!smlManagerObjectRegister(
			manager, SML_COMMAND_TYPE_GET, session, 
			devinf10, NULL, NULL, _request_devinf, NULL, agent, 
			error))
		goto error_free_loc;
	if (!smlManagerObjectRegister(
			manager, SML_COMMAND_TYPE_GET, session, 
			devinf11, NULL, NULL, _request_devinf, NULL, agent, 
			error))
		goto error_free_loc;
	if (!smlManagerObjectRegister(
			manager, SML_COMMAND_TYPE_GET, session, 
			devinf12, NULL, NULL, _request_devinf, NULL, agent, 
			error))
		goto error_free_loc;

	/* RESULTS callbacks
	 *
	 * This command is used to send the device information which
	 * was requested with a GET command. This command can be send by
	 * server and clients. Please see above when this happens.
	 *
	 * A server must be able to handle and interpret a RESULTS
	 * command from a client. Especially this is required because a
	 * server must explicitly request such a command with a previous
	 * GET command.
	 *
	 * Please note that a client can ignore a RESULTS from a server.
	 * Nevertheless it is recommended that a client does not ignore
	 * the device information of a server.
	 */

	smlTrace(TRACE_INTERNAL, "%s: register callbacks for RESULTS command", __func__);
	if (!smlManagerObjectRegister(
			manager, SML_COMMAND_TYPE_RESULTS, session, 
			devinf10, NULL, NULL, _recv_devinf, NULL, agent, 
			error))
		goto error_free_loc;
	if (!smlManagerObjectRegister(
			manager, SML_COMMAND_TYPE_RESULTS, session,
			devinf11, NULL, NULL, _recv_devinf, NULL, agent,
		 	error))
		goto error_free_loc;
	if (!smlManagerObjectRegister(
			manager, SML_COMMAND_TYPE_RESULTS, session, 
			devinf12, NULL, NULL, _recv_devinf, NULL, agent, 
			error))
		goto error_free_loc;

	smlLocationUnref(devinf10);
	smlLocationUnref(devinf11);
	smlLocationUnref(devinf12);

	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

error_free_loc:
	if (devinf10)
		smlLocationUnref(devinf10);
	if (devinf11)
		smlLocationUnref(devinf11);
	if (devinf12)
		smlLocationUnref(devinf12);
error:
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
	return FALSE;
}

SmlBool smlDevInfAgentRegister(SmlDevInfAgent *agent, SmlManager *manager, SmlError **error)
{
	smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, agent, manager, error);
	CHECK_ERROR_REF

	SmlBool retval = smlDevInfAgentRegisterSession(agent, manager, NULL, error);
	
	if (!retval)
		goto error;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return TRUE;

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

