// **********************************************************************
//
// Copyright (c) 1999
// Object Oriented Concepts, Inc.
// Billerica, MA, USA
//
// All Rights Reserved
//
// **********************************************************************

#include <OB/Basic.h>
#include <OB/Except.h>
#include <OB/Template.h>
#include <OB/Declarations.h>
#include <OB/IOP.h>
#include <OB/Object.h>
#include <OB/Policy.h>
#include <OB/OBPolicies.h>
#include <OB/GIOP.h>
#include <OB/IIOP.h>
#include <OB/OCI.h>
#include <OB/OCI_IIOP.h>
#include <OB/Util.h>
#include <OB/Net.h>

#include <OCI_IIOP_impl.h>

//
// Undefine minor (this is needed for SunOS 4.1 and some other Unix flavors)
//
#undef minor

//#define OB_OCI_DEBUG

// ----------------------------------------------------------------------
// Some utility functions
// ----------------------------------------------------------------------

CORBA_ULong
OBIIOPPrintProfileBody(ostream& out, const IOP_IOR& ior)
{
    CORBA_ULong count = 0;
    for(CORBA_ULong i = 0 ; i < ior.profiles.length() ; i++)
    {
	if(ior.profiles[i].tag == IOP_TAG_INTERNET_IOP)
	{
	    count++;

	    //
	    // Get the IIOP profile body
	    //
	    const CORBA_Octet* coct = ior.profiles[i].profile_data.data();
	    CORBA_Boolean endian;
	    OBUnmarshal(endian, coct, false);
	    IIOP_ProfileBody body;
	    OBUnmarshal(body, coct, endian != OBEndian);

	    //
	    // Print info
	    //
	    out << "IIOP profile #" << count << ":\n";
	    out << "iiop_version: " << int(body.iiop_version.major) << '.'
		<< int(body.iiop_version.minor) << '\n';
	    out << "host: " << body.host << '\n';
	    out << "port: " << body.port << '\n';
	    out << "object_key:\n";
	    OBPrintOctets(out, body.object_key.data(),
			  body.object_key.length());
	}
    }

    return count;
}

CORBA_Boolean
OBIIOPCompareBodies(const IIOP_ProfileBody& body1,
		    const IIOP_ProfileBody& body2)
{
    //
    // Compare versions
    //
    if(body1.iiop_version.major != body2.iiop_version.major ||
       body1.iiop_version.minor != body2.iiop_version.minor)
	return CORBA_FALSE;
    
    //
    // Compare ports
    //
    if(body1.port != body2.port)
	return CORBA_FALSE;
    
    //
    // Compare object keys
    //
    if(body1.object_key.length() != body2.object_key.length())
	return CORBA_FALSE;
    
    if(memcmp(body1.object_key.data(), body2.object_key.data(),
	      body1.object_key.length()) != 0)
	return CORBA_FALSE;
    
    //
    // Direct host name comparison
    //
    if(strcmp(body1.host, body2.host) != 0)
    {
	//
	// Direct host name comparision failed - must look up
	// addresses to be really sure if the hosts differ
	//
	try
	{
	    struct sockaddr_in addr1;
	    OBGetInAddr(body1.host, addr1);
	    
	    struct sockaddr_in addr2;
	    OBGetInAddr(body2.host, addr2);
	    
	    if(addr1.sin_addr.s_addr != addr2.sin_addr.s_addr)
		return CORBA_FALSE;
	}
	catch(const CORBA_COMM_FAILURE&)
	{
	    //
	    // Return false on hostname lookup failure
	    //
	    return CORBA_FALSE;
	}
    }
    
    //
    // OK, found a match
    //
    return CORBA_TRUE;
}

CORBA_Boolean
OBIIOPEquivalent(const OCI_IOR& ior1, const OCI_IOR& ior2)
{
    CORBA_ULong p1, p2, b1, b2;
    CORBA_ULong cnt1 = 0, cnt2 = 0;
    IIOP_ProfileBody** bodies1 = 0;
    IIOP_ProfileBody** bodies2 = 0;

    //
    // Calculate number of IIOP profiles in ior1
    //
    for(p1 = 0 ; p1 < ior1.profiles.length() ; p1++)
	if(ior1.profiles[p1].tag == IOP_TAG_INTERNET_IOP)
	    cnt1++;
    
    //
    // Calculate number of IIOP profiles in ior2
    //
    for(p2 = 0 ; p2 < ior2.profiles.length() ; p2++)
	if(ior2.profiles[p2].tag == IOP_TAG_INTERNET_IOP)
	    cnt2++;

    //
    // Return false now if the number of IIOP profile bodies do not
    // match
    //
    if(cnt1 != cnt2)
	return CORBA_FALSE;

    //
    // Create an array with all IIOP profile bodies of ior1
    //
    bodies1 = new IIOP_ProfileBody*[cnt1];
    for(p1 = 0, b1 = 0 ; p1 < ior1.profiles.length() ; p1++)
	if(ior1.profiles[p1].tag == IOP_TAG_INTERNET_IOP)
	{
	    const CORBA_Octet* coct =
		ior1.profiles[p1].profile_data.data();
	    CORBA_Boolean endian;
	    OBUnmarshal(endian, coct, false);
	    IIOP_ProfileBody* p = new IIOP_ProfileBody;
	    OBUnmarshal(*p, coct, endian != OBEndian);
	    bodies1[b1++] = p;
	}
    
    assert(b1 == cnt1);
    
    //
    // Create an array with all IIOP profile bodies of ior2
    //
    bodies2 = new IIOP_ProfileBody*[cnt2];
    for(p2 = 0, b2 = 0 ; p2 < ior2.profiles.length() ; p2++)
	if(ior2.profiles[p2].tag == IOP_TAG_INTERNET_IOP)
	{
	    const CORBA_Octet* coct =
		ior2.profiles[p2].profile_data.data();
	    CORBA_Boolean endian;
	    OBUnmarshal(endian, coct, false);
	    IIOP_ProfileBody* p = new IIOP_ProfileBody;
	    OBUnmarshal(*p, coct, endian != OBEndian);
	    bodies2[b2++] = p;
	}
    
    assert(b2 == cnt2);

    //
    // Check for profile body matches
    //
    for(b1 = 0 ; b1 < cnt1 ; b1++)
    {
	for(b2 = 0 ; b2 < cnt2 ; b2++)
	{
	    if(!bodies2[b2])
		continue;
	    
	    //
	    // Compare profile bodies
	    //
	    if(!OBIIOPCompareBodies(*bodies1[b1], *bodies2[b2]))
		continue;
	    
	    //
	    // OK, found a match
	    //
	    delete bodies1[b1];
	    bodies1[b1] = 0;
	    delete bodies2[b2];
	    bodies2[b2] = 0;
	}
    }

    //
    // Check whether there are any unmatched IIOP profile bodies
    //
    CORBA_ULong unmatched1 = 0;
    for(b1 = 0 ; b1 < cnt1 ; b1++)
	if(bodies1[b1])
	{
	    unmatched1++;
	    delete bodies1[b1];
	}

    delete [] bodies1;

    CORBA_ULong unmatched2 = 0;
    for(b2 = 0 ; b2 < cnt2 ; b2++)
	if(bodies2[b2])
	{
	    unmatched2++;
	    delete bodies2[b2];
	}

    delete [] bodies2;

    return unmatched1 == 0 && unmatched2 == 0;
}

CORBA_ULong
OBIIOPHash(const OCI_IOR& ior, CORBA_ULong maximum)
{
    CORBA_ULong hash = 0;

    for(CORBA_ULong i = 0 ; i < ior.profiles.length() ; i++)
    {
	if(ior.profiles[i].tag == IOP_TAG_INTERNET_IOP)
	{
	    //
	    // Get the IIOP profile body
	    //
	    const CORBA_Octet* coct = ior.profiles[i].profile_data.data();
	    CORBA_Boolean endian;
	    OBUnmarshal(endian, coct, false);
	    IIOP_ProfileBody body;
	    OBUnmarshal(body, coct, endian != OBEndian);

	    //
	    // Add port to hash
	    //
	    hash ^= body.port;

	    //
	    // Add object key to hash
	    //
	    for(CORBA_ULong j = 0 ; j + 1 < body.object_key.length() ; j += 2)
		hash ^= body.object_key[j + 1] * 256 + body.object_key[j];
	}
    }

    return hash % (maximum + 1);
}

OCI_ObjectKey*
OBIIOPExtractKey(const OCI_IOR& ior, const char* host, CORBA_UShort port,
		 bool loopbackMatches)
{
    for(CORBA_ULong i = 0 ; i < ior.profiles.length() ; i++)
    {
	if(ior.profiles[i].tag == IOP_TAG_INTERNET_IOP)
	{
	    //
	    // Get the IIOP profile body
	    //
	    const CORBA_Octet* coct = ior.profiles[i].profile_data.data();
	    CORBA_Boolean endian;
	    OBUnmarshal(endian, coct, false);
	    IIOP_ProfileBody body;
	    OBUnmarshal(body, coct, endian != OBEndian);

	    //
	    // Compare ports
	    //
	    if(port != body.port)
		continue;
	    
	    //
	    // Direct host name comparison
	    //
	    if(strcmp(host, body.host) != 0)
	    {
		//
		//
		// Direct host name comparision failed - must look up
		// addresses to be really sure if the hosts differ
		//
		try
		{
		    struct sockaddr_in addr1;
		    OBGetInAddr(host, addr1);
		    
		    struct sockaddr_in addr2;
		    OBGetInAddr(body.host, addr2);
		    
		    if(addr1.sin_addr.s_addr != addr2.sin_addr.s_addr)
		    {
			//
			// Address comparison failed - shall I extract
			// the key if the profile body contains the
			// loopback address?
			//
			if(loopbackMatches)
			{
			    if(addr2.sin_addr.s_addr != htonl(INADDR_LOOPBACK))
			    {
				continue;
			    }
			}
			else
			    continue;
		    }
		}
		catch(const CORBA_COMM_FAILURE&)
		{
		    //
		    // Continue on hostname lookup failure
		    //
		    continue;
		}
	    }
	    
	    //
	    // OK, found a match
	    //
	    return new OCI_ObjectKey(body.object_key);
	}
    }

    //
    // No match found
    //
    return new OCI_ObjectKey();
}

// ----------------------------------------------------------------------
// OCI_IIOP_Transport_impl private and protected member implementations
// ----------------------------------------------------------------------

void
OCI_IIOP_Transport_impl::setBlock(CORBA_Boolean block)
{
    if(block != block_)
    {
	block_ = block;

	if(block_)
	    OBSetBlock(fd_);
	else
	    OBSetNoBlock(fd_);
    }
}

// ----------------------------------------------------------------------
// OCI_IIOP_Transport_impl constructor and destructor
// ----------------------------------------------------------------------

OCI_IIOP_Transport_impl::OCI_IIOP_Transport_impl(OCI_IIOP_Connector_impl* con,
						 OCI_Handle fd,
						 CORBA_Boolean block)
    : fd_(fd), shutdown_(CORBA_FALSE), block_(block),
      info_(new OCI_IIOP_TransportInfo_impl(this, con))
{
#ifdef OB_OCI_DEBUG
    cout << "OCI_IIOP_Transport_impl" << endl;
#endif
}

OCI_IIOP_Transport_impl::OCI_IIOP_Transport_impl(OCI_IIOP_Acceptor_impl* acc,
						 OCI_Handle fd,
						 CORBA_Boolean block)
    : fd_(fd), shutdown_(CORBA_FALSE), block_(block),
      info_(new OCI_IIOP_TransportInfo_impl(this, acc))
{
#ifdef OB_OCI_DEBUG
    cout << "OCI_IIOP_Transport_impl" << endl;
#endif
}

OCI_IIOP_Transport_impl::~OCI_IIOP_Transport_impl()
{
#ifdef OB_OCI_DEBUG
    cout << "~OCI_IIOP_Transport_impl" << endl;
#endif

    if(fd_ >= 0)
	close(CORBA_TRUE);

    CORBA_release(info_);
}

// ----------------------------------------------------------------------
// OCI_IIOP_Transport_impl public member implementation
// ----------------------------------------------------------------------

void
OCI_IIOP_Transport_impl::close(CORBA_Boolean discard)
{
    if(fd_ == -1) // unblock_threads() may call close()
	return;

#ifndef WIN32
    //
    // With many BSD-based TCP/IP implementations, when the server
    // completely closes the socket, the TCP connection gets reset,
    // and all messages buffered in the sockets internal send buffer
    // are discarded.
    //
    // This is bad for protocols such as GIOP, since the
    // GIOP::CloseConnection message, that is sent right before
    // connection closure, is discarded.
    //
    // A workaround is to use the shutdown system call to only close
    // the send side of the socket, which will cause the other side of
    // the connection to receive an EOF when receiving from the
    // socket, and close down its end. In the meanwhile, this side of
    // the connection should just receive until it gets an EOF.
    //
    // A disadvantage to this workaround is that a connection cannot
    // be closed immediately anymore.
    //

    if(!discard)
    {
	//
	// Shut down send part of the socket
	//
	if(!shutdown_)
	{
	    ::shutdown(fd_, 1); // Shutdown send side only
	    shutdown_ = CORBA_TRUE;
	}
	
	//
	// Receive dummy data, blocking, until no data can be received anymore
	//
	setBlock(true);
	int result;
	do
	{
	    CORBA_Octet dummy[256];
	    result = ::recv(fd_, (char*)dummy, 256, 0);
	}
	while(result > 0);
    }
#endif

#ifdef OB_TRACE
    {
	OCI_AcceptorInfo_var accInfo = info_ -> acceptor_info();
	CORBA_String_var msg;
	if(!CORBA_is_nil(accInfo))
	    msg = CORBA_string_dup("Closing connection from ");
	else
	    msg = CORBA_string_dup("Closing connection to ");
        OCI_IIOP_InetAddr_var addr = info_ -> remote_addr();
        msg += (int)addr[0];
        msg += ".";
        msg += (int)addr[1];
        msg += ".";
        msg += (int)addr[2];
        msg += ".";
        msg += (int)addr[3];
        msg += ":";
        msg += info_ -> remote_port();
        OBMessageViewer::instance() -> trace(1, msg);
    }
#endif
    
    //
    // Call callbacks
    //
    info_ -> _OB_callCloseCB(info_);

    //
    // Destroy the info object
    //
    info_ -> _OB_destroy();

    //
    // Close the socket
    //
    int saveFd = fd_;
    fd_ = -1; // Must be set to -1 before the close
    OBCloseSocket(saveFd);
    block_ = CORBA_TRUE;
}

