diff --git a/src/libtomahawk/CMakeLists.txt b/src/libtomahawk/CMakeLists.txt index f3f81a8b3..22f985fc4 100644 --- a/src/libtomahawk/CMakeLists.txt +++ b/src/libtomahawk/CMakeLists.txt @@ -307,6 +307,7 @@ list(APPEND libSources network/Connection.cpp network/ControlConnection.cpp network/QTcpSocketExtra.cpp + network/ConnectionManager.cpp playlist/PlaylistUpdaterInterface.cpp playlist/dynamic/DynamicPlaylist.cpp diff --git a/src/libtomahawk/network/ConnectionManager.cpp b/src/libtomahawk/network/ConnectionManager.cpp new file mode 100644 index 000000000..d8cce351b --- /dev/null +++ b/src/libtomahawk/network/ConnectionManager.cpp @@ -0,0 +1,261 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#include "ConnectionManager.h" +#include "ControlConnection.h" +#include "Servent.h" + +#include "database/Database.h" +#include "database/DatabaseImpl.h" +#include "sip/SipPlugin.h" +#include "utils/Logger.h" + +#include +#include + +ConnectionManager::ConnectionManager( const QString &nodeid ) + : m_nodeid( nodeid ) +{ + // TODO sth? +} + +void +ConnectionManager::handleSipInfo( const Tomahawk::peerinfo_ptr &peerInfo ) +{ + // Start handling in a separate thread so that we do not block the event loop. + QtConcurrent::run( this, &ConnectionManager::handleSipInfoPrivate, peerInfo ); +} + +void +ConnectionManager::handleSipInfoPrivate( const Tomahawk::peerinfo_ptr &peerInfo ) +{ + m_mutex.lock(); + // Respect different behaviour before 0.7.99 + if ( !peerInfo->versionString().isEmpty() && TomahawkUtils::compareVersionStrings( peerInfo->versionString(), "Tomahawk Player EmptyOS 0.7.99" ) < 0) + { + peerInfoDebug( peerInfo ) << Q_FUNC_INFO << "Using old-style (<0.7.99) connection order."; + SipInfo we = Servent::getSipInfoForOldVersions( Servent::instance()->getLocalSipInfos( QString(), QString() ) ); + SipInfo they = peerInfo->sipInfos().first(); + if ( they.isVisible() ) + { + if ( !we.isVisible() || we.host() < they.host() || (we.host() == they.host() && we.port() < they.port())) + { + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Initiate connection to" << peerInfo->id() << "at" << they.host() << "peer of:" << peerInfo->sipPlugin()->account()->accountFriendlyName(); + connectToPeer( peerInfo, false ); + return; + // We connected to the peer, so we are done here. + } + } + m_mutex.unlock(); + return; + } + foreach ( SipInfo info, peerInfo->sipInfos() ) + { + if (info.isVisible()) + { + // There is at least one SipInfo that may be visible. Try connecting. + // Duplicate Connections are checked by connectToPeer, so we do not need to take care of this + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Initiate connection to" << peerInfo->id() << "at" << info.host() << "peer of:" << peerInfo->sipPlugin()->account()->accountFriendlyName(); + connectToPeer( peerInfo, false ); + // We connected to the peer, so we are done here. + return; + } + } + m_mutex.unlock(); +} + +void +ConnectionManager::connectToPeer( const Tomahawk::peerinfo_ptr &peerInfo, bool lock ) +{ + // Lock, so that we will not attempt to do two parallell connects. + if (lock) + { + m_mutex.lock(); + } + // Check that we are not already connected to this peer + ControlConnection* cconn = Servent::instance()->lookupControlConnection( peerInfo->nodeId() ); + if ( cconn != NULL || !m_controlConnection.isNull() ) + { + // We are already connected to this peer, so just add some more details. + peerInfoDebug( peerInfo ) << "Existing connection found, not connecting."; + cconn->addPeerInfo( peerInfo ); + if ( cconn != NULL ) + { + m_controlConnection = QPointer(cconn); + } + m_mutex.unlock(); + return; + // TODO: Keep the peerInfo in mind for reconnecting + // FIXME: Do we need this for reconnecting if the connection drops? + } + + // If we are not connected, try to connect + m_currentPeerInfo = peerInfo; + peerInfoDebug( peerInfo ) << "No existing connection found, trying to connect."; + m_sipCandidates.append( peerInfo->sipInfos() ); + + QVariantMap m; + m["conntype"] = "accept-offer"; + m["key"] = peerInfo->key(); + m["nodeid"] = Database::instance()->impl()->dbid(); + + m_controlConnection = QPointer( new ControlConnection( Servent::instance() ) ); + m_controlConnection->addPeerInfo( peerInfo ); + m_controlConnection->setFirstMessage( m ); + + if ( peerInfo->id().length() ) + m_controlConnection->setName( peerInfo->contactId() ); + if ( peerInfo->nodeId().length() ) + m_controlConnection->setId( peerInfo->nodeId() ); + + m_controlConnection->setProperty( "nodeid", peerInfo->nodeId() ); + + Servent::instance()->registerControlConnection( m_controlConnection.data() ); + tryConnect(); +} + +void ConnectionManager::tryConnect() +{ + // ATTENTION: mutex should be already locked by the calling function. + Q_ASSERT( !m_controlConnection.isNull() ); + + if ( m_sipCandidates.isEmpty() ) + { + // No more possibilities to connect. + peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "No more possible SIP endpoints for " << m_controlConnection->name() << " skipping."; + + // Clean up. + m_currentPeerInfo.clear(); + delete m_controlConnection.data(); + m_mutex.unlock(); + return; + } + + // Use first available SIP endpoint and remove it from the list + SipInfo info = m_sipCandidates.takeFirst(); + if ( !info.isVisible() ) + { + peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Try next SipInfo, we can't connect to this one"; + tryConnect(); + return; + } + + peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Connecting to " << info.host() << ":" << info.port(); + Q_ASSERT( info.port() > 0 ); + + // Check that we are not connecting to ourselves + foreach( QHostAddress ha, Servent::instance()->addresses() ) + { + if ( info.host() == ha.toString() && info.port() == Servent::instance()->port() ) + { + peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Tomahawk won't try to connect to" << info.host() << ":" << info.port() << ": same ip:port as ourselves."; + tryConnect(); + return; + } + } + if ( info.host() == Servent::instance()->additionalAddress() && info.port() == Servent::instance()->additionalPort() ) + { + peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Tomahawk won't try to connect to" << info.host() << ":" << info.port() << ": same ip:port as ourselves."; + tryConnect(); + return; + } + + // We should have already setup a first message in connectToPeer + Q_ASSERT( !m_controlConnection->firstMessage().isNull() ); + + QTcpSocketExtra* sock = new QTcpSocketExtra(); + sock->setConnectTimeout( CONNECT_TIMEOUT ); + sock->_disowned = false; + sock->_conn = m_controlConnection.data(); + sock->_outbound = true; + + connect( sock, SIGNAL( connected() ), this, SLOT( socketConnected() ) ); + connect( sock, SIGNAL( error( QAbstractSocket::SocketError ) ), this, SLOT( socketError( QAbstractSocket::SocketError ) ) ); + + peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Connecting socket to " << info.host() << ":" << info.port(); + sock->connectToHost( info.host(), info.port(), QTcpSocket::ReadWrite ); + sock->moveToThread( thread() ); +} + +void +ConnectionManager::socketError( QAbstractSocket::SocketError error ) +{ + Q_UNUSED( error ); + Q_ASSERT( !m_controlConnection.isNull() ); + + QTcpSocketExtra* sock = (QTcpSocketExtra*)sender(); + peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Connecting to " << sock->peerAddress().toString() << " failed: " << sock->errorString(); + sock->deleteLater(); + + // Try to connect with the next available SipInfo. + tryConnect(); +} + + +void +ConnectionManager::socketConnected() +{ + QTcpSocketExtra* sock = (QTcpSocketExtra*)sender(); + + peerInfoDebug( m_currentPeerInfo ) << Q_FUNC_INFO << "Connected to hostaddr: " << sock->peerAddress() << ", hostname:" << sock->peerName(); + + Q_ASSERT( !sock->_conn.isNull() ); + + handoverSocket( sock ); + m_currentPeerInfo.clear(); + m_mutex.unlock(); +} + +void +ConnectionManager::handoverSocket( QTcpSocketExtra* sock ) +{ + Q_ASSERT( !m_controlConnection.isNull() ); + Q_ASSERT( sock ); + Q_ASSERT( m_controlConnection->socket().isNull() ); + Q_ASSERT( sock->isValid() ); + + disconnect( sock, SIGNAL( readyRead() ), this, SLOT( readyRead() ) ); + disconnect( sock, SIGNAL( disconnected() ), sock, SLOT( deleteLater() ) ); + disconnect( sock, SIGNAL( error( QAbstractSocket::SocketError ) ), this, SLOT( socketError( QAbstractSocket::SocketError ) ) ); + + sock->_disowned = true; + m_controlConnection->setOutbound( sock->_outbound ); + m_controlConnection->setPeerPort( sock->peerPort() ); + + m_controlConnection->start( sock ); + m_currentPeerInfo.clear(); + m_mutex.unlock(); +} + + +static QMutex* nodeMapMutex = new QMutex(); +static QMap< QString, QSharedPointer< ConnectionManager > > connectionManagers; + +QSharedPointer +ConnectionManager::getManagerForNodeId( const QString &nodeid ) +{ + QMutexLocker locker( nodeMapMutex ); + if ( connectionManagers.contains( nodeid ) && !connectionManagers.value( nodeid ).isNull() ) { + return connectionManagers.value( nodeid ); + } + + // There exists no connection for this nodeid + QSharedPointer< ConnectionManager > manager( new ConnectionManager( nodeid ) ); + connectionManagers[nodeid] = manager; + return manager; +} diff --git a/src/libtomahawk/network/ConnectionManager.h b/src/libtomahawk/network/ConnectionManager.h new file mode 100644 index 000000000..4f405ebbf --- /dev/null +++ b/src/libtomahawk/network/ConnectionManager.h @@ -0,0 +1,68 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Uwe L. Korn + * + * Tomahawk 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. + * + * Tomahawk 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 Tomahawk. If not, see . + */ + +#ifndef CONNECTIONMANAGER_H +#define CONNECTIONMANAGER_H + +#include "DllMacro.h" + +#include "sip/PeerInfo.h" + +#include +#include +#include + +class QTcpSocketExtra; + +class DLLEXPORT ConnectionManager : public QObject +{ + Q_OBJECT + +public: + static QSharedPointer getManagerForNodeId( const QString& nodeid ); + ConnectionManager( const QString& nodeid ); + + void handleSipInfo( const Tomahawk::peerinfo_ptr& peerInfo ); + +private slots: + void socketConnected(); + void socketError( QAbstractSocket::SocketError error ); + +private: + void connectToPeer(const Tomahawk::peerinfo_ptr& peerInfo , bool lock); + void handleSipInfoPrivate( const Tomahawk::peerinfo_ptr& peerInfo ); + + /** + * Transfers ownership of socket to the ControlConnection and inits the ControlConnection + */ + void handoverSocket( QTcpSocketExtra* sock ); + + /** + * Attempt to connect to the peer using the current stored information. + */ + void tryConnect(); + + // We just keep this for debug purposes and only during connection attempts. + Tomahawk::peerinfo_ptr m_currentPeerInfo; + QString m_nodeid; + QPointer m_controlConnection; + QList m_sipCandidates; + QMutex m_mutex; +}; + +#endif // CONNECTIONMANAGER_H diff --git a/src/libtomahawk/network/ControlConnection.cpp b/src/libtomahawk/network/ControlConnection.cpp index feda66add..4b7e082b4 100644 --- a/src/libtomahawk/network/ControlConnection.cpp +++ b/src/libtomahawk/network/ControlConnection.cpp @@ -276,7 +276,11 @@ void ControlConnection::addPeerInfo( const peerinfo_ptr& peerInfo ) { peerInfo->setControlConnection( this ); - m_peerInfos.insert( peerInfo ); + // Check if we already have added this peerInfo + if ( !m_peerInfos.contains( peerInfo ) ) + { + m_peerInfos.insert( peerInfo ); + } } diff --git a/src/libtomahawk/network/Servent.cpp b/src/libtomahawk/network/Servent.cpp index 126bc932a..bd5267a4e 100644 --- a/src/libtomahawk/network/Servent.cpp +++ b/src/libtomahawk/network/Servent.cpp @@ -27,6 +27,7 @@ #include "ControlConnection.h" #include "database/Database.h" #include "database/DatabaseImpl.h" +#include "network/ConnectionManager.h" #include "StreamConnection.h" #include "SourceList.h" #include "sip/SipInfo.h" @@ -247,7 +248,6 @@ Servent::createConnectionKey( const QString& name, const QString &nodeid, const return _key; } - bool Servent::isValidExternalIP( const QHostAddress& addr ) { @@ -351,6 +351,17 @@ Servent::lookupControlConnection( const SipInfo& sipInfo ) return NULL; } +ControlConnection* +Servent::lookupControlConnection( const QString& nodeid ) +{ + foreach ( ControlConnection* c, m_controlconnections ) + { + if ( c->id() == nodeid ) + return c; + } + return NULL; +} + QList Servent::getLocalSipInfos( const QString& nodeid, const QString& key ) @@ -391,7 +402,7 @@ Servent::getLocalSipInfos( const QString& nodeid, const QString& key ) } SipInfo -Servent::getSipInfoForOldVersions( const QList& sipInfos ) const +Servent::getSipInfoForOldVersions( const QList& sipInfos ) { SipInfo info = SipInfo(); info.setVisible( false ); @@ -429,7 +440,7 @@ Servent::registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ) peerInfoDebug(peerInfo) << "we need to establish the connection now... thinking"; if ( !connectedToSession( peerInfo->nodeId() ) ) { - connectToPeer( peerInfo ); + ConnectionManager::getManagerForNodeId( peerInfo->nodeId() )->handleSipInfo( peerInfo ); } else { @@ -491,40 +502,7 @@ void Servent::handleSipInfo( const Tomahawk::peerinfo_ptr& peerInfo ) if ( peerInfo->sipInfos().isEmpty() ) return; - // Respect different behaviour before 0.7.99 - if ( !peerInfo->versionString().isEmpty() && TomahawkUtils::compareVersionStrings( peerInfo->versionString(), "Tomahawk Player EmptyOS 0.7.99" ) < 0) - { - SipInfo we = getSipInfoForOldVersions( getLocalSipInfos( QString(), QString() ) ); - SipInfo they = peerInfo->sipInfos().first(); - if ( they.isVisible() ) - { - if ( !we.isVisible() || we.host() < they.host() || (we.host() == they.host() && we.port() < they.port())) - { - tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Initiate connection to" << peerInfo->id() << "at" << they.host() << "peer of:" << peerInfo->sipPlugin()->account()->accountFriendlyName(); - connectToPeer( peerInfo ); - // We connected to the peer, so we are done here. - return; - } - } - } - foreach ( SipInfo info, peerInfo->sipInfos() ) - { - if (info.isVisible()) - { - // There is at least one SipInfo that may be visible. Try connecting. - // Duplicate Connections are checked by connectToPeer, so we do not need to take care of this - tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Initiate connection to" << peerInfo->id() << "at" << info.host() << "peer of:" << peerInfo->sipPlugin()->account()->accountFriendlyName(); - connectToPeer( peerInfo ); - // We connected to the peer, so we are done here. - return; - } - } - - // If we reach this point none of the previous SipInfos was visible. - if ( peerInfo->controlConnection() ) - delete peerInfo->controlConnection(); - - tDebug() << Q_FUNC_INFO << peerInfo->id() << "They are not visible, doing nothing atm"; + ConnectionManager::getManagerForNodeId( peerInfo->nodeId() )->handleSipInfo( peerInfo ); } void @@ -721,7 +699,6 @@ Servent::createParallelConnection( Connection* orig_conn, Connection* new_conn, // if we can connect to them directly: if ( orig_conn && orig_conn->outbound() ) { - QList sipInfo = QList(); SipInfo info = SipInfo(); info.setVisible( true ); info.setKey( key ); @@ -729,8 +706,7 @@ Servent::createParallelConnection( Connection* orig_conn, Connection* new_conn, info.setHost( orig_conn->socket()->peerAddress().toString() ); info.setPort( orig_conn->peerPort() ); Q_ASSERT( info.isValid() ); - sipInfo.append( info ); - connectToPeer( peerinfo_ptr(), sipInfo, new_conn ); + initiateConnection( info, new_conn ); } else // ask them to connect to us: { @@ -755,13 +731,13 @@ Servent::socketConnected() { QTcpSocketExtra* sock = (QTcpSocketExtra*)sender(); - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << thread() << "socket:" << sock << ", hostaddr:" << sock->peerAddress() << ", hostname:" << sock->peerName(); + tLog( LOGVERBOSE ) << Q_FUNC_INFO << thread() << "socket:" << sock << ", hostaddr:" << sock->peerAddress() << ", hostname:" << sock->peerName(); if ( sock->_conn.isNull() ) { sock->close(); sock->deleteLater(); - tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Socket's connection was null, could have timed out or been given an invalid address"; + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Socket's connection was null, could have timed out or been given an invalid address"; return; } @@ -769,9 +745,9 @@ Servent::socketConnected() handoverSocket( conn, sock ); } - // transfers ownership of socket to the connection and inits the connection -void Servent::handoverSocket( Connection* conn, QTcpSocketExtra* sock ) +void +Servent::handoverSocket( Connection* conn, QTcpSocketExtra* sock ) { Q_ASSERT( conn ); Q_ASSERT( sock ); @@ -780,8 +756,7 @@ void Servent::handoverSocket( Connection* conn, QTcpSocketExtra* sock ) disconnect( sock, SIGNAL( readyRead() ), this, SLOT( readyRead() ) ); disconnect( sock, SIGNAL( disconnected() ), sock, SLOT( deleteLater() ) ); - disconnect( sock, SIGNAL( error( QAbstractSocket::SocketError ) ), - this, SLOT( socketError( QAbstractSocket::SocketError ) ) ); + disconnect( sock, SIGNAL( error( QAbstractSocket::SocketError ) ), this, SLOT( socketError( QAbstractSocket::SocketError ) ) ); sock->_disowned = true; conn->setOutbound( sock->_outbound ); @@ -807,149 +782,33 @@ Servent::cleanupSocket( QTcpSocketExtra *sock ) } void -Servent::connectToPeer( const peerinfo_ptr& peerInfo ) +Servent::initiateConnection( const SipInfo& sipInfo, Connection* conn ) { - Q_ASSERT( this->thread() == QThread::currentThread() ); - - peerInfoDebug( peerInfo ) << "connectToPeer: search for already established connections to the same nodeid:" << m_controlconnections.count() << "connections"; - if ( peerInfo->controlConnection() ) - { - if ( peerInfo->controlConnection()->isReady() && peerInfo->controlConnection()->isRunning() ) { - peerInfoDebug( peerInfo ) << Q_FUNC_INFO << "We have a running ControlConnection, so no use to connect."; - return; - } - else - { - peerInfoDebug( peerInfo ) << Q_FUNC_INFO << "deleting the existing ControlConnection"; - delete peerInfo->controlConnection(); - } - } - - bool isDupe = false; - ControlConnection* conn = 0; - // try to find a ControlConnection with the same SipInfo, then we dont need to try to connect again - - foreach ( ControlConnection* c, m_controlconnections ) - { - Q_ASSERT( c ); - - if ( c->id() == peerInfo->nodeId() ) - { - conn = c; - - foreach ( const peerinfo_ptr& currentPeerInfo, c->peerInfos() ) - { - peerInfoDebug( currentPeerInfo ) << "Same object:" << ( peerInfo == currentPeerInfo ) << ( peerInfo.data() == currentPeerInfo.data() ) << ( peerInfo->debugName() == currentPeerInfo->debugName() ); - - if ( peerInfo == currentPeerInfo ) - { - isDupe = true; - peerInfoDebug( currentPeerInfo ) << "Not adding, because it's a dupe: peerInfoCount remains the same" << conn->peerInfos().count(); - break; - } - } - - if ( !c->peerInfos().contains( peerInfo ) ) - { - c->addPeerInfo( peerInfo ); -// peerInfoDebug(peerInfo) << "Adding " << peerInfo->debugName() << ", not a dupe... new peerInfoCount:" << c->peerInfos().count(); -// foreach ( const peerinfo_ptr& kuh, c->peerInfos() ) -// { -// peerInfoDebug(peerInfo) << " ** " << kuh->debugName(); -// } - } - - break; - } - } - - peerInfoDebug( peerInfo ) << "connectToPeer: found a match:" << ( conn ? conn->name() : "false" ) << "dupe:" << isDupe; - - if ( isDupe ) - { - peerInfoDebug( peerInfo ) << "it's a dupe, nothing to do here, returning and stopping processing: peerInfoCount:" << conn->peerInfos().count(); - } - - if ( conn ) - return; - - QVariantMap m; - m["conntype"] = "accept-offer"; - m["key"] = peerInfo->key(); - m["nodeid"] = Database::instance()->impl()->dbid(); - - peerInfoDebug(peerInfo) << "No match found, creating a new ControlConnection..."; - conn = new ControlConnection( this ); - conn->addPeerInfo( peerInfo ); - conn->setFirstMessage( m ); - - if ( peerInfo->id().length() ) - conn->setName( peerInfo->contactId() ); - if ( peerInfo->nodeId().length() ) - conn->setId( peerInfo->nodeId() ); - - conn->setProperty( "nodeid", peerInfo->nodeId() ); - - registerControlConnection( conn ); - connectToPeer( peerInfo, peerInfo->sipInfos(), conn ); -} - - -void -Servent::connectToPeer(const peerinfo_ptr& peerInfo, const QList& sipInfos, Connection* conn ) -{ - if ( sipInfos.isEmpty() ) - { - if ( conn != NULL ) { - peerInfoDebug(peerInfo) << Q_FUNC_INFO << "No more possible SIP endpoints for " << conn->name() << " skipping."; - } else { - peerInfoDebug(peerInfo) << Q_FUNC_INFO << "No more possible SIP endpoints for skipping."; - } - // If a peerinfo was supplied and has a ControlConnection which should be destroyed, than use this - if ( !peerInfo.isNull() && peerInfo->controlConnection() ) - delete peerInfo->controlConnection(); - else - // Connecting failed, so destroy this connection. - delete conn; - return; - } - QList sipInfo = QList( sipInfos ); - // Use first available SIP endpoint and remove it from the list - SipInfo info = sipInfo.takeFirst(); - if ( !info.isVisible() ) - { - peerInfoDebug(peerInfo) << Q_FUNC_INFO << "Try next SipInfo, we can't connect to this one"; - connectToPeer( peerInfo, sipInfo, conn ); - return; - } - - peerInfoDebug(peerInfo) << Q_FUNC_INFO << info.host() << ":" << info.port() << thread() << QThread::currentThread(); - - Q_ASSERT( info.port() > 0 ); + Q_ASSERT( sipInfo.isValid() ); + Q_ASSERT( sipInfo.isVisible() ); + Q_ASSERT( sipInfo.port() > 0 ); Q_ASSERT( conn ); // Check that we are not connecting to ourselves foreach( QHostAddress ha, m_externalAddresses ) { - if ( info.host() == ha.toString() ) + if ( sipInfo.host() == ha.toString() ) { - peerInfoDebug(peerInfo) << Q_FUNC_INFO << "Tomahawk won't try to connect to" << info.host() << ":" << info.port() << ": same IP as ourselves."; - connectToPeer( peerInfo, sipInfo, conn ); + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Tomahawk won't try to connect to" << sipInfo.host() << ":" << sipInfo.port() << ": same IP as ourselves."; return; } } - if ( info.host() == m_externalHostname ) + if ( sipInfo.host() == m_externalHostname ) { - peerInfoDebug(peerInfo) << Q_FUNC_INFO << "Tomahawk won't try to connect to" << info.host() << ":" << info.port() << ": same IP as ourselves."; - connectToPeer( peerInfo, sipInfo, conn ); + tLog( LOGVERBOSE ) << Q_FUNC_INFO << "Tomahawk won't try to connect to" << sipInfo.host() << ":" << sipInfo.port() << ": same IP as ourselves."; return; } - if ( info.key().length() && conn->firstMessage().isNull() ) + if ( !sipInfo.key().isEmpty() && conn->firstMessage().isNull() ) { QVariantMap m; m["conntype"] = "accept-offer"; - m["key"] = info.key(); + m["key"] = sipInfo.key(); m["controlid"] = Database::instance()->impl()->dbid(); conn->setFirstMessage( m ); } @@ -961,45 +820,43 @@ Servent::connectToPeer(const peerinfo_ptr& peerInfo, const QList& sipIn sock->_outbound = true; connect( sock, SIGNAL( connected() ), SLOT( socketConnected() ) ); - NewClosure( sock, SIGNAL( error( QAbstractSocket::SocketError ) ), - this, SLOT( connectToPeerFailed( Tomahawk::peerinfo_ptr, QList, Connection*, QTcpSocketExtra* ) ), - peerInfo, sipInfo, conn, sock ); + connect( sock, SIGNAL( error( QAbstractSocket::SocketError ) ), this, SLOT( socketError( QAbstractSocket::SocketError ) ) ); if ( !conn->peerIpAddress().isNull() ) - sock->connectToHost( conn->peerIpAddress(), info.port(), QTcpSocket::ReadWrite ); + sock->connectToHost( conn->peerIpAddress(), sipInfo.port(), QTcpSocket::ReadWrite ); else - sock->connectToHost( info.host(), info.port(), QTcpSocket::ReadWrite ); + sock->connectToHost( sipInfo.host(), sipInfo.port(), QTcpSocket::ReadWrite ); sock->moveToThread( thread() ); } -void -Servent::connectToPeerFailed( const peerinfo_ptr& peerInfo, QList sipInfo, Connection* conn, QTcpSocketExtra* socket ) -{ - peerInfoDebug(peerInfo) << Q_FUNC_INFO << "Connecting to " << socket->peerAddress().toString() << " failed: " << socket->errorString(); - bool connIsNull = socket->_conn.isNull(); - cleanupSocket( socket ); - - if ( !connIsNull ) { - // Try next SipInfo (don't do this if the connection was destroyed in between) - connectToPeer( peerInfo, sipInfo, conn ); - } - else - { - peerInfoDebug( peerInfo ) << Q_FUNC_INFO << "Connecting stopped because Connection is null."; - } -} - void Servent::socketError( QAbstractSocket::SocketError e ) { QTcpSocketExtra* sock = (QTcpSocketExtra*)sender(); + if ( !sock ) + { + tLog() << "SocketError, sock is null"; + return; + } + if ( !sock->_conn.isNull() ) { Connection* conn = sock->_conn.data(); - tLog() << Q_FUNC_INFO << e << conn->id() << conn->name(); - } + tLog() << "Servent::SocketError:" << e << conn->id() << conn->name(); - cleanupSocket( sock ); + if ( !sock->_disowned ) + { + // connection will delete if we already transferred ownership, otherwise: + sock->deleteLater(); + } + + conn->markAsFailed(); // will emit failed, then finished + } + else + { + tLog() << "SocketError, connection is null"; + sock->deleteLater(); + } } void diff --git a/src/libtomahawk/network/Servent.h b/src/libtomahawk/network/Servent.h index 50be80d7b..9724f46ff 100644 --- a/src/libtomahawk/network/Servent.h +++ b/src/libtomahawk/network/Servent.h @@ -66,6 +66,7 @@ Q_OBJECT public: static Servent* instance(); static bool isValidExternalIP( const QHostAddress& addr ); + static SipInfo getSipInfoForOldVersions( const QList &sipInfos ); explicit Servent( QObject* parent = 0 ); virtual ~Servent(); @@ -81,6 +82,7 @@ public: void registerControlConnection( ControlConnection* conn ); void unregisterControlConnection( ControlConnection* conn ); ControlConnection* lookupControlConnection( const SipInfo& sipInfo ); + ControlConnection* lookupControlConnection( const QString& nodeid ); // you may call this method as often as you like for the same peerInfo, dupe checking is done inside void registerPeer( const Tomahawk::peerinfo_ptr& peerInfo ); @@ -90,8 +92,7 @@ public slots: void onSipInfoChanged(); public: - void connectToPeer( const Tomahawk::peerinfo_ptr& ha ); - void connectToPeer(const Tomahawk::peerinfo_ptr &peerInfo, const QList& sipInfos, Connection* conn ); + void initiateConnection( const SipInfo& sipInfo, Connection* conn ); void reverseOfferRequest( ControlConnection* orig_conn, const QString &theirdbid, const QString& key, const QString& theirkey ); bool visibleExternally() const { return (!m_externalHostname.isNull()) || (m_externalAddresses.length() > 0); } @@ -132,7 +133,6 @@ public: bool isReady() const { return m_ready; } QList getLocalSipInfos(const QString& nodeid, const QString &key); - SipInfo getSipInfoForOldVersions( const QList &sipInfos ) const; signals: void dbSyncTriggered(); void streamStarted( StreamConnection* ); @@ -145,7 +145,6 @@ protected: public slots: void setExternalAddress( QHostAddress ha, unsigned int port ); - void connectToPeerFailed( const Tomahawk::peerinfo_ptr& peerInfo, QList sipInfo, Connection* conn , QTcpSocketExtra *socket ); void socketError( QAbstractSocket::SocketError e ); void createParallelConnection( Connection* orig_conn, Connection* new_conn, const QString& key );