Logo Search packages:      
Sourcecode: kmess version File versions

directconnectionpool.cpp

/***************************************************************************
                          directconnectionpool.cpp -  description
                             -------------------
    begin                : Thu 1 5 2005
    copyright            : (C) 2005 by Diederik van der Boor
    email                : "vdboor" --at-- "codingdomain.com"
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program 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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "directconnectionpool.h"
#include "directconnectionbase.h"

#include "../../kmessdebug.h"

#ifdef KMESSDEBUG_DIRECTCONNECTION
  #define KMESSDEBUG_DIRECTCONNECTION_GENERAL
#endif


// The constructor
DirectConnectionPool::DirectConnectionPool()
  : QObject(0, "DirectConnectionPool")
  , activeConnection_(0)
{
  
}



// The destructor
DirectConnectionPool::~DirectConnectionPool()
{
#ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL
  kdDebug() << "DirectConnectionPool: Destructor: Deleting pending sockets and active connection." << endl;
#endif

  clearPending();

  // Remove the active connection if it was still active.
  if( activeConnection_ != 0 )
  {
    disconnect(activeConnection_, SIGNAL(connectionClosed()), this, SLOT(slotConnectionClosed()));
    disconnect(activeConnection_, SIGNAL(connectionFailed()), this, SLOT(slotConnectionFailed()));
    activeConnection_->deleteLater();
    activeConnection_ = 0;
  }

#ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL
  kdDebug() << "DESTROYED DirectConnectionPool" << endl;
#endif
}



/**
 * Add a connection to the list, tells the object to connect to the given ipaddress/port.
 * Returns true when the connection could be added to the pending list (e.g. it's openConnection() method didn't fail).
 */
00067 bool DirectConnectionPool::addConnection(DirectConnectionBase *connection, const QString &ipAddress, const int port)
{
  // Refuse if there is already an active connection
  if( activeConnection_ != 0 )
  {
    kdWarning() << "DirectConnectionPool: Refusing connection attempt, a connection has already been made." << endl;
    return 0;
  }

#ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL
  kdDebug() << "DirectConnectionPool: Adding new client connection." << endl;
#endif

  pendingConnections_.append( connection );  // append to pending list before signals are fired.

  // Connect it
  connect( connection, SIGNAL(     connectionEstablished() ) ,  // The connection was established
           this,         SLOT( slotConnectionEstablished() ) );
  connect( connection, SIGNAL(          connectionFailed() ) ,  // The connection could not be made
           this,         SLOT(      slotConnectionFailed() ) );
  connect( connection, SIGNAL(          connectionClosed() ) ,  // The connection was closed
           this,         SLOT(      slotConnectionClosed() ) );
  connect( connection, SIGNAL(      connectionAuthorized() ) ,  // The connection was authorized
           this,         SLOT(  slotConnectionAuthorized() ) );

  // Connect the the host
  bool opened = connection->openConnection( ipAddress, port, true );
  if( ! opened )
  {
    pendingConnections_.remove( connection );
    connection->deleteLater();

#ifdef KMESSDEBUG_P2PAPPLICATION_GENERAL
    kdDebug() << "DirectConnectionPool::addConnection: Could not connect to " << ipAddress << ":" << port << endl;
#endif
    return false;
  }

  return true;
}



/**
 * Add a connection to the list, starts listening for incoming connections.
 */
00113 int DirectConnectionPool::addServerConnection(DirectConnectionBase *connection)
{
  // Refuse if there is already an active connection
  if( activeConnection_ != 0 )
  {
    kdWarning() << "DirectConnectionPool: Refusing server connection attempt, a connection has already been made." << endl;
    return 0;
  }

#ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL
  kdDebug() << "DirectConnectionPool: Adding new server connection." << endl;
#endif

  pendingConnections_.append( connection );  // append to pending list before signals are fired.

  // Connect it
  connect( connection, SIGNAL(     connectionEstablished() ) ,  // The connection was established
           this,         SLOT( slotConnectionEstablished() ) );
  connect( connection, SIGNAL(          connectionFailed() ) ,  // The connection could not be made
           this,         SLOT(      slotConnectionFailed() ) );
  connect( connection, SIGNAL(          connectionClosed() ) ,  // The connection was closed
           this,         SLOT(      slotConnectionClosed() ) );
  connect( connection, SIGNAL(      connectionAuthorized() ) ,  // The connection was authorized
           this,         SLOT(  slotConnectionAuthorized() ) );

  bool listening = false;

  // Attempt to listen at one of the 10 ports we use.
  for(int i = 0; i < 10; i++)
  {
    // The openServerPort() automatically picks another port
    // if the previous call failed.
    listening = connection->openServerPort();

    if(listening)
    {
      break;
    }
  }

  if( ! listening )
  {
    pendingConnections_.remove( connection );
    connection->deleteLater();

#ifdef KMESSDEBUG_P2PAPPLICATION_GENERAL
    kdDebug() << "DirectConnectionPool::addServerConnection: Could not find a port to listen at." << endl;
#endif
    return 0;
  }

  // Return port we're listening at.
  return connection->getLocalServerPort();
}



/**
 * Remove all connections from the list.
 */
00173 void DirectConnectionPool::clearPending()
{
#ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL
  kdDebug() << "DirectConnectionPool: Removing pending sockets." << endl;
#endif

  // Tell all other pending connections to abort
  QPtrListIterator<DirectConnectionBase> it(pendingConnections_);
  while( it.current() != 0 )
  {
    DirectConnectionBase *pendingConnection = it.current();
    pendingConnection->deleteLater();  // delete should not be called from a slot.
    ++it;
  }

  // Clear the list
  pendingConnections_.clear();
}



/**
 * Return the active connection.
 */
00197 DirectConnectionBase * DirectConnectionPool::getActiveConnection() const
{
  return activeConnection_;
}



/**
 * Indicate whether the is a active connection or not.
 */
00207 bool DirectConnectionPool::hasActiveConnection() const
{
  return (activeConnection_ != 0);
}



/**
 * Indicate whether there are connections pending or not.
 */
00217 bool DirectConnectionPool::hasPendingConnections() const
{
  return (pendingConnections_.count() > 0);
}



/**
 * A direct connection was authorized
 */
00227 void DirectConnectionPool::slotConnectionAuthorized()
{
#ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL
  kdDebug() << "DirectConnectionPool: A direct connection was authorized." << endl;
#endif

  // Find out which object sent the signal
  const QObject *eventSender = sender();
  if(KMESS_NULL(eventSender)) return;
#ifdef KMESSTEST
  ASSERT( eventSender->inherits("DirectConnectionBase") );
#endif

  // Signal to ApplicationList
  DirectConnectionBase *connection = static_cast<DirectConnectionBase*>(const_cast<QObject*>(eventSender));
#ifdef KMESSTEST
  ASSERT( connection == activeConnection_ );
#endif
  emit activeConnectionAuthorized();
}



/**
 * A direct connection was closed
 */
00253 void DirectConnectionPool::slotConnectionClosed()
{
#ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL
  kdDebug() << "DirectConnectionPool: A direct connection was closed." << endl;
#endif

  // Find out which object sent the signal
  const QObject *eventSender = sender();
  if(KMESS_NULL(eventSender)) return;
#ifdef KMESSTEST
  ASSERT( eventSender->inherits("DirectConnectionBase") );
#endif

  // Remove the connection
  DirectConnectionBase *connection = static_cast<DirectConnectionBase*>(const_cast<QObject*>(eventSender));
  pendingConnections_.remove(connection);

  // Signal when the active connection closed
  if(connection == activeConnection_)
  {
#ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL
    kdDebug() << "DirectConnectionPool: Closed connection was the active connection!" << endl;
#endif

    emit activeConnectionClosed();
    activeConnection_ = 0;  // reset, will deleter later
  }
  else
  {
#ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL
    kdDebug() << "DirectConnectionPool: Closed connection was another pending socket from the pool." << endl;
#endif

    // Check whethere there is still a chance to get a direct connection
    if( activeConnection_ == 0 && pendingConnections_.isEmpty() )
    {
#ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL
      kdDebug() << "DirectConnectionPool: No remaining direct connections left." << endl;
#endif

      // Emit signal for fallback (e.g. sending files over the switchboard)
      emit allConnectionsFailed();
    }
  }

  // delete the connection
  connection->deleteLater();
}



/**
 * A direct connection is established.
 */