void
#ifdef WIN32
OCI_IIOP_Transport_impl::unblock_threads(CORBA_Boolean discard)
#else
OCI_IIOP_Transport_impl::unblock_threads(CORBA_Boolean)
#endif
{
    //
    // Shutdown the socket
    //
    assert(!shutdown_); // unblock_threads() must be called before close(),
                        // and may also not be called twice
    ::shutdown(fd_, 2); // Shutdown send and receive side to unblock threads
    shutdown_ = CORBA_TRUE;

    //

    // Under WIN32 we have to close the socket to unblock any pending
    // receives.
    //
#ifdef WIN32
    close(discard);
#endif
}

void
OCI_IIOP_Transport_impl::receive(OCI_Buffer_ptr buf, CORBA_Boolean block)
{
    setBlock(block);

    while(!buf -> is_full())
    {
	int result = ::recv(fd_, (char*)buf -> rest(),
			    buf -> rest_length(), 0);

	if(result == 0)
	{
	    throw CORBA_COMM_FAILURE("", OBMinorRecvZero, CORBA_COMPLETED_NO);
	}
	else if(result == -1)
	{
            if(OBIsInterrupted())
                continue;
            
	    if(!block && OBWouldBlock())
		return;

	    throw CORBA_COMM_FAILURE(OBLastError(), OBMinorRecv,
				     CORBA_COMPLETED_NO);
	}

	buf -> advance(result);
    }
}

CORBA_Boolean
OCI_IIOP_Transport_impl::receive_detect(OCI_Buffer_ptr buf,
					CORBA_Boolean block)
{
    setBlock(block);

    while(!buf -> is_full())
    {
        int result = ::recv(fd_, (char*)buf -> rest(),
			    buf -> rest_length(), 0);

	if(result == 0)
	{
	    return CORBA_FALSE;
	}
	else if(result == -1)
	{
            if(OBIsInterrupted())
                continue;
            
	    if(OBConnectionDown())
		return CORBA_FALSE;

	    if(!block && OBWouldBlock())
		return CORBA_TRUE;

	    throw CORBA_COMM_FAILURE(OBLastError(), OBMinorRecv,
				     CORBA_COMPLETED_NO);
	}

	buf -> advance(result);
    }

    return CORBA_TRUE;
}

void
OCI_IIOP_Transport_impl::receive_timeout(OCI_Buffer_ptr buf, CORBA_ULong t)
{
    if(t == 0)
    {
	receive(buf, CORBA_FALSE);
	return;
    }

    setBlock(CORBA_FALSE);

    while(!buf -> is_full())
    {
	fd_set fdSet;
	FD_ZERO(&fdSet);
	FD_SET(fd_, &fdSet);
	
	struct timeval tv;
	tv.tv_sec = t / 1000;
	tv.tv_usec = (t - tv.tv_sec * 1000) * 1000;

	int resultS = ::select(fd_ + 1, &fdSet, 0, 0, &tv);
	
	if(resultS == 0)
	{
	    return;
	}
	else if(resultS == -1)
	{
            if(OBIsInterrupted())
                continue;
            
	    throw CORBA_COMM_FAILURE(OBLastError(), OBMinorSelect,
				     CORBA_COMPLETED_NO);
	}

        int result = ::recv(fd_, (char*)buf -> rest(),
			    buf -> rest_length(), 0);

        if(result == 0)
        {
            throw CORBA_COMM_FAILURE("", OBMinorRecvZero,
                                     CORBA_COMPLETED_NO);
        }
        else if(result == -1)
        {
            if(OBIsInterrupted())
                continue;
            
            if(OBWouldBlock())
                return;

            throw CORBA_COMM_FAILURE(OBLastError(), OBMinorRecv,
                                     CORBA_COMPLETED_NO);
        }

        buf -> advance(result);
    }
}

void
OCI_IIOP_Transport_impl::send(OCI_Buffer_ptr buf, CORBA_Boolean block)
{
    setBlock(block);

    while(!buf -> is_full())
    {
	int result = ::send(fd_, (const char*)buf -> rest(),
			    buf -> rest_length(), 0);

	if(result == 0)
	{
	    throw CORBA_COMM_FAILURE("", OBMinorSendZero, CORBA_COMPLETED_NO);
	}
	else if(result == -1)
	{
            if(OBIsInterrupted())
                continue;
            
	    if(!block && OBWouldBlock())
		return;

	    throw CORBA_COMM_FAILURE(OBLastError(), OBMinorSend,
				     CORBA_COMPLETED_NO);
	}

	buf -> advance(result);
    }
}

CORBA_Boolean
OCI_IIOP_Transport_impl::send_detect(OCI_Buffer_ptr buf, CORBA_Boolean block)
{
    setBlock(block);

    while(!buf -> is_full())
    {
	int result = ::send(fd_, (const char*)buf -> rest(),
			    buf -> rest_length(), 0);

	if(result == 0)
	{
	    return CORBA_FALSE;
	}
	else if(result == -1)
	{
            if(OBIsInterrupted())
                continue;
            
	    if(OBConnectionDown())
		return CORBA_FALSE;

	    if(!block && OBWouldBlock())
		return CORBA_TRUE;

	    throw CORBA_COMM_FAILURE(OBLastError(), OBMinorSend,
				     CORBA_COMPLETED_NO);
	}

	buf -> advance(result);
    }

    return CORBA_TRUE;
}

void
OCI_IIOP_Transport_impl::send_timeout(OCI_Buffer_ptr buf, CORBA_ULong t)
{
    if(t == 0)
    {
	send(buf, CORBA_FALSE);
	return;
    }

    setBlock(CORBA_FALSE);
    
    while(!buf -> is_full())
    {
	fd_set fdSet;
	FD_ZERO(&fdSet);
	FD_SET(fd_, &fdSet);
	
	struct timeval tv;
	tv.tv_sec = t / 1000;
	tv.tv_usec = (t - tv.tv_sec * 1000) * 1000;

	int resultS = ::select(fd_ + 1, 0, &fdSet, 0, &tv);
	
	if(resultS == 0)
	{
	    return;
	}
	else if(resultS == -1)
	{
            if(OBIsInterrupted())
                continue;

	    throw CORBA_COMM_FAILURE(OBLastError(), OBMinorSelect,
				     CORBA_COMPLETED_NO);
	}

        int result = ::send(fd_, (const char*)buf -> rest(),
                            buf -> rest_length(), 0);

        if(result == 0)
        {
            throw CORBA_COMM_FAILURE("", OBMinorSendZero,
                                     CORBA_COMPLETED_NO);
        }
        else if(result == -1)
        {
            if(OBIsInterrupted())
                continue;
            
            if(OBWouldBlock())
                return;

            throw CORBA_COMM_FAILURE(OBLastError(), OBMinorSend,
                                     CORBA_COMPLETED_NO);
        }

        buf -> advance(result);
    }
}

OCI_TransportInfo_ptr
OCI_IIOP_Transport_impl::get_info()
{
    return OCI_TransportInfo::_duplicate(info_);
}

// ----------------------------------------------------------------------
// OCI_IIOP_Connector_impl private and protected member implementations
// ----------------------------------------------------------------------

void
OCI_IIOP_Connector_impl::close()
{
    //
    // Destroy the info object
    //
    info_ -> _OB_destroy();

    //
    // Close the socket
    //
    int saveFd = fd_;
    fd_ = -1; // Must be set to -1 before the close
    OBCloseSocket(saveFd);
}

// ----------------------------------------------------------------------
// OCI_IIOP_Connector_impl constructor and destructor
// ----------------------------------------------------------------------

OCI_IIOP_Connector_impl::OCI_IIOP_Connector_impl(const char* host,
						 CORBA_UShort port,
                                                 const OCI_ConnectCBSeq& cb)
    : host_(host), port_(port), fd_(-1),
      info_(new OCI_IIOP_ConnectorInfo_impl(this, cb))
{
#ifdef OB_OCI_DEBUG
    cout << "OCI_IIOP_Connector_impl" << endl;
#endif

    //
    // Get address
    //
    memset(&addr_, 0, sizeof(addr_));
    addr_.sin_family = AF_INET;
    addr_.sin_port = htons(port_);
    try
    {
        OBGetInAddr(host_, addr_);
    }
    catch(const CORBA_SystemException&)
    {
        _OB_setRef(0);
        throw;
    }
}

OCI_IIOP_Connector_impl::~OCI_IIOP_Connector_impl()
{
#ifdef OB_OCI_DEBUG
    cout << "~OCI_IIOP_Connector_impl" << endl;
#endif

    if(fd_ >= 0)
	close();

    CORBA_release(info_);
}

// ----------------------------------------------------------------------
// OCI_IIOP_Connector_impl public member implementation
// ----------------------------------------------------------------------

OCI_Transport_ptr
OCI_IIOP_Connector_impl::connect()
{
    if(fd_ >= 0)
	close();

    //
    // Create socket
    //
    if((fd_ = OBCreateSocket()) == -1)
    {
	fd_ = -1;
	throw CORBA_COMM_FAILURE(OBLastError(), OBMinorSocket,
				 CORBA_COMPLETED_NO);
    }

    //
    // Set TCP_NODELAY option
    //
    int no = 1;
    if(setsockopt(fd_, IPPROTO_TCP, TCP_NODELAY, (char*)&no, sizeof(int))
       == -1)
    {
	int err = OBGetError();
	OBCloseSocket(fd_);
	fd_ = -1;
	OBSetError(err);
	throw CORBA_COMM_FAILURE(OBLastError(), OBMinorSetsockopt,
				 CORBA_COMPLETED_NO);
    }
   
    //
    // Connect
    //
#ifdef OB_TRACE
    {
        CORBA_String_var msg = CORBA_string_dup("New connection to ");
        msg += inet_ntoa(addr_.sin_addr);
        msg += ":";
        msg += ntohs(addr_.sin_port);
        OBMessageViewer::instance() -> trace(1, msg);
    }
#endif
    while(true)
    {
        if(::connect(fd_, (struct sockaddr*)&addr_, sizeof(addr_)) == -1)
        {
            if(OBIsInterrupted())
                continue;
	    
	    int err = OBGetError();
	    OBCloseSocket(fd_);
	    fd_ = -1;
	    OBSetError(err);
	    throw CORBA_COMM_FAILURE(OBLastError(), OBMinorConnect,
				     CORBA_COMPLETED_NO);
        }

        break;
    }
    
    //
    // Create new transport (the last argument CORBA_TRUE means blocking)
    //
    OCI_Transport_var tr = new OCI_IIOP_Transport_impl(this, fd_, CORBA_TRUE);
    fd_ = -1;

    //
    // Call callbacks
    //
    OCI_TransportInfo_var trInfo = tr -> get_info();
    try
    {
	info_ -> _OB_callConnectCB(trInfo);
    }
    catch(const CORBA_SystemException&)
    {
	tr -> close(CORBA_TRUE);
	throw;
    }

    //
    // Return new transport
    //
    return tr._retn();
}

OCI_Transport_ptr
OCI_IIOP_Connector_impl::connect_timeout(CORBA_ULong t)
{
    if(fd_ >= 0)
	close();

    //
    // Create socket
    //
    if((fd_ = OBCreateSocket()) == -1)
    {
	fd_ = -1;
	throw CORBA_COMM_FAILURE(OBLastError(), OBMinorSocket,
				 CORBA_COMPLETED_NO);
    }

    //
    // Set TCP_NODELAY option
    //
    int no = 1;
    if(setsockopt(fd_, IPPROTO_TCP, TCP_NODELAY, (char*)&no, sizeof(int))
       == -1)
    {
	int err = OBGetError();
	OBCloseSocket(fd_);
	fd_ = -1;
	OBSetError(err);
	throw CORBA_COMM_FAILURE(OBLastError(), OBMinorSetsockopt,
				 CORBA_COMPLETED_NO);
    }

    //
    // Set socket to non-blocking mode
    //
    OBSetNoBlock(fd_);
   
    //
    // Connect
    //
#ifdef OB_TRACE
    {
        CORBA_String_var msg = CORBA_string_dup("New connection to ");
        msg += inet_ntoa(addr_.sin_addr);
        msg += ":";
        msg += ntohs(addr_.sin_port);
        OBMessageViewer::instance() -> trace(1, msg);
    }
#endif
    while(true)
    {
        if(::connect(fd_, (struct sockaddr*)&addr_, sizeof(addr_)) == -1)
        {
            if(OBIsInterrupted())
                continue;

	    if(OBInProgress())
	    {
		//
		// Call select() until timeout expired or connection
		// fd_ becomes writeable.
		//
		while(true)
		{
		    
		    struct timeval tv;
		    tv.tv_sec = t / 1000;
		    tv.tv_usec = (t - tv.tv_sec * 1000) * 1000;

		    fd_set writeFdSet;
		    FD_ZERO(&writeFdSet);
		    FD_SET(fd_, &writeFdSet);

#ifndef WIN32
		    int resultS = ::select(fd_ + 1, 0, &writeFdSet, 0, &tv);
#else
		    //
		    // Under WIN32 ::connect failure notification is
		    // done through the exceptfds.
		    //
		    fd_set exceptFdSet;
		    FD_ZERO(&exceptFdSet);
		    FD_SET(fd_, &exceptFdSet);

		    int resultS = ::select(fd_ + 1, 0, &writeFdSet,
					   &exceptFdSet, &tv);
#endif
		    
		    if(resultS == 0)
		    {
			OBCloseSocket(fd_);
			fd_ = -1;
			return OCI_Transport::_nil();
		    }
		    else if(resultS == -1)
		    {
			if(OBIsInterrupted())
			    continue;
			
			throw CORBA_COMM_FAILURE(OBLastError(), OBMinorSelect,
						 CORBA_COMPLETED_NO);
		    }
		    
#ifdef WIN32
		    //
		    // Windows seems to need this call to Sleep(),
		    // otherwise no error is reported through
		    // getsockopt. I have no idea why.
		    //
		    Sleep(0);
#endif

		    //
		    // Check for connect error
		    //
#ifdef HAVE_GETSOCKOPT_WITH_SIZE_T
		    size_t optlen = sizeof(int);
#else
		    int optlen = sizeof(int);
#endif
		    int optval;
		    if(getsockopt(fd_, SOL_SOCKET, SO_ERROR, (char*)&optval,
				  &optlen) == -1)
		    {
			int err = OBGetError();
			OBCloseSocket(fd_);
			fd_ = -1;
			OBSetError(err);
			throw CORBA_COMM_FAILURE(OBLastError(),
						 OBMinorGetsockopt,
						 CORBA_COMPLETED_NO);
		    }
		    
		    if(optval > 0)
		    {
			OBCloseSocket(fd_);
			fd_ = -1;
			OBSetError(optval);
			throw CORBA_COMM_FAILURE(OBLastError(), OBMinorConnect,
						 CORBA_COMPLETED_NO);
		    }
		    
		    break;
		}
	    }
	    else
	    {
		int err = OBGetError();
		OBCloseSocket(fd_);
		fd_ = -1;
		OBSetError(err);
		throw CORBA_COMM_FAILURE(OBLastError(), OBMinorConnect,
					 CORBA_COMPLETED_NO);
	    }
        }
	
        break;
    }
    
    //
    // Create new transport (the last argument CORBA_FALSE means non-blocking)
    //
    OCI_Transport_var tr = new OCI_IIOP_Transport_impl(this, fd_, CORBA_FALSE);
    fd_ = -1;

    //
    // Call callbacks
    //
    OCI_TransportInfo_var trInfo = tr -> get_info();
    try
    {
	info_ -> _OB_callConnectCB(trInfo);
    }
    catch(const CORBA_SystemException&)
    {
	tr -> close(CORBA_TRUE);
	throw;
    }

    //
    // Return new transport
    //
    return tr._retn();
}

OCI_ObjectKey*
OCI_IIOP_Connector_impl::is_usable(const OCI_IOR& ior)
{
    return OBIIOPExtractKey(ior, host_, port_, false);
}

OCI_ObjectKey*
OCI_IIOP_Connector_impl::is_usable_with_policies(const OCI_IOR& ior,
						 const CORBA_PolicyList&
						 policies)
{
    //
    // Make sure that the set of policies is met
    //
    for(CORBA_ULong i = 0; i < policies.length(); i++)
    {
        if(policies[i] -> policy_type() == SSL_CONNECT_POLICY)
        {
            SSL_ConnectPolicy_var connectPolicy =
                SSL_ConnectPolicy::_narrow(policies[i]);

	    assert(!CORBA_is_nil(connectPolicy));

            if(connectPolicy -> value() == SSL_ConnectSecure)
                return new OCI_ObjectKey();
        }
        else if(policies[i] -> policy_type() == OB_PROTOCOL_POLICY)
        {
            OB_ProtocolPolicy_var protocolPolicy =
                OB_ProtocolPolicy::_narrow(policies[i]);
            assert(!CORBA_is_nil(protocolPolicy));
            if(protocolPolicy -> value() != tag())
                return new OCI_ObjectKey();
        }
    }

    //
    // Make sure that the IOR is usable
    //
    return is_usable(ior);

}

OCI_ConnectorInfo_ptr
OCI_IIOP_Connector_impl::get_info()
{
    return OCI_ConnectorInfo::_duplicate(info_);
}

// ----------------------------------------------------------------------
// OCI_IIOP_Acceptor_impl constructor and destructor
// ----------------------------------------------------------------------

OCI_IIOP_Acceptor_impl::OCI_IIOP_Acceptor_impl(const char* host,
					       CORBA_UShort port)
    : host_(host), port_(port), fd_(-1),
      info_(new OCI_IIOP_AcceptorInfo_impl(this))
{
#ifdef OB_OCI_DEBUG
    cout << "OCI_IIOP_Acceptor_impl" << endl;
#endif

    int no;

    //
    // Get address
    //
    memset(&addr_, 0, sizeof(addr_));
    addr_.sin_family = AF_INET;
    addr_.sin_addr.s_addr = htonl(INADDR_ANY);

    //
    // Create socket
    //
    if((fd_ = OBCreateSocket()) == -1)
    {
	_OB_setRef(0);
	throw CORBA_COMM_FAILURE(OBLastError(), OBMinorSocket,
				 CORBA_COMPLETED_NO);
    }

    //
    // Set TCP_NODELAY option
    //
    no = 1;
    if(setsockopt(fd_, IPPROTO_TCP, TCP_NODELAY, (char*)&no, sizeof(int))
       == -1)
    {
	int err = OBGetError();
	OBCloseSocket(fd_);
	fd_ = -1;
	_OB_setRef(0);
	OBSetError(err);
	throw CORBA_COMM_FAILURE(OBLastError(), OBMinorSetsockopt,
				 CORBA_COMPLETED_NO);
    }

    //
    // Bind
    //
    no = 1;
    if(setsockopt(fd_, SOL_SOCKET, SO_REUSEADDR, (char*)&no,
		  sizeof(int)) == -1)
    {
	int err = OBGetError();
	OBCloseSocket(fd_);
	fd_ = -1;
	_OB_setRef(0);
	OBSetError(err);
	throw CORBA_COMM_FAILURE(OBLastError(), OBMinorSetsockopt,
				 CORBA_COMPLETED_NO);
    }
	
    addr_.sin_port = htons(port_);

    if(bind(fd_, (struct sockaddr*)&addr_, sizeof(addr_)) == -1)
    {
	int err = OBGetError();
	OBCloseSocket(fd_);
	fd_ = -1;
	_OB_setRef(0);
	OBSetError(err);
	throw CORBA_COMM_FAILURE(OBLastError(), OBMinorBind,
				 CORBA_COMPLETED_NO);
    }

#ifdef HAVE_GETSOCKNAME_WITH_SIZE_T
    size_t len = sizeof(addr_);
#else
    int len = sizeof(addr_);
#endif
    int result = getsockname(fd_, (struct sockaddr*)&addr_, &len);
    assert(result != -1);

    port_ = ntohs(addr_.sin_port);

#ifdef OB_TRACE
    {
        CORBA_String_var msg =
            CORBA_string_dup("Accepting connections on port ");
        msg += ntohs(addr_.sin_port);
        OBMessageViewer::instance() -> trace(1, msg);
    }
#endif
}

OCI_IIOP_Acceptor_impl::~OCI_IIOP_Acceptor_impl()
{
#ifdef OB_OCI_DEBUG
    cout << "~OCI_IIOP_Acceptor_impl" << endl;
#endif

    if(fd_ >= 0)
	close();

    CORBA_release(info_);
}

