/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/*
 *  Copyright (C) 2008 OMC Denmark ApS.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "get_lorica.h"
#include "utils/macros.h"

#include <stdio.h>
#include <string.h>

// Will realloc and strcat src to *dest, freeing *dest if
// not successfull. Returns a pointer to the longer string.
static char*
append_string(char **dest,
	      const char *src)
{
	char *retv = NULL;
	size_t size;


	if (!*dest) {
		size = sizeof(char) * (strlen(src) + 1);
		retv = (char*)calloc(size, sizeof(char));
	} else {
		size = sizeof(char) * (strlen(*dest) + strlen(src) + 1);
		retv = (char*)realloc((void*)*dest, size);
	}
	if (!retv) {
		free(*dest);
		return NULL;
	}

	strcat_s(retv, size, src);
	*dest = retv;

	return *dest;
}


// Build corbaloc string or return NULL on error
static char*
corbaloc_str(const char *IORTable_name,
	     const char *hostname,
	     const unsigned short port)
{
	if (!IORTable_name)
		return NULL;
	if (!hostname)
		return NULL;
	if (!port)
		return NULL;

	char *retv = NULL;
	char brutus_port[32] = { '\0' };
	if (0 >= _snprintf_s(brutus_port, sizeof(brutus_port), sizeof(brutus_port), "%hu", port))
		goto out;

	if (!append_string(&retv, "corbaloc:iiop:1.2@"))
		goto out;
	if (!append_string(&retv, hostname))
		goto out;
	if (!append_string(&retv, ":"))
		goto out;
	if (!append_string(&retv, (const char*)brutus_port))
		goto out;
	if (!append_string(&retv, "/"))
		goto out;
	append_string(&retv, IORTable_name);

out:
	return retv;
}

::Lorica::ReferenceMapper_ptr
get_lorica(::CORBA::ORB_ptr orb,
	   bool &secure)
{
	BRUTUS_LOG_INF("Entering get_lorica()");

	::Lorica::ReferenceMapper_var reference_mapper = ::Lorica::ReferenceMapper::_nil();
	secure = false;

	BRUTUS_LOG_INF("Getting Lorica reference");

	char *lorica_loc = corbaloc_str(::Lorica::ReferenceMapper::IOR_TABLE_KEY, "localhost", ::Lorica::ReferenceMapper::lorica_in_port);
	if (!lorica_loc)
		return ::Lorica::ReferenceMapper::_nil();

	::CORBA::Object_var mapper_obj = ::CORBA::Object::_nil();
	try {
		mapper_obj = orb->string_to_object(lorica_loc);
		free(lorica_loc);
		if (CORBA::is_nil(mapper_obj.in())) {
			BRUTUS_LOG_CRITICAL("Could not resolve Lorica reference mapper - service down?");
			return ::Lorica::ReferenceMapper::_nil();
		}
	}
	catch (const ::CORBA::Exception &e) {
		BRUTUS_LOG_CRITICAL(e._info().c_str());
		BRUTUS_LOG_CRITICAL("CORBA exception caught when resolving Lorica");
		return ::Lorica::ReferenceMapper::_nil();
	}
	catch (...) {
		BRUTUS_LOG_CRITICAL("Unknown C++ exception caught when resolving Lorica");
		return ::Lorica::ReferenceMapper::_nil();
	}

	try {
		reference_mapper = ::Lorica::ReferenceMapper::_narrow(mapper_obj.in());
		if (::CORBA::is_nil(reference_mapper.in())) {
			BRUTUS_LOG_CRITICAL("Could not narrow object to Lorica reference mapper");
			return ::Lorica::ReferenceMapper::_nil();
		}
	}
	catch (const ::CORBA::Exception &e) {
		BRUTUS_LOG_CRITICAL(e._info().c_str());
		BRUTUS_LOG_CRITICAL("CORBA exception caught when narowing reference mapper");
		return ::Lorica::ReferenceMapper::_nil();
	}
	catch (...) {
		BRUTUS_LOG_CRITICAL("Unknown C++ exception caught when narowing reference mapper");
		return ::Lorica::ReferenceMapper::_nil();
	}

	try {
		secure = (bool)reference_mapper->supports_security();
		if (!secure)
			BRUTUS_LOG_INF("Secure Lorica not supported");
	}
	catch (const ::CORBA::Exception &e) {
		BRUTUS_LOG_CRITICAL(e._info().c_str());
		BRUTUS_LOG_CRITICAL("CORBA exception caught when narowing reference mapper");
		return ::Lorica::ReferenceMapper::_nil();
	}
	catch (...) {
		BRUTUS_LOG_CRITICAL("Unknown C++ exception caught when narowing reference mapper");
		return ::Lorica::ReferenceMapper::_nil();
	}

	BRUTUS_LOG_INF("Leaving get_lorica()");
	return reference_mapper._retn();
}
