/*PROTECTED REGION ID(DataBase::main.cpp) ENABLED START*/
//=============================================================================
//
// file :        DataBase.cpp
//
// description : C++ source for the DataBase device server main.
//               The main rule is to initialise (and create) the Tango
//               system and to create the DServerClass singleton.
//                The main should be the same for every Tango device server.
//
// project :     TANGO.
//
//=============================================================================
//                This file is generated by POGO
//        (Program Obviously used to Generate tango Object)
//=============================================================================
//
//
// Copyright (C) :      2004,2005,2006,2007,2008,2009,2010,2011,2012,2013,2014
//						European Synchrotron Radiation Facility
//                      BP 220, Grenoble 38043
//                      FRANCE
//
// This file is part of Tango.
//
// Tango is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Tango 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Tango.  If not, see <http://www.gnu.org/licenses/>.
//

#include <tango/tango.h>
#include "DataBase.h"
#include "DatabaseConnectionPool.h"
#include "Logging.h"

#ifndef WIN32
#include <sys/resource.h>
#endif

#ifndef  _TG_WINDOWS_
#include <arpa/inet.h>
#include <omniORB4/internal/giopStrand.h>
#include <omniORB4/internal/giopStream.h>
#include <omniORB4/internal/GIOP_S.h>

#include <omniORB4/omniInterceptors.h>

#include <unordered_set>
std::unordered_set<std::string> addresses_allowed;
omni_thread::key_t key;
int acl_enabled;

CORBA::Boolean retrieve_client_address(omni::omniInterceptors::serverReceiveRequest_T::info_T& info)
{
	// The address returned by peeraddress() will be of the form "giop:tcp:<ip>:<port>"
	// or "giop:ssl:<ip>:<port>" where <ip> seems to be in the inet_ntop format:
	// omniORB/orbcore/tcp/tcpConnection.cc +228 and omniORB/orbcore/tcpSocket.cc +1071
	Value_t *v = new Value_t(info.giop_s.strand().connection->peeraddress());
	omni_thread::self()->set_value(key, v);
	return true;
}

bool convert_to_ntop_format(std::string &address)
{
	int af;
	char str[INET6_ADDRSTRLEN];
	char buf[sizeof(struct in6_addr)];

	/* Convert to binary form */
	if (inet_pton(AF_INET, address.c_str(), buf)) {
	  af = AF_INET;
	} else {
	  if (inet_pton(AF_INET6, address.c_str(), buf))
	    af = AF_INET6;
	  else {
	    std::cerr << "Address " << address
	      << " is ignored due invalid format" << std::endl;
	    return false;
	  }
	}

	/* Convert to text form */
	if (inet_ntop(af, buf, str, sizeof(buf))) {
	  address = std::string(str);
	  return true;
	} else {
	  std::cerr << "Fail to convert address to text format" << std::endl;
	  return false;
	}
}

void load_authorization_rules(std::ifstream &acl_file)
{
	std::string line;
	while (std::getline(acl_file, line))
	{
	  line.erase(0, line.find_first_not_of(" "));
	  size_t pos = line.find_first_of("#");
	  if (pos != std::string::npos)
	    line.erase(pos, std::string::npos);

	  /* Ignore comments which have to start with # and empty lines */
	  if (line[0] != '#' && line.size()) {
	    std::string transport = line.substr(0, MIN_TRANSPORT_STRING_LENGTH);
	    if (transport == "giop:uni" /* giop:unix */) {
	      /* UNIX domain sockets are always allowed by default */
	    } else if (transport == "giop:tcp" || transport == "giop:ssl") {
	      std::string prefix = line.substr(0, 9);
	      std::string address = line.substr(9);
	      char octet[4][4];
	      /* Try to extract octet by octet in the case of IPv4 address */
	      if (sscanf(address.c_str(), "%[0-9].%[0-9].%[0-9].%[0-9*]",
				      octet[0], octet[1], octet[2], octet[3]) != 4) {
	        // It should be an IPv6 pass it as is (without expand anything)
	        if (convert_to_ntop_format(address))
	          addresses_allowed.insert(prefix + address);
	      } else {
	        if (octet[3][0] == '*') {
	          /* Expand wildcard to 1..254 */
		  char buf[4];
	          for (int i=1; i<255; ++i) {
		    sprintf(buf,"%d",i);
	            address = std::string(octet[0]) + "." + octet[1] + "." + octet[2] + "." + buf;
	            if (convert_to_ntop_format(address))
	              addresses_allowed.insert(prefix + address);
	          }
	        } else {
	          if (convert_to_ntop_format(address))
	            addresses_allowed.insert(prefix + address);
	        }
	      }
	    } else {
	      /* giop:htt, giop:fd: or a future yet no-existing transport layer
	       * are not supported yet so blocked by default */
	    }
	  }
	}
}
#endif