// ----------------------------------------------------------------------
// OCI_IIOP_Acceptor_impl public member implementation
// ----------------------------------------------------------------------

void
OCI_IIOP_Acceptor_impl::close()
{
    //
    // Destroy the info object
    //
    info_ -> _OB_destroy();

    //
    // Close the socket
    //
    int saveFd = fd_;
    fd_ = -1;
    OBCloseSocket(saveFd);
}

void
OCI_IIOP_Acceptor_impl::unblock_threads()
{
    //
    // Create socket for interrupt
    //
    int intFd;
    if((intFd = OBCreateSocket()) == -1)
    {
	intFd = -1;
	throw CORBA_COMM_FAILURE(OBLastError(), OBMinorSocket,
				 CORBA_COMPLETED_NO);
    }

    //
    // Connect to the acceptor socket
    //
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = addr_.sin_family;
    addr.sin_port = addr_.sin_port;
    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    while(true)
    {
        if(::connect(intFd, (struct sockaddr*)&addr, sizeof(addr)) == -1)
        {
            if(OBIsInterrupted())
                continue;
	//
	// Ignore connect failures - it's possible that accept has not
	// been called
	//
/*
	int err = OBGetError();
	OBCloseSocket(intFd);
	intFd = -1;
	OBSetError(err);
	throw CORBA_COMM_FAILURE(OBLastError(), OBMinorConnect,
				 CORBA_COMPLETED_NO);
*/
        }
        break;
    }

    //
    // Close the interrupt socket
    //
    OBCloseSocket(intFd);
}

void
OCI_IIOP_Acceptor_impl::listen()
{
    //
    // Listen
    //
    while(true)
    {
        if(::listen(fd_, 5) == -1)
        {
            if(OBIsInterrupted())
               continue;
               
            int err = OBGetError();
            OBCloseSocket(fd_);
            fd_ = -1;
            OBSetError(err);
            throw CORBA_COMM_FAILURE(OBLastError(), OBMinorListen,
                                     CORBA_COMPLETED_NO);
        }
        break;
    }
}

OCI_Transport_ptr
OCI_IIOP_Acceptor_impl::accept()
{
    //
    // Accept
    //
    struct sockaddr_in newAddr;
#ifdef HAVE_ACCEPT_WITH_SIZE_T
    size_t len = sizeof(newAddr);
#else
    int len = sizeof(newAddr);
#endif
    int newFd;
    while(true)
    {
	if((newFd = ::accept(fd_, (struct sockaddr*)&newAddr, &len)) == -1)
        {
	    //
	    // Here we have special handling for this case, since
	    // accept can generate other loop-and-retry errors on some
	    // platforms.
	    //
            if(OBAcceptRetry())
                continue;
	    
            int err = OBGetError();
            OBCloseSocket(fd_);
            fd_ = -1;
            OBSetError(err);
            throw CORBA_COMM_FAILURE(OBLastError(), OBMinorAccept,
                                     CORBA_COMPLETED_NO);
        }
	
        break;
    }
	
#ifdef OB_TRACE
    {
        CORBA_String_var msg = CORBA_string_dup("New connection from ");
        msg += inet_ntoa(newAddr.sin_addr);
        msg += ":";
        msg += ntohs(newAddr.sin_port);
        OBMessageViewer::instance() -> trace(1, msg);
    }
#endif

    //
    // Set TCP_NODELAY option
    //
    int no = 1;
    if(setsockopt(newFd, IPPROTO_TCP, TCP_NODELAY, (char*)&no,
		  sizeof(int)) == -1)
    {
	int err = OBGetError();
	OBCloseSocket(fd_);
	OBCloseSocket(newFd);
	fd_ = -1;
	OBSetError(err);
	throw CORBA_COMM_FAILURE(OBLastError(), OBMinorSetsockopt,
				 CORBA_COMPLETED_NO);
    }
    
    //
    // Create new transport
    //
    OCI_Transport_var tr =
	new OCI_IIOP_Transport_impl(this, newFd, CORBA_TRUE);

    //
    // Call callbacks
    //
    OCI_TransportInfo_var trInfo = tr -> get_info();
    try
    {
	info_ -> _OB_callAcceptCB(trInfo);
    }
    catch(const CORBA_SystemException&)
    {
	tr -> close(CORBA_TRUE);
	throw;
    }

    //
    // Return new transport
    //
    return tr._retn();
}

void
OCI_IIOP_Acceptor_impl::add_profile(const OCI_ObjectKey& key, OCI_IOR& ior)
{
    assert(port_ > 0);

    IIOP_ProfileBody body;
    body.iiop_version.major = 1;
    body.iiop_version.minor = 0;
    body.host = host_;
    body.port = port_;
    body.object_key = key;

    CORBA_ULong len = ior.profiles.length() + 1;
    ior.profiles.length(len);
    ior.profiles[len - 1].tag = tag();
    CORBA_ULong count = 0;
    OBMarshalCount(OBEndian, count);
    OBMarshalCount(body, count);
    ior.profiles[len - 1].profile_data.length(count);
    CORBA_Octet* oct = ior.profiles[len - 1].profile_data.data();
#ifdef OB_CLEAR_MEM
    memset(oct, 0, count);
#endif
    OBMarshal(OBEndian, oct);
    OBMarshal(body, oct);
}

OCI_ObjectKey*
OCI_IIOP_Acceptor_impl::is_local(const OCI_IOR& ior)
{
    return OBIIOPExtractKey(ior, host_, port_, true);
}

OCI_AcceptorInfo_ptr
OCI_IIOP_Acceptor_impl::get_info()
{
    return OCI_AcceptorInfo::_duplicate(info_);
}

// ----------------------------------------------------------------------
// OCI_IIOP_ConFactory_impl constructor and destructor
// ----------------------------------------------------------------------

OCI_IIOP_ConFactory_impl::OCI_IIOP_ConFactory_impl()
    : info_(new OCI_IIOP_ConFactoryInfo_impl)
{
#ifdef OB_OCI_DEBUG
    cout << "OCI_IIOP_ConFactory_impl" << endl;
#endif
}

OCI_IIOP_ConFactory_impl::~OCI_IIOP_ConFactory_impl()
{
#ifdef OB_OCI_DEBUG
    cout << "~OCI_IIOP_ConFactory_impl" << endl;
#endif
    CORBA_release(info_);
}

// ----------------------------------------------------------------------
// OCI_IIOP_ConFactory_impl public member implementation
// ----------------------------------------------------------------------

OCI_Connector_ptr
OCI_IIOP_ConFactory_impl::create(const OCI_IOR& ior)
{
    for(CORBA_ULong i = 0 ; i < ior.profiles.length() ; i++)
    {
	if(ior.profiles[i].tag == tag())
	{
	    //
	    // Get the IIOP profile body
	    //
	    const CORBA_Octet* coct = ior.profiles[i].profile_data.data();
	    CORBA_Boolean endian;
	    OBUnmarshal(endian, coct, false);
	    IIOP_ProfileBody body;
	    OBUnmarshal(body, coct, endian != OBEndian);

	    //
	    // Create new connector
	    //
            OCI_ConnectCBSeq_var cbs = info_ -> _OB_getConnectCBSeq();
	    return new OCI_IIOP_Connector_impl(body.host, body.port, cbs);
	}
    }

    return OCI_Connector::_nil();
}

