/*
 * 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 <config.h>

#include "../syncml.h"
#include "sml_devinf_obj_internals.h"

#include <libsyncml/sml_support.h>
#include "libsyncml/sml_error_internals.h"

#include <libsyncml/sml_session.h>
#include <libsyncml/sml_elements.h>
#include <libsyncml/sml_command.h>
#include "libsyncml/parser/sml_xml_parse.h"
#include "libsyncml/data_sync_api/sml_location.h"

#ifdef WIN32
#include <windef.h>
#ifndef MSVC
// For MinGW
#include <winbase.h>
#endif
#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,
              GError **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__, (*error)->message);
	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) {
		g_object_unref(as->recvDevInf);
		as->recvDevInf = NULL;
	}
	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);
	GError *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;
		}

		/* verify that it is a real result with data */

		if (smlCommandGetType(result) != SML_COMMAND_TYPE_RESULTS) {
			g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC,
			            "devinf command was not a result");
			goto error;
		}
	
		SmlItem *item = smlCommandGetItem(result);
		if (!item) {
			g_set_error(&error, SML_ERROR, SML_ERROR_GENERIC,
			            "devinf result did not have a item");
			goto error;
		}

		char *data = NULL;
		gsize size = 0;
		if (!smlItemGetData(item, &data, &size, &error))
			goto error;
	
		/* parse and cache device information */

		as->recvDevInf = smlXmlDevInfParse(data, size, &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__, error->message);
	g_error_free(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 gboolean
_send_devinf (SmlDevInfAgent *agent,
              SmlSession *session,
              SmlCommand *get,
              GError **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 &&
	    smlSessionGetType(session) == SML_SESSION_TYPE_SERVER &&
	    !sml_dev_inf_get_support_large_objs(agent->devinf))
	{
		g_set_error(error, SML_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 (sml_dev_inf_get_support_large_objs(agent->devinf))
	{
		if (smlSessionGetLocalMaxMsgSize(session) < 1)
		{
			g_set_error(error, SML_ERROR, SML_ERROR_INTERNAL_MISCONFIGURATION,
				"If large object support is enabled then MaxMsgSize must be set.");
			goto error;
		}
		if (smlSessionGetLocalMaxObjSize(session) < 1)
		{
			g_set_error(error, SML_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 = smlCommandNewDevInfResult(get, agent->devinf, SML_DEVINF_VERSION_10, error);
			else if (smlSessionGetVersion(session) == SML_VERSION_12)
				result = smlCommandNewDevInfResult(get, agent->devinf, SML_DEVINF_VERSION_12, error);
			else
				result = smlCommandNewDevInfResult(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 = smlCommandNewDevInfPut(agent->devinf, SML_DEVINF_VERSION_10, error);
			else if (smlSessionGetVersion(session) == SML_VERSION_12)
				cmd = smlCommandNewDevInfPut(agent->devinf, SML_DEVINF_VERSION_12, error);
			else
				cmd = smlCommandNewDevInfPut(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__, (*error)->message);
	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;
	GError *error = NULL;
	char *data = NULL;
	gsize size = 0;
	
	if (!smlItemGetData(smlCommandGetItem(cmd), &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 = smlXmlDevInfParse(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__, error->message);
	g_error_free(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;
	GError *error = NULL;
	
	if (!_send_devinf(agent, session, cmd, &error))
		goto error;
	
	smlTrace(TRACE_EXIT, "%s", __func__);
	return;
error:
	smlSessionDispatchEvent(session, SML_SESSION_EVENT_ERROR, NULL, NULL, NULL, error);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, error->message);
	g_error_free(error);
}

SmlDevInfAgent*
smlDevInfAgentNew (SmlDevInf *devinf,
                   GError **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) {
		g_set_error(error, SML_ERROR, SML_ERROR_INTERNAL_NO_MEMORY,
		            "Cannot create new hash table.");
		goto error;
	}

	if (!sml_dev_inf_get_man(devinf) &&
	    !sml_dev_inf_set_man(devinf, "OpenSync", error))
		goto error;
	if (!sml_dev_inf_get_mod(devinf) &&
	    !sml_dev_inf_set_mod(devinf, "libsyncml", error))
		goto error;
	if (!sml_dev_inf_get_oem(devinf))
	{
#ifdef WIN32
		if (!sml_dev_inf_set_oem(devinf, "Microsoft (R)", error))
			goto error;
		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);
		if (!sml_dev_inf_set_fwv(devinf, szVersion, error))
			goto error;
#else
		struct utsname *buf = malloc(sizeof(struct utsname));
		if (uname(buf) == 0)
		{
			if (!sml_dev_inf_set_oem(devinf, buf->sysname, error))
				goto error;
			if (!sml_dev_inf_set_fwv(devinf, buf->release, error))
				goto error;
		}
		smlSafeFree((gpointer *)&buf);
#endif
	}
	if (!sml_dev_inf_get_swv(devinf) &&
	    !sml_dev_inf_set_swv(devinf, smlGetLibraryVersion(), error))
		goto error;

	smlTrace(TRACE_EXIT, "%s: %p", __func__, agent);
	return agent;
error:
	if (agent)
		smlSafeFree((gpointer *) &agent);
	smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, (*error)->message);
	return NULL;
}


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

/* Set the devinf of the remote peer. */
gboolean
smlDevInfAgentSetSessionDevInf (SmlDevInfAgent *agent,
                                SmlSession *session,
                                SmlDevInf *devinf,
                                GError **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__, (*error)->message);
	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 */
gboolean
smlDevInfAgentSendDevInf (SmlDevInfAgent *agent,
                          SmlSession *session,
                          GError **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__, (*error)->message);
	return FALSE;
}

/** Issues a Get request for the devinf on the session if needed */
gboolean
smlDevInfAgentRequestDevInf (SmlDevInfAgent *agent,
                             SmlSession *session,
                             GError **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 = smlCommandNewDevInfGet(SML_DEVINF_VERSION_10, error);
		else if (smlSessionGetVersion(session) == SML_VERSION_12)
			get = smlCommandNewDevInfGet(SML_DEVINF_VERSION_12, error);
		else
			get = smlCommandNewDevInfGet(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__, (*error)->message);
	return FALSE;
}

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

	SmlLocation *devinf10 = NULL;
	SmlLocation *devinf11 = NULL;
	SmlLocation *devinf12 = NULL;

	devinf12 = sml_location_new();
	sml_location_set_uri(devinf12, "./devinf12");

	devinf11 = sml_location_new();
	sml_location_set_uri(devinf11, "./devinf11");

	devinf10 = sml_location_new();
	sml_location_set_uri(devinf10, "./devinf10");

	/* 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;
	if (!smlManagerObjectRegister(
			manager, SML_COMMAND_TYPE_PUT, session, 
			NULL, devinf11, NULL, _recv_devinf, NULL, agent, 
			error))
		goto error;
	if (!smlManagerObjectRegister(
			manager, SML_COMMAND_TYPE_PUT, session, 
			NULL, devinf12, NULL, _recv_devinf, NULL, agent, 
			error))
		goto error;

	/* 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;
	if (!smlManagerObjectRegister(
			manager, SML_COMMAND_TYPE_GET, session, 
			devinf11, NULL, NULL, _request_devinf, NULL, agent, 
			error))
		goto error;
	if (!smlManagerObjectRegister(
			manager, SML_COMMAND_TYPE_GET, session, 
			devinf12, NULL, NULL, _request_devinf, NULL, agent, 
			error))
		goto error;

	/* 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;
	if (!smlManagerObjectRegister(
			manager, SML_COMMAND_TYPE_RESULTS, session,
			devinf11, NULL, NULL, _recv_devinf, NULL, agent,
		 	error))
		goto error;
	if (!smlManagerObjectRegister(
			manager, SML_COMMAND_TYPE_RESULTS, session, 
			devinf12, NULL, NULL, _recv_devinf, NULL, agent, 
			error))
		goto error;

	g_object_unref(devinf10);
	g_object_unref(devinf11);
	g_object_unref(devinf12);

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

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

	gboolean 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__, (*error)->message);
	return FALSE;
}