int main(int argc,char *argv[])
{

	TANGO_LOG << "main(): arrived " << std::endl;

	Tango::Util *tango_util;
	Tango::Util::_UseDb = false; // suppress database use

#ifndef WIN32

        // Setting maximum number of opened file

	// Default limit
	struct rlimit limit;
	limit.rlim_cur = 1024;
	limit.rlim_max = 1024;

#ifndef  _TG_WINDOWS_
	// Browse argv and try to find the --aclFile option
	int k = 0;
	while(k<argc && !acl_enabled) {
	  acl_enabled = (strcasecmp(argv[k],"-aclFile")==0);
	  if(!acl_enabled) k++;
	}
	if( acl_enabled ) {
	  if(k>=argc-1) {
	    TANGO_LOG << "Invalid aclFile parameter." << std::endl;
	    return -1;
	  }
	  std::ifstream infile;
	  infile.open(argv[k+1]);
	  if( infile.is_open() ) {
	    load_authorization_rules(infile);
	    infile.close();
	  } else {
	    TANGO_LOG << "File " << argv[k+1] << " not found." << std::endl;
	    return -1;
	  }
	}
#endif

	// Browse argv and try to find the -maxFile option
	int found = 0;
	int i = 0;
	while(i<argc && !found) {
	  found = (strcasecmp(argv[i],"-maxFile")==0);
	  if(!found) i++;
	}
	if( found ) {
	  if(i>=argc-1) {
	    TANGO_LOG << "Invalid flimit parameter." << std::endl;
	    return -1;
	  }
	  int flimit = atoi(argv[i+1]);
	  if(flimit==0) {
	    TANGO_LOG << "Invalid flimit parameter." << std::endl;
	    return -1;
	  }
	  limit.rlim_cur = flimit;
	  limit.rlim_max = flimit;
	}

	// Apply the max open file limit
	if( setrlimit(RLIMIT_NOFILE,&limit) != 0 ) {
	  TANGO_LOG << "setrlimit(RLIMIT_NOFILE," << (int)limit.rlim_cur << ") failed." << std::endl;
	  if(errno==EPERM) {
	    TANGO_LOG << "You may need to increase maximum number of opened file system limit." << std::endl;
	  } else {
	    TANGO_LOG << "setrlimit() failed with error code : " << (int)errno << std::endl;
	  }
	  return -1;
	}
#endif

	// Browse argv and try to find the -connPoolSize option
	int j = 0;
	int found_conn = 0;
	int conn_size = DEFAULT_CONN_POOL_SIZE;
	while(j<argc && !found_conn) {
#ifndef WIN32
	  found_conn = (strcasecmp(argv[j],"-poolSize")==0);
#else
	  found_conn = (stricmp(argv[j],"-poolSize")==0);
#endif
	  if(!found_conn) j++;
	}
	if( found_conn ) {
	  if(j>=argc-1) {
	    TANGO_LOG << "Invalid poolSize parameter." << std::endl;
	    return -1;
	  }
	  conn_size = atoi(argv[j+1]);
	  if(conn_size<=0) {
	    TANGO_LOG << "Invalid poolSize parameter." << std::endl;
	    return -1;
	  }
	}

	DatabaseConnectionPool::set_conn_pool_size(conn_size);

	try
	{
//
// Initialise the device server
//
		TANGO_LOG_INFO << "main(): calling  Tango::Util::Init(argc,argv)" << std::endl;
		tango_util = Tango::Util::init(argc,argv);

// construct database name

		DataBase_ns::DataBase::db_name = "sys/database/";
		DataBase_ns::DataBase::db_name.append(argv[1]);
		TANGO_LOG_INFO << "main(): create DataBase " << DataBase_ns::DataBase::db_name << std::endl;

//
// Create and install interceptors
//

		DataBase_ns::DbInter *dbi = new DataBase_ns::DbInter();
		tango_util->set_interceptors(dbi);

#ifndef  _TG_WINDOWS_
//
// Add an OmniORB interceptor
//
		if (acl_enabled) {
		  key = omni_thread::allocate_key();
		  omniORB::getInterceptors()->serverReceiveRequest.add(&retrieve_client_address);
		}
#endif

//
// Set the serialization method
//

		tango_util->set_serial_model(Tango::NO_SYNC);

//
// Create the device server singleton which will create everything
//

		TANGO_LOG_INFO << "main(): calling  tango_util->server_init()" << std::endl;
		tango_util->server_init();

//
// Export devices to the outside world as CORBA named servant and to TANGO
// database
//
		Tango::DeviceImpl *dbase, *dserver;
        Tango::DevVarStringArray *export_parms = new Tango::DevVarStringArray();

		dserver = tango_util->get_dserver_device();
		dbase = tango_util->get_device_by_name(DataBase_ns::DataBase::db_name);
//
// export database as named servant
//
		TANGO_LOG << "main(): export DataBase as named servant (name=database)" << std::endl;
		export_parms->length(5);

// export dserver object to TANGO database

		Tango::Device_var d = dserver->_this();
		dserver->set_d_var(Tango::Device::_duplicate(d));
		(*export_parms)[0] = CORBA::string_dup(dserver->get_name().c_str());
		const char *dserver_str_ior = Tango::Util::instance()->get_orb()->object_to_string(d);
		(*export_parms)[1] = CORBA::string_dup(dserver_str_ior);
		delete [] dserver_str_ior;
		(*export_parms)[2] = CORBA::string_dup(tango_util->get_host_name().c_str());
		(*export_parms)[3] = CORBA::string_dup(tango_util->get_pid_str().c_str());
		(*export_parms)[4] = CORBA::string_dup(tango_util->get_version_str().c_str());

		(static_cast<DataBase_ns::DataBase *>(dbase))->db_export_device(export_parms);

// export database object to TANGO database

		(*export_parms)[0] = CORBA::string_dup(dbase->get_name().c_str());
		const char *str_ior = Tango::Util::instance()->get_orb()->object_to_string(dbase->get_d_var());
		(*export_parms)[1] = CORBA::string_dup(str_ior);
		delete [] str_ior;
		((DataBase_ns::DataBase*)dbase)->db_export_device(export_parms);

		delete export_parms;

//
// Run the endless loop
//

		TANGO_LOG << "Ready to accept request" << std::endl;
		(tango_util->get_orb())->run();
	}
	catch (std::bad_alloc&)
	{
		TANGO_LOG << "Can't allocate memory to store device object !!!" << std::endl;
		TANGO_LOG << "Exiting" << std::endl;
	}
	catch (CORBA::Exception &e)
	{
		TANGO_LOG << "Received a CORBA::Exception" << std::endl;
		Tango::Except::print_exception(e);
		TANGO_LOG << "Exiting" << std::endl;
	}

	return(0);
}


/*PROTECTED REGION END*/	//	DataBase::main.cpp