OCI_Connector_ptr
OCI_IIOP_ConFactory_impl::create_with_policies(
    const OCI_IOR& ior, const CORBA_PolicyList& policies)
{
    if(!consider_with_policies(ior, policies))
        return OCI_Connector::_nil();

    return create(ior);
}

CORBA_Boolean
OCI_IIOP_ConFactory_impl::consider_with_policies(const OCI_IOR& ior,
						 const CORBA_PolicyList&
						 policies)
{
    for(CORBA_ULong i = 0; i < ior.profiles.length(); ++i)
    {
        if(ior.profiles[i].tag == tag())
        {
            //
            // Make sure that the policies are met
            //
            for(CORBA_ULong j = 0; j < policies.length(); j++)
            {
                if(policies[j] -> policy_type() == SSL_CONNECT_POLICY)
                {
                    SSL_ConnectPolicy_var connectPolicy =
                        SSL_ConnectPolicy::_narrow(policies[j]);
		    
		    assert(!CORBA_is_nil(connectPolicy));
		    
                    if(connectPolicy -> value() == SSL_ConnectSecure)
                        return CORBA_FALSE;
                }
                else if(policies[j] -> policy_type() ==
                        OB_PROTOCOL_POLICY)
                {
                    OB_ProtocolPolicy_var protocolPolicy =
                        OB_ProtocolPolicy::_narrow(policies[j]);
                    assert(!CORBA_is_nil(protocolPolicy));
                    if(protocolPolicy -> value() != tag())
                        return CORBA_FALSE;
                }
            }

            return CORBA_TRUE;
        }
    }

    return CORBA_FALSE;
}

CORBA_Boolean
OCI_IIOP_ConFactory_impl::equivalent(const OCI_IOR& ior1, const OCI_IOR& ior2)
{
    return OBIIOPEquivalent(ior1, ior2);
}

CORBA_ULong
OCI_IIOP_ConFactory_impl::hash(const OCI_IOR& ior, CORBA_ULong maximum)
{
    return OBIIOPHash(ior, maximum);
}

OCI_ConFactoryInfo_ptr
OCI_IIOP_ConFactory_impl::get_info()
{
    return OCI_ConFactoryInfo::_duplicate(info_);
}

// ----------------------------------------------------------------------
// OCI_IIOP_TransportInfo_impl private and protected member implementation
// ----------------------------------------------------------------------

void
OCI_IIOP_TransportInfo_impl::init()
{
    //
    // I have to save the local and remote address and port, since I
    // get garbage if I calculate these values "on the fly" during the
    // close callback.
    //

#ifdef HAVE_GETSOCKNAME_WITH_SIZE_T
    size_t len = sizeof(struct sockaddr_in);
#else
    int len = sizeof(struct sockaddr_in);
#endif
    int result;

    //
    // Get the local address and port
    //
    result = getsockname(transport_ -> fd_, (struct sockaddr*)&addr_, &len);
    assert(result == 0);

    //
    // Get the remote address and port
    //
    result = getpeername(transport_ -> fd_, (struct sockaddr*)&remoteAddr_,
			 &len);
    assert(result == 0);
}

// ----------------------------------------------------------------------
// OCI_IIOP_TransportInfo_impl constructor and destructor
// ----------------------------------------------------------------------

OCI_IIOP_TransportInfo_impl::OCI_IIOP_TransportInfo_impl(
    OCI_IIOP_Transport_impl* transport, OCI_IIOP_Connector_impl* connector)
    : transport_(transport)
{
    connectorInfo_ = connector -> get_info();

    //
    // Initialize some data members
    //
    init();
}

OCI_IIOP_TransportInfo_impl::OCI_IIOP_TransportInfo_impl(
    OCI_IIOP_Transport_impl* transport, OCI_IIOP_Acceptor_impl* acceptor)
    : transport_(transport)
{
    acceptorInfo_ = acceptor -> get_info();

    //
    // Initialize some data members
    //
    init();
}

OCI_IIOP_TransportInfo_impl::~OCI_IIOP_TransportInfo_impl()
{
}

// ----------------------------------------------------------------------
// OCI_IIOP_TransportInfo_impl public member implementation
// ----------------------------------------------------------------------

OCI_ConnectorInfo_ptr
OCI_IIOP_TransportInfo_impl::connector_info()
{
    return OCI_ConnectorInfo::_duplicate(connectorInfo_);
}

OCI_AcceptorInfo_ptr
OCI_IIOP_TransportInfo_impl::acceptor_info()
{
    return OCI_AcceptorInfo::_duplicate(acceptorInfo_);
}

void
OCI_IIOP_TransportInfo_impl::add_close_cb(OCI_CloseCB_ptr cb)
{
    OB_SYNCHRONIZED

    for(CORBA_ULong i = 0 ; i < closeCBSeq_.length() ; i++)
	if(closeCBSeq_[i] -> _is_equivalent(cb))
	    return; // Already registered

    closeCBSeq_.append(OCI_CloseCB::_duplicate(cb)); // Add callback object
}

void
OCI_IIOP_TransportInfo_impl::remove_close_cb(OCI_CloseCB_ptr cb)
{
    OB_SYNCHRONIZED

    for(CORBA_ULong i = 0 ; i < closeCBSeq_.length() ; i++)
	if(closeCBSeq_[i] -> _is_equivalent(cb))
	{
	    closeCBSeq_.remove(i); // Remove callback object
	    i--;
	}
}

OCI_IIOP_InetAddr_slice*
OCI_IIOP_TransportInfo_impl::addr()
{
    OB_SYNCHRONIZED

    if(transport_ == 0)
	throw CORBA_NO_RESOURCES();

    OCI_IIOP_InetAddr_slice* addr = new OCI_IIOP_InetAddr;
    memcpy(addr, (const char*)&addr_.sin_addr.s_addr, 4);
    return addr;
}

CORBA_UShort
OCI_IIOP_TransportInfo_impl::port()
{
    OB_SYNCHRONIZED

    if(transport_ == 0)
	throw CORBA_NO_RESOURCES();

    return ntohs(addr_.sin_port);
}

OCI_IIOP_InetAddr_slice*
OCI_IIOP_TransportInfo_impl::remote_addr()
{
    OB_SYNCHRONIZED

    if(transport_ == 0)
	throw CORBA_NO_RESOURCES();

    OCI_IIOP_InetAddr_slice* addr = new OCI_IIOP_InetAddr;
    memcpy(addr, (const char*)&remoteAddr_.sin_addr.s_addr, 4);
    return addr;
}

CORBA_UShort
OCI_IIOP_TransportInfo_impl::remote_port()
{
    OB_SYNCHRONIZED

    if(transport_ == 0)
	throw CORBA_NO_RESOURCES();

    return ntohs(remoteAddr_.sin_port);
}

void
OCI_IIOP_TransportInfo_impl::_OB_callCloseCB(OCI_TransportInfo_ptr info)
{
    OB_SYNCHRONIZED

    for(CORBA_ULong i = 0 ; i < closeCBSeq_.length() ; i++)
	closeCBSeq_[i] -> close_cb(info);
}

void
OCI_IIOP_TransportInfo_impl::_OB_destroy()
{
    OB_SYNCHRONIZED

    transport_ = 0;
}

// ----------------------------------------------------------------------
// OCI_IIOP_ConnectorInfo_impl public member implementation
// ----------------------------------------------------------------------

void
OCI_IIOP_ConnectorInfo_impl::add_connect_cb(OCI_ConnectCB_ptr cb)
{
    OB_SYNCHRONIZED

    for(CORBA_ULong i = 0 ; i < connectCBSeq_.length() ; i++)
	if(connectCBSeq_[i] -> _is_equivalent(cb))
	    return; // Already registered

    connectCBSeq_.append(OCI_ConnectCB::_duplicate(cb)); // Add callback object
}

void
OCI_IIOP_ConnectorInfo_impl::remove_connect_cb(OCI_ConnectCB_ptr cb)
{
    OB_SYNCHRONIZED

    for(CORBA_ULong i = 0 ; i < connectCBSeq_.length() ; i++)
	if(connectCBSeq_[i] -> _is_equivalent(cb))
	{
	    connectCBSeq_.remove(i); // Remove callback object
	    i--;
	}
}

OCI_IIOP_InetAddr_slice*
OCI_IIOP_ConnectorInfo_impl::remote_addr()
{
    OB_SYNCHRONIZED

    if(connector_ == 0)
	throw CORBA_NO_RESOURCES();

    OCI_IIOP_InetAddr_var rc = new OCI_IIOP_InetAddr;
    memcpy(rc.inout(), (const char*)&connector_ -> addr_.sin_addr.s_addr, 4);

    return rc._retn();
}

CORBA_UShort
OCI_IIOP_ConnectorInfo_impl::remote_port()
{
    OB_SYNCHRONIZED

    if(connector_ == 0)
	throw CORBA_NO_RESOURCES();

    return ntohs(connector_ -> addr_.sin_port);
}

void
OCI_IIOP_ConnectorInfo_impl::_OB_callConnectCB(OCI_TransportInfo_ptr info)
{
    OB_SYNCHRONIZED

    for(CORBA_ULong i = 0 ; i < connectCBSeq_.length() ; i++)
	connectCBSeq_[i] -> connect_cb(info);
}

void
OCI_IIOP_ConnectorInfo_impl::_OB_destroy()
{
    OB_SYNCHRONIZED

    connector_ = 0;
}

// ----------------------------------------------------------------------
// OCI_IIOP_AcceptorInfo_impl public member implementation
// ----------------------------------------------------------------------

void
OCI_IIOP_AcceptorInfo_impl::add_accept_cb(OCI_AcceptCB_ptr cb)
{
    OB_SYNCHRONIZED

    for(CORBA_ULong i = 0 ; i < acceptCBSeq_.length() ; i++)
	if(acceptCBSeq_[i] -> _is_equivalent(cb))
	    return; // Already registered

    acceptCBSeq_.append(OCI_AcceptCB::_duplicate(cb)); // Add callback object
}

void
OCI_IIOP_AcceptorInfo_impl::remove_accept_cb(OCI_AcceptCB_ptr cb)
{
    OB_SYNCHRONIZED

    for(CORBA_ULong i = 0 ; i < acceptCBSeq_.length() ; i++)
	if(acceptCBSeq_[i] -> _is_equivalent(cb))
	{
	    acceptCBSeq_.remove(i); // Remove callback object
            return;
	}
}

char*
OCI_IIOP_AcceptorInfo_impl::host()
{
    OB_SYNCHRONIZED

    if(acceptor_ == 0)
	throw CORBA_NO_RESOURCES();

    return CORBA_string_dup(acceptor_ -> host_);
}

OCI_IIOP_InetAddr_slice*
OCI_IIOP_AcceptorInfo_impl::addr()
{
    OB_SYNCHRONIZED

    if(acceptor_ == 0)
	throw CORBA_NO_RESOURCES();

    struct sockaddr_in addr;
#ifdef HAVE_GETSOCKNAME_WITH_SIZE_T
    size_t len = sizeof(addr);
#else
    int len = sizeof(addr);
#endif
    int result = getsockname(acceptor_ -> fd_, (struct sockaddr*)&addr, &len);
    OCI_IIOP_InetAddr_var rc = new OCI_IIOP_InetAddr;
    if(result == 0)
        memcpy(rc.inout(), (const char*)&addr.sin_addr.s_addr, 4);

    return rc._retn();
}

CORBA_UShort
OCI_IIOP_AcceptorInfo_impl::port()
{
    OB_SYNCHRONIZED

    if(acceptor_ == 0)
	throw CORBA_NO_RESOURCES();

    struct sockaddr_in addr;
#ifdef HAVE_GETSOCKNAME_WITH_SIZE_T
    size_t len = sizeof(addr);
#else
    int len = sizeof(addr);
#endif
    int result = getsockname(acceptor_ -> fd_, (struct sockaddr*)&addr, &len);

    if(result == 0)
        return ntohs(addr.sin_port);
    return 0;
}

void
OCI_IIOP_AcceptorInfo_impl::_OB_callAcceptCB(OCI_TransportInfo_ptr info)
{
    OB_SYNCHRONIZED

    for(CORBA_ULong i = 0 ; i < acceptCBSeq_.length() ; i++)
	acceptCBSeq_[i] -> accept_cb(info);
}

void
OCI_IIOP_AcceptorInfo_impl::_OB_destroy()
{
    OB_SYNCHRONIZED

    acceptor_ = 0;
}

// ----------------------------------------------------------------------
// OCI_IIOP_ConFactoryInfo_impl public member implementation
// ----------------------------------------------------------------------

void
OCI_IIOP_ConFactoryInfo_impl::add_connect_cb(OCI_ConnectCB_ptr cb)
{
    OB_SYNCHRONIZED

    for(CORBA_ULong i = 0 ; i < connectCBSeq_.length() ; i++)
	if(connectCBSeq_[i] -> _is_equivalent(cb))
	    return; // Already registered

    connectCBSeq_.append(OCI_ConnectCB::_duplicate(cb)); // Add callback object
}

void
OCI_IIOP_ConFactoryInfo_impl::remove_connect_cb(OCI_ConnectCB_ptr cb)
{
    OB_SYNCHRONIZED

    for(CORBA_ULong i = 0 ; i < connectCBSeq_.length() ; i++)
	if(connectCBSeq_[i] -> _is_equivalent(cb))
	{
	    connectCBSeq_.remove(i); // Remove callback object
	    i--;
	}
}

OCI_ConnectCBSeq*
OCI_IIOP_ConFactoryInfo_impl::_OB_getConnectCBSeq()
{
    OB_SYNCHRONIZED

    return new OCI_ConnectCBSeq(connectCBSeq_);
}