00307 void DirectConnectionPool::slotConnectionEstablished()
{
#ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL
  kdDebug() << "DirectConnectionPool: Direct connection established." << endl;
#endif

  // Test whether two connection attempts were established quickly after each other.
  if( activeConnection_ != 0 )
  {
#ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL
    kdDebug() << "DirectConnectionPool: Already got an active connection, removing other connections." << endl;
#endif
    clearPending();
    return;
  }

  // No connection yet, find the first successful connection.
  QPtrListIterator<DirectConnectionBase> it(pendingConnections_);
  while( it.current() != 0 )
  {
    DirectConnectionBase *pendingConnection = it.current();
    if( pendingConnection->isConnected() )
    {
      // Established connection found, remove from list.
      pendingConnections_.remove( pendingConnection );

      // Try to initialize the connection.
      bool success = pendingConnection->initialize();
      if( ! success )
      {
        // Not initialized (e.g. preamble cound not be sent in case of a MsnDirectConnection),
        // try the next item in the list instead.
#ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL
        kdDebug() << "DirectConnectionPool::slotConnectionEstablished() - could not initialize connection, "
                  << "trying next available connection." << endl;
#endif
        pendingConnection->deleteLater();
        pendingConnection = 0;
        continue;
      }
      else
      {
        // Initialized, this is the active connection!
        activeConnection_ = pendingConnection;
        break;
      }
    }

    ++it;
  }

  // If there is an initialized connection, remove all other pending connections.
  if( activeConnection_ != 0 )
  {
    if( pendingConnections_.count() > 0 )
    {
      clearPending();
    }

    // Signal the connection is established, allows to sent any handshake, etc..
    emit connectionEstablished();
  }
}



/**
 * A direct connection could not be made.
 */
00376 void DirectConnectionPool::slotConnectionFailed()
{
#ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL
  kdDebug() << "DirectConnectionPool: A direct connection failed." << endl;
#endif

  // Find out which object sent the signal
  const QObject *eventSender = sender();
  if(KMESS_NULL(eventSender)) return;
#ifdef KMESSTEST
  ASSERT( eventSender->inherits("DirectConnectionBase") );
#endif

  // Remove the connection
  DirectConnectionBase *connection = static_cast<DirectConnectionBase*>(const_cast<QObject*>(eventSender));
  pendingConnections_.remove(connection);

  // If there is still no connection made
  if( activeConnection_ == 0 )
  {
    // If there are no remaining pending connections
    if( pendingConnections_.count() == 0 )
    {
#ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL
      kdDebug() << "DirectConnectionPool: No remaining direct connections left." << endl;
#endif

      // Emit signal for fallback (e.g. sending files over the switchboard)
      emit allConnectionsFailed();
    }
    else
    {
#ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL
      kdDebug() << "DirectConnectionPool: There are " << pendingConnections_.count() << " remaining direct connections, waiting for one to succeed." << endl;
#endif
    }
  }

  // If the current connection failed
  if( connection == activeConnection_ )
  {
    // This really shouldn't happen
    kdWarning() << "DirectConnectionPool: The active connection failed!" << endl;

    // Reset current object
    activeConnection_ = 0;

    // Emit signal so some fallback can occur
    emit allConnectionsFailed();
  }


  // Delete the failed connection object.
  connection->deleteLater();
}



/**
 * Verify whether the currently active connection is still connected.
 * If the connection appears to be closed, it will be deleted.
 * @return Returns false when the connection was invalid and deleted, true otherwise.
 */
00439 bool DirectConnectionPool::verifyActiveConnection()
{
  if( activeConnection_ != 0
  && ! activeConnection_->isConnected() )
  {
#ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL
    kdDebug() << "DirectConnectionPool: Active connection is no longer connected, deleting connection." << endl;
#endif

    // Avoid calling slots.
    disconnect(activeConnection_, SIGNAL(connectionClosed()), this, SLOT(slotConnectionClosed()));
    disconnect(activeConnection_, SIGNAL(connectionFailed()), this, SLOT(slotConnectionFailed()));

    // Delete connection, might fire connectionClosed() signal.
    activeConnection_->deleteLater();
    activeConnection_ = 0;

    return false;
  }
  else
  {
    return true;
  }
}



#include "directconnectionpool.moc"

Generated by  Doxygen 1.6.0   Back to index