//=============================================================================
//
// File : kvi_irccontext.cpp
// Created on Sun 09 May 2004 20:37:46 by Szymon Stefanek
//
// This file is part of the KVIrc IRC client distribution
// Copyright (C) 2004 Szymon Stefanek <pragma at kvirc dot net>
//
// 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 opinion) any later version.
//
// This program is distributed in the HOPE that it will be USEFUL,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, write to the Free Software Foundation,
// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
//=============================================================================
# define __KVIRC__
# include "kvi_irccontext.h"
# include "kvi_console.h"
# include "kvi_channel.h"
# include "kvi_query.h"
# include "kvi_frame.h"
# include "kvi_debug.h"
# include "kvi_sparser.h"
# include "kvi_ircconnection.h"
# include "kvi_ircconnectiontarget.h"
# include "kvi_asynchronousconnectiondata.h"
# include "kvi_ircconnectionstatedata.h"
# include "kvi_ircconnectionuserinfo.h"
# include "kvi_irctoolbar.h"
# include "kvi_out.h"
# include "kvi_ircserverdb.h"
# include "kvi_proxydb.h"
# include "kvi_options.h"
# include "kvi_locale.h"
# include "kvi_ircdatastreammonitor.h"
# include "kvi_error.h"
# include "kvi_thread.h" // for KviThread::msleep()
# include "kvi_app.h"
# include "kvi_kvs_eventtriggers.h"
# include "kvi_kvs_script.h"
# include "kvi_netutils.h"
# include "kvi_ircurl.h"
# include "kvi_qcstring.h"
# include "kvi_useridentity.h"
# define __KVI_DEBUG__
# include "kvi_debug.h"
# include <tqtimer.h>
// the irc context identifiers start from 1
static unsigned int g_uNextIrcContextId = 1 ;
extern KVIRC_API KviIrcServerDataBase * g_pIrcServerDataBase ;
extern KVIRC_API KviProxyDataBase * g_pProxyDataBase ;
KviIrcContext : : KviIrcContext ( KviConsole * pConsole )
: TQObject ( 0 )
{
m_uId = g_uNextIrcContextId ;
g_uNextIrcContextId + + ;
m_pConsole = pConsole ;
m_pConnection = 0 ;
m_pFrame = m_pConsole - > frame ( ) ;
m_pDeadChannels = 0 ;
m_pDeadQueries = 0 ;
m_pContextWindows = 0 ;
m_pLinksWindow = 0 ;
m_pListWindow = 0 ;
m_eState = Idle ;
m_pAsynchronousConnectionData = 0 ;
m_pSavedAsynchronousConnectionData = 0 ;
m_uConnectAttemptCount = 0 ;
m_pMonitorList = 0 ;
m_pReconnectTimer = 0 ;
m_uConnectAttemptCount = 1 ;
m_iHeartbeatTimerId = startTimer ( 5000 ) ;
}
KviIrcContext : : ~ KviIrcContext ( )
{
killTimer ( m_iHeartbeatTimerId ) ;
while ( m_pMonitorList )
{
KviIrcDataStreamMonitor * m = m_pMonitorList - > first ( ) ;
if ( m ) m - > die ( ) ;
else {
delete m_pMonitorList ;
m_pMonitorList = 0 ;
}
}
if ( m_pReconnectTimer ) delete m_pReconnectTimer ;
if ( m_pLinksWindow ) m_pLinksWindow - > die ( ) ;
if ( m_pListWindow ) m_pListWindow - > die ( ) ;
closeAllDeadChannels ( ) ;
closeAllDeadQueries ( ) ;
closeAllContextWindows ( ) ;
destroyConnection ( ) ;
if ( m_pAsynchronousConnectionData ) delete m_pAsynchronousConnectionData ;
if ( m_pSavedAsynchronousConnectionData ) delete m_pSavedAsynchronousConnectionData ;
}
void KviIrcContext : : unhighlightAllWindows ( )
{
m_pFrame - > unhighlightWindowsOfContext ( this ) ;
}
void KviIrcContext : : registerDataStreamMonitor ( KviIrcDataStreamMonitor * m )
{
if ( ! m_pMonitorList )
{
m_pMonitorList = new KviPointerList < KviIrcDataStreamMonitor > ;
m_pMonitorList - > setAutoDelete ( false ) ;
}
m_pMonitorList - > append ( m ) ;
}
void KviIrcContext : : unregisterDataStreamMonitor ( KviIrcDataStreamMonitor * m )
{
if ( ! m_pMonitorList ) return ;
m_pMonitorList - > removeRef ( m ) ;
if ( m_pMonitorList - > isEmpty ( ) )
{
delete m_pMonitorList ;
m_pMonitorList = 0 ;
}
}
void KviIrcContext : : closeAllDeadChannels ( )
{
while ( m_pDeadChannels )
{
KviChannel * c = m_pDeadChannels - > first ( ) ;
if ( c ) m_pFrame - > closeWindow ( c ) ;
else {
// ops....
delete m_pDeadChannels ;
m_pDeadChannels = 0 ;
}
}
}
void KviIrcContext : : closeAllDeadQueries ( )
{
while ( m_pDeadQueries )
{
KviQuery * q = m_pDeadQueries - > first ( ) ;
if ( q ) m_pFrame - > closeWindow ( q ) ;
else {
// ops....
delete m_pDeadQueries ;
m_pDeadQueries = 0 ;
}
}
}
void KviIrcContext : : closeAllContextWindows ( )
{
while ( m_pContextWindows )
{
KviWindow * w = m_pContextWindows - > first ( ) ;
if ( w ) m_pFrame - > closeWindow ( w ) ;
else {
// ops...
delete m_pContextWindows ;
m_pContextWindows = 0 ;
}
}
}
KviChannel * KviIrcContext : : findDeadChannel ( const TQString & name )
{
if ( ! m_pDeadChannels ) return 0 ;
for ( KviChannel * c = m_pDeadChannels - > first ( ) ; c ; c = m_pDeadChannels - > next ( ) )
{
__range_valid ( c - > isDeadChan ( ) ) ;
if ( KviTQString : : equalCI ( name , c - > windowName ( ) ) ) return c ;
}
return 0 ;
}
KviQuery * KviIrcContext : : findDeadQuery ( const TQString & name )
{
if ( ! m_pDeadQueries ) return 0 ;
for ( KviQuery * c = m_pDeadQueries - > first ( ) ; c ; c = m_pDeadQueries - > next ( ) )
{
__range_valid ( c - > isDeadQuery ( ) ) ;
if ( KviTQString : : equalCI ( name , c - > windowName ( ) ) ) return c ;
}
return 0 ;
}
KviQuery * KviIrcContext : : firstDeadQuery ( )
{
if ( ! m_pDeadQueries ) return 0 ;
return m_pDeadQueries - > first ( ) ;
}
void KviIrcContext : : registerContextWindow ( KviWindow * pWnd )
{
if ( ! m_pContextWindows )
{
m_pContextWindows = new KviPointerList < KviWindow > ;
m_pContextWindows - > setAutoDelete ( false ) ;
}
m_pContextWindows - > append ( pWnd ) ;
}
void KviIrcContext : : registerDeadChannel ( KviChannel * c )
{
if ( ! m_pDeadChannels )
{
m_pDeadChannels = new KviPointerList < KviChannel > ;
m_pDeadChannels - > setAutoDelete ( false ) ;
}
m_pDeadChannels - > append ( c ) ;
}
void KviIrcContext : : registerDeadQuery ( KviQuery * q )
{
if ( ! m_pDeadQueries )
{
m_pDeadQueries = new KviPointerList < KviQuery > ;
m_pDeadQueries - > setAutoDelete ( false ) ;
}
m_pDeadQueries - > append ( q ) ;
}
bool KviIrcContext : : unregisterDeadChannel ( KviChannel * c )
{
// was a dead channel ?
if ( ! m_pDeadChannels ) return false ;
if ( ! m_pDeadChannels - > removeRef ( c ) )
{
return false ;
}
if ( m_pDeadChannels - > isEmpty ( ) )
{
delete m_pDeadChannels ;
m_pDeadChannels = 0 ;
}
return true ;
}
bool KviIrcContext : : unregisterContextWindow ( KviWindow * pWnd )
{
if ( ! m_pContextWindows ) return false ;
if ( ! m_pContextWindows - > removeRef ( pWnd ) )
{
return false ;
}
if ( m_pContextWindows - > isEmpty ( ) )
{
delete m_pContextWindows ;
m_pContextWindows = 0 ;
}
return true ;
}
bool KviIrcContext : : unregisterDeadQuery ( KviQuery * q )
{
if ( ! m_pDeadQueries ) return false ;
if ( ! m_pDeadQueries - > removeRef ( q ) )
{
return false ;
}
if ( m_pDeadQueries - > isEmpty ( ) )
{
delete m_pDeadQueries ;
m_pDeadQueries = 0 ;
}
return true ;
}
void KviIrcContext : : createLinksWindow ( )
{
if ( m_pLinksWindow ) return ;
KviKvsScript : : run ( " links.open " , m_pConsole ) ;
}
void KviIrcContext : : createListWindow ( )
{
if ( m_pListWindow ) return ;
KviKvsScript : : run ( " list.open " , m_pConsole ) ;
}
void KviIrcContext : : destroyConnection ( )
{
if ( ! m_pConnection ) return ;
m_pConnection - > closeAllChannels ( ) ;
m_pConnection - > closeAllQueries ( ) ;
if ( m_pLinksWindow ) m_pLinksWindow - > control ( EXTERNAL_SERVER_DATA_PARSER_CONTROL_RESET ) ;
if ( m_pListWindow ) m_pListWindow - > control ( EXTERNAL_SERVER_DATA_PARSER_CONTROL_RESET ) ;
m_pConsole - > connectionDetached ( ) ;
// make sure that m_pConnection is already 0 in any
// event triggered by KviIrcConnection destructor
KviIrcConnection * pTmp = m_pConnection ;
m_pConnection = 0 ;
delete pTmp ;
}
void KviIrcContext : : setState ( State eState )
{
if ( m_eState = = eState ) return ;
m_eState = eState ;
m_pFrame - > childContextStateChange ( this ) ;
emit stateChanged ( ) ;
if ( eState = = KviIrcContext : : Idle ) destroyConnection ( ) ;
m_pConsole - > updateCaption ( ) ;
}
void KviIrcContext : : setAsynchronousConnectionData ( KviAsynchronousConnectionData * d )
{
if ( m_pAsynchronousConnectionData ) delete m_pAsynchronousConnectionData ;
m_pAsynchronousConnectionData = d ;
}
void KviIrcContext : : destroyAsynchronousConnectionData ( )
{
if ( ! m_pAsynchronousConnectionData ) return ;
delete m_pAsynchronousConnectionData ;
m_pAsynchronousConnectionData = 0 ;
}
void KviIrcContext : : loginComplete ( )
{
setState ( Connected ) ;
}
void KviIrcContext : : connectButtonClicked ( )
{
if ( ! connection ( ) )
{
if ( m_pReconnectTimer )
{
// reconnection was in progress...
delete m_pReconnectTimer ;
m_pReconnectTimer = 0 ;
destroyAsynchronousConnectionData ( ) ;
m_pConsole - > outputNoFmt ( KVI_OUT_SYSTEMERROR ,
__tr2qs ( " Reconnect attempt aborted " ) ) ;
return ;
}
// No connections in progress
m_uConnectAttemptCount = 1 ;
connectToCurrentServer ( ) ;
} else {
// Sth is going on
terminateConnectionRequest ( false ) ;
}
}
void KviIrcContext : : connectToCurrentServer ( )
{
if ( m_pReconnectTimer )
{
delete m_pReconnectTimer ;
m_pReconnectTimer = 0 ;
}
m_pConsole - > outputNoFmt ( KVI_OUT_SYSTEMMESSAGE , " " ) ; // spacer
if ( ! m_pAsynchronousConnectionData )
{
// No connection target specified.
// If we have a saved target, reuse it
if ( m_pSavedAsynchronousConnectionData )
{
m_pAsynchronousConnectionData = m_pSavedAsynchronousConnectionData ;
m_pSavedAsynchronousConnectionData = 0 ;
}
}
if ( m_pAsynchronousConnectionData )
{
// we have a specified connection target (either from outside or saved)
if ( m_pAsynchronousConnectionData - > szServer . isEmpty ( ) )
{
// an empty server might mean "reuse the last server in context"
if ( m_pAsynchronousConnectionData - > bUseLastServerInContext )
{
if ( m_pSavedAsynchronousConnectionData )
{
// reuse the saved connection data
// the server for sure
m_pAsynchronousConnectionData - > szServer = m_pSavedAsynchronousConnectionData - > szServer ;
m_pAsynchronousConnectionData - > uPort = m_pSavedAsynchronousConnectionData - > uPort ;
m_pAsynchronousConnectionData - > bPortIsOk = true ;
m_pAsynchronousConnectionData - > bUseIpV6 = m_pSavedAsynchronousConnectionData - > bUseIpV6 ;
m_pAsynchronousConnectionData - > bUseSSL = m_pSavedAsynchronousConnectionData - > bUseSSL ;
m_pAsynchronousConnectionData - > m_pReconnectInfo = m_pSavedAsynchronousConnectionData - > m_pReconnectInfo ;
// and the other info, only if not overridden by the user
if ( m_pAsynchronousConnectionData - > szBindAddress . isEmpty ( ) )
m_pAsynchronousConnectionData - > szBindAddress = m_pSavedAsynchronousConnectionData - > szBindAddress ;
if ( m_pAsynchronousConnectionData - > szCommandToExecAfterConnect . isEmpty ( ) )
m_pAsynchronousConnectionData - > szCommandToExecAfterConnect = m_pSavedAsynchronousConnectionData - > szCommandToExecAfterConnect ;
if ( m_pAsynchronousConnectionData - > szLinkFilter . isEmpty ( ) )
m_pAsynchronousConnectionData - > szLinkFilter = m_pSavedAsynchronousConnectionData - > szLinkFilter ;
if ( m_pAsynchronousConnectionData - > szPass . isEmpty ( ) )
m_pAsynchronousConnectionData - > szPass = m_pSavedAsynchronousConnectionData - > szPass ;
if ( m_pAsynchronousConnectionData - > szNick . isEmpty ( ) )
m_pAsynchronousConnectionData - > szNick = m_pSavedAsynchronousConnectionData - > szNick ;
if ( m_pAsynchronousConnectionData - > szInitUMode . isEmpty ( ) )
m_pAsynchronousConnectionData - > szInitUMode = m_pSavedAsynchronousConnectionData - > szInitUMode ;
} else
m_pConsole - > outputNoFmt ( KVI_OUT_SYSTEMWARNING , __tr2qs ( " This is the first connection in this IRC context: using the global server setting " ) ) ;
} // else it just means "do connect" to the globally selected irc server in the options dialog
}
if ( ! ( m_pAsynchronousConnectionData - > szServer . isEmpty ( ) ) )
{
// ok , have a server to look for in the db
// FIXME: this is a bit ugly... could it be managed in some completly different and nicer way ?
KviIrcServerDefinition d ;
d . szServer = m_pAsynchronousConnectionData - > szServer ;
d . bPortIsValid = m_pAsynchronousConnectionData - > bPortIsOk ;
d . uPort = m_pAsynchronousConnectionData - > uPort ;
d . bIpV6 = m_pAsynchronousConnectionData - > bUseIpV6 ;
d . bSSL = m_pAsynchronousConnectionData - > bUseSSL ;
d . szLinkFilter = m_pAsynchronousConnectionData - > szLinkFilter ;
d . szPass = m_pAsynchronousConnectionData - > szPass ;
d . szNick = m_pAsynchronousConnectionData - > szNick ;
d . szInitUMode = m_pAsynchronousConnectionData - > szInitUMode ;
TQString szError ;
if ( ! g_pIrcServerDataBase - > makeCurrentServer ( & d , szError ) )
{
m_pConsole - > outputNoFmt ( KVI_OUT_SYSTEMERROR , szError ) ;
destroyAsynchronousConnectionData ( ) ;
return ;
}
} // else we just connect to the globally selected irc server in the options dialog
}
KviIrcServerDataBaseRecord * rec = g_pIrcServerDataBase - > currentRecord ( ) ;
KviIrcNetwork * net ;
KviIrcServer * srv ;
net = rec ? rec - > network ( ) : 0 ;
srv = net ? rec - > currentServer ( ) : 0 ;
KviProxy * prx = 0 ;
if ( ! srv )
{
m_pConsole - > outputNoFmt ( KVI_OUT_SYSTEMERROR , __tr2qs ( " No servers available. Check the options dialog or use the /SERVER command " ) ) ;
destroyAsynchronousConnectionData ( ) ;
return ;
}
if ( ! net )
{
// BUG
m_pConsole - > outputNoFmt ( KVI_OUT_SYSTEMERROR , __tr2qs ( " Ooops.. you've hit a bug in the servers database... I have found a server but not a network... " ) ) ;
destroyAsynchronousConnectionData ( ) ;
return ;
}
prx = srv - > proxyServer ( g_pProxyDataBase ) ;
if ( ! prx & & ( srv - > proxy ( ) ! = - 1 ) & & KVI_OPTION_BOOL ( KviOption_boolUseProxyHost ) )
{
prx = g_pProxyDataBase - > currentProxy ( ) ;
if ( ! prx )
{
m_pConsole - > outputNoFmt ( KVI_OUT_SYSTEMWARNING , __tr2qs ( " No proxy hosts available, resuming direct connection " ) ) ;
}
}
KviStr szBindAddress ;
if ( m_pAsynchronousConnectionData )
{
szBindAddress = m_pAsynchronousConnectionData - > szBindAddress ;
srv - > m_pReconnectInfo = m_pAsynchronousConnectionData - > m_pReconnectInfo ;
}
// Find out the identity we'll be using in this connection
// First check the server for one
const KviUserIdentity * pIdentity = 0 ;
TQString szUserIdentityId = srv - > userIdentityId ( ) ;
if ( ! szUserIdentityId . isEmpty ( ) )
pIdentity = KviUserIdentityManager : : instance ( ) - > findIdentity ( szUserIdentityId ) ;
// If not found, look in the network instead
if ( ! pIdentity )
szUserIdentityId = net - > userIdentityId ( ) ;
if ( ! szUserIdentityId . isEmpty ( ) )
pIdentity = KviUserIdentityManager : : instance ( ) - > findIdentity ( szUserIdentityId ) ;
// If not found, get the default identity (this is GRANTED to be never null, eventually filled up with defaults)
pIdentity = KviUserIdentityManager : : instance ( ) - > defaultIdentity ( ) ;
if ( m_pConnection ) delete m_pConnection ;
m_pConnection = new KviIrcConnection (
this ,
new KviIrcConnectionTarget (
net ,
srv ,
prx ,
szBindAddress . ptr ( )
) ,
new KviUserIdentity ( * pIdentity )
) ;
setState ( Connecting ) ;
if ( m_pAsynchronousConnectionData )
{
m_pConnection - > stateData ( ) - > setCommandToExecAfterConnect ( m_pAsynchronousConnectionData - > szCommandToExecAfterConnect ) ;
destroyAsynchronousConnectionData ( ) ;
}
m_pConsole - > connectionAttached ( ) ;
// save stuff for later
// FIXME: this management of "next" connection should be reviewed a bit anyway
if ( m_pSavedAsynchronousConnectionData ) delete m_pSavedAsynchronousConnectionData ;
m_pSavedAsynchronousConnectionData = new KviAsynchronousConnectionData ( ) ;
m_pSavedAsynchronousConnectionData - > szServer = srv - > m_szHostname ;
m_pSavedAsynchronousConnectionData - > uPort = srv - > port ( ) ;
m_pSavedAsynchronousConnectionData - > bPortIsOk = true ;
m_pSavedAsynchronousConnectionData - > bUseIpV6 = srv - > isIpV6 ( ) ;
m_pSavedAsynchronousConnectionData - > bUseSSL = srv - > useSSL ( ) ;
m_pSavedAsynchronousConnectionData - > szPass = srv - > password ( ) ;
m_pSavedAsynchronousConnectionData - > szInitUMode = srv - > m_szInitUMode ;
m_pSavedAsynchronousConnectionData - > m_pReconnectInfo = srv - > m_pReconnectInfo ;
// this never fails!
m_pConnection - > start ( ) ;
}
void KviIrcContext : : connectionFailed ( int iError )
{
if ( ! m_pConnection ) return ; // this may happen in the destructor!
m_pConsole - > output ( KVI_OUT_SYSTEMERROR ,
__tr2qs ( " Connection attempt failed [%s] " ) ,
m_pConnection - > target ( ) - > server ( ) - > m_szHostname . utf8 ( ) . data ( ) ) ;
// if the connection has been aborted by the user then just go idle
if ( iError = = KviError_operationAborted )
goto enter_idle_state ;
// FIXME: this should stop on critical errors !
if ( KVI_OPTION_BOOL ( KviOption_boolAutoReconnectOnUnexpectedDisconnect ) )
{
if ( ( ! KVI_OPTION_UINT ( KviOption_uintMaxAutoReconnectAttempts ) | |
( m_uConnectAttemptCount < KVI_OPTION_UINT ( KviOption_uintMaxAutoReconnectAttempts ) ) ) )
{
m_uConnectAttemptCount + + ;
//FIXME: Multiply the delay by (m_uConnectAttemptCount / 2) so later connects are less frequent.
if ( ! _OUTPUT_MUTE )
{
TQString tmp ;
KviTQString : : sprintf ( tmp , __tr2qs ( " Will attempt to reconnect in %d seconds " ) , KVI_OPTION_UINT ( KviOption_uintAutoReconnectDelay ) ) ;
TQString num ;
if ( ! KVI_OPTION_UINT ( KviOption_uintMaxAutoReconnectAttempts ) )
KviTQString : : sprintf ( num , __tr2qs ( " %d " ) , m_uConnectAttemptCount ) ;
else
KviTQString : : sprintf ( num , __tr2qs ( " %d of %d " ) ,
m_uConnectAttemptCount , KVI_OPTION_UINT ( KviOption_uintMaxAutoReconnectAttempts ) ) ;
tmp + = " [ " + num + " ] " ;
m_pConsole - > outputNoFmt ( KVI_OUT_SYSTEMMESSAGE , tmp ) ;
}
KviIrcServer oldServer ( * ( connection ( ) - > server ( ) ) ) ;
TQString oldNickname = connection ( ) - > userInfo ( ) - > isAway ( ) ? connection ( ) - > userInfo ( ) - > nickNameBeforeAway ( ) : connection ( ) - > userInfo ( ) - > nickName ( ) ;
KviAsynchronousConnectionData * d = new KviAsynchronousConnectionData ( ) ;
d - > szServer = oldServer . m_szHostname ;
d - > uPort = oldServer . port ( ) ;
d - > bPortIsOk = true ;
d - > bUseIpV6 = oldServer . isIpV6 ( ) ;
d - > bUseSSL = oldServer . useSSL ( ) ;
d - > szPass = oldServer . password ( ) ;
d - > szNick = oldNickname ;
d - > szInitUMode = oldServer . m_szInitUMode ;
d - > szCommandToExecAfterConnect = " " ;
setAsynchronousConnectionData ( d ) ;
beginAsynchronousConnect ( 1000 * KVI_OPTION_UINT ( KviOption_uintAutoReconnectDelay ) ) ;
setState ( Idle ) ; // destroy the actual connection
return ;
} else {
if ( ! _OUTPUT_MUTE )
m_pConsole - > output ( KVI_OUT_SYSTEMERROR , __tr2qs ( " Maximum number of reconnect attempts reached (%d): giving up " ) , KVI_OPTION_UINT ( KviOption_uintMaxAutoReconnectAttempts ) ) ;
}
}
// reset the attempt count
m_uConnectAttemptCount = 1 ;
if ( connection ( ) - > server ( ) - > cacheIp ( ) )
{
if ( ( ( ( int ) iError ) = = KviError_connectionTimedOut ) | |
( ( ( int ) iError ) = = KviError_connectionRefused ) | |
( ( ( int ) iError ) = = KviError_networkUnreachable ) | |
( ( ( int ) iError ) = = KviError_hostUnreachable ) )
{
m_pConsole - > output ( KVI_OUT_SYSTEMWARNING , __tr2qs ( " The connection attempt failed while using a cached IP address for the current server " ) ) ;
m_pConsole - > output ( KVI_OUT_SYSTEMWARNING , __tr2qs ( " The problem *might* be caused by an updated DNS entry " ) ) ;
m_pConsole - > output ( KVI_OUT_SYSTEMWARNING , __tr2qs ( " Try reconnecting with caching disabled " ) ) ;
}
}
enter_idle_state :
setState ( Idle ) ;
}
void KviIrcContext : : connectionEstabilished ( )
{
//
// The connection has been estabilished, the
// KviIrcConnection will attempt to login now
//
m_uConnectAttemptCount = 1 ;
bool bStopOutput = false ;
setState ( LoggingIn ) ; // this must be set in order for $server and other functions to return the correct values
bStopOutput = KVS_TRIGGER_EVENT_0_HALTED ( KviEvent_OnIrcConnectionEstabilished , m_pConsole ) ;
if ( ! bStopOutput )
{
m_pConsole - > output ( KVI_OUT_CONNECTION , __tr2qs ( " %Q established [%s (%s:%u)] " ) ,
connection ( ) - > socket ( ) - > usingSSL ( ) ? & ( __tr2qs ( " Secure connection " ) ) : & ( __tr2qs ( " Connection " ) ) ,
connection ( ) - > server ( ) - > m_szHostname . utf8 ( ) . data ( ) ,
connection ( ) - > server ( ) - > m_szIp . utf8 ( ) . data ( ) ,
connection ( ) - > server ( ) - > m_uPort ) ;
}
// Add to recent server list (build the url of type irc[6]://<server>:<port>
TQString url ;
KviIrcUrl : : join ( url , connection ( ) - > server ( ) ) ;
g_pApp - > addRecentServer ( url ) ;
// save the last server this console used
//if(m_pLastIrcServer)delete m_pLastIrcServer;
//m_pLastIrcServer = new KviIrcServer(*(connection()->server()));
}
void KviIrcContext : : connectionTerminated ( )
{
if ( ! m_pConnection ) return ; // this may happen in the destructor!
KviIrcServer oldServer ( * ( connection ( ) - > server ( ) ) ) ;
if ( oldServer . m_pReconnectInfo ) delete oldServer . m_pReconnectInfo ;
KviIrcServerReconnectInfo * pInfo = new KviIrcServerReconnectInfo ( ) ;
pInfo - > m_szNick = connection ( ) - > userInfo ( ) - > isAway ( ) ? connection ( ) - > userInfo ( ) - > nickNameBeforeAway ( ) : connection ( ) - > userInfo ( ) - > nickName ( ) ;
pInfo - > m_bIsAway = connection ( ) - > userInfo ( ) - > isAway ( ) ;
pInfo - > m_szAwayReason = connection ( ) - > userInfo ( ) - > awayReason ( ) ;
// we consider it unexpected when we haven't sent a QUIT message and we're connected
// or alternatively when a simulation of such a termination is requested (this is used to keep the queries open etc..)
bool bUnexpectedDisconnect = ( ! ( connection ( ) - > stateData ( ) - > sentQuit ( ) ) ) & & ( m_eState = = KviIrcContext : : Connected ) | |
connection ( ) - > stateData ( ) - > simulateUnexpectedDisconnect ( ) ;
TQString szChannels , szProtectedChannels , szPasswords , szCurPass , szCurChan ;
if ( bUnexpectedDisconnect )
{
if ( KVI_OPTION_BOOL ( KviOption_boolAutoReconnectOnUnexpectedDisconnect ) )
{
if ( KVI_OPTION_BOOL ( KviOption_boolRejoinChannelsAfterReconnect ) )
{
// FIXME: THIS SHOULD BE A KviIrcConnection FUNCTION
int idx = 0 ;
KviChannel * c ;
TQString szChannels , szProtectedChannels , szPasswords , szCurPass , szCurChan ;
// first only chans without key, in groups of 4
for ( c = connection ( ) - > channelList ( ) - > first ( ) ; c ; c = connection ( ) - > channelList ( ) - > next ( ) )
{
szCurPass = c - > channelKey ( ) ;
szCurChan = c - > windowName ( ) ;
if ( szCurPass . isEmpty ( ) )
{
if ( ! szChannels . isEmpty ( ) )
szChannels . append ( " , " ) ;
szChannels . append ( szCurChan ) ;
} else {
if ( ! szProtectedChannels . isEmpty ( ) )
szProtectedChannels . append ( " , " ) ;
szProtectedChannels . append ( szCurChan ) ;
if ( ! szPasswords . isEmpty ( ) )
szPasswords . append ( " , " ) ;
szPasswords . append ( szCurPass ) ;
}
}
if ( ( ! szChannels . isEmpty ( ) ) | | ( ! szProtectedChannels . isEmpty ( ) ) )
{
pInfo - > m_szJoinChannels . append ( szProtectedChannels ) ;
if ( ! szProtectedChannels . isEmpty ( ) & & ! szChannels . isEmpty ( ) )
pInfo - > m_szJoinChannels . append ( ' , ' ) ;
pInfo - > m_szJoinChannels . append ( szChannels ) ;
pInfo - > m_szJoinChannels . append ( " " ) ;
pInfo - > m_szJoinChannels . append ( szPasswords ) ;
}
}
if ( KVI_OPTION_BOOL ( KviOption_boolReopenQueriesAfterReconnect ) )
{
for ( KviQuery * q = connection ( ) - > queryList ( ) - > first ( ) ; q ; q = connection ( ) - > queryList ( ) - > next ( ) )
{
pInfo - > m_szOpenQueryes . append ( q - > target ( ) ) ;
}
}
}
if ( KVI_OPTION_BOOL ( KviOption_boolKeepChannelsOpenOnUnexpectedDisconnect ) | | KVI_OPTION_BOOL ( KviOption_boolKeepChannelsOpenOnDisconnect ) )
connection ( ) - > keepChannelsOpenAfterDisconnect ( ) ;
if ( KVI_OPTION_BOOL ( KviOption_boolKeepQueriesOpenOnUnexpectedDisconnect ) | | KVI_OPTION_BOOL ( KviOption_boolKeepQueriesOpenOnDisconnect ) )
connection ( ) - > keepQueriesOpenAfterDisconnect ( ) ;
} else {
if ( KVI_OPTION_BOOL ( KviOption_boolKeepChannelsOpenOnDisconnect ) )
connection ( ) - > keepChannelsOpenAfterDisconnect ( ) ;
if ( KVI_OPTION_BOOL ( KviOption_boolKeepQueriesOpenOnDisconnect ) )
connection ( ) - > keepQueriesOpenAfterDisconnect ( ) ;
}
setState ( Idle ) ;
bool bStopOutput = false ;
bStopOutput = KVS_TRIGGER_EVENT_0_HALTED ( KviEvent_OnIrcConnectionTerminated , m_pConsole ) ;
if ( ! bStopOutput )
{
m_pConsole - > output ( KVI_OUT_CONNECTION , __tr2qs ( " Connection terminated [%s (%s:%u)] " ) ,
oldServer . hostName ( ) . utf8 ( ) . data ( ) ,
oldServer . ip ( ) . utf8 ( ) . data ( ) ,
oldServer . port ( ) ) ;
}
// do reconnect
if ( bUnexpectedDisconnect & & KVI_OPTION_BOOL ( KviOption_boolAutoReconnectOnUnexpectedDisconnect ) )
{
//m_uConnectAttemptCount = 1;
if ( ! _OUTPUT_MUTE )
m_pConsole - > output ( KVI_OUT_CONNECTION , __tr2qs ( " The connection terminated unexpectedly. Trying to reconnect... " ) ) ;
KviAsynchronousConnectionData * d = new KviAsynchronousConnectionData ( ) ;
d - > szServer = oldServer . m_szHostname ;
d - > uPort = oldServer . port ( ) ;
d - > bPortIsOk = true ;
d - > bUseIpV6 = oldServer . isIpV6 ( ) ;
d - > bUseSSL = oldServer . useSSL ( ) ;
d - > szPass = oldServer . password ( ) ;
d - > szInitUMode = oldServer . m_szInitUMode ;
d - > m_pReconnectInfo = pInfo ;
setAsynchronousConnectionData ( d ) ;
beginAsynchronousConnect ( 1000 * KVI_OPTION_UINT ( KviOption_uintAutoReconnectDelay ) ) ;
}
}
void KviIrcContext : : beginAsynchronousConnect ( unsigned int uDelayInMSecs )
{
if ( m_pReconnectTimer ) delete m_pReconnectTimer ;
m_pReconnectTimer = new TQTimer ( this ) ;
connect ( m_pReconnectTimer , TQ_SIGNAL ( timeout ( ) ) , this , TQ_SLOT ( asynchronousConnect ( ) ) ) ;
m_pReconnectTimer - > start ( uDelayInMSecs ) ;
}
void KviIrcContext : : asynchronousConnect ( )
{
if ( m_pReconnectTimer )
{
delete m_pReconnectTimer ;
m_pReconnectTimer = 0 ;
}
if ( state ( ) ! = Idle ) // need a brutal disconnect here
terminateConnectionRequest ( true , " Changing server... " ) ;
connectToCurrentServer ( ) ;
}
void KviIrcContext : : terminateConnectionRequest ( bool bForce , const TQString & szQuitMsg , bool bSimulateUnexpectedDisconnect )
{
if ( ! connection ( ) ) return ; // hm ?
if ( bSimulateUnexpectedDisconnect ) connection ( ) - > stateData ( ) - > setSimulateUnexpectedDisconnect ( ) ;
switch ( m_eState )
{
case Connected :
{
// was connected : send a quit and abort the connection
bool bWasSentQuit = true ;
if ( ! connection ( ) - > stateData ( ) - > sentQuit ( ) )
{
KVS_TRIGGER_EVENT_0 ( KviEvent_OnDisconnectRequest , m_pConsole ) ;
TQString szQuit = szQuitMsg ;
if ( szQuit . isEmpty ( ) ) szQuit = KVI_OPTION_STRING ( KviOption_stringQuitMessage ) ;
szQuit . replace ( " ; " , " \\ ; " ) ;
szQuit . replace ( " \n " , " " ) ;
TQString buffer ;
KviKvsVariant ret ;
if ( KviKvsScript : : evaluate ( szQuit , console ( ) , 0 , & ret ) )
ret . asString ( buffer ) ;
else
buffer = szQuit ;
KviTQCString dat = console ( ) - > encodeText ( buffer ) ;
bWasSentQuit = false ;
connection ( ) - > stateData ( ) - > setSentQuit ( ) ;
connection ( ) - > sendFmtData ( " QUIT :%s " , dat . data ( ) ? dat . data ( ) : " " ) ; // here theoretically we COULD get disconnected
} // else it was already sent anyway
if ( KVI_OPTION_BOOL ( KviOption_boolForceBrutalQuit ) | | bWasSentQuit | | bForce )
{
if ( ! bWasSentQuit )
{
// idle for some milliseconds in order to allow the quit message to reach
// the remote end without breaking the connection
KviThread : : msleep ( 100 ) ;
}
// and brutally abort the connection (if it still exists!!!)
if ( connection ( ) ) connection ( ) - > abort ( ) ;
} else {
if ( ! bWasSentQuit )
m_pConsole - > outputNoFmt ( KVI_OUT_SYSTEMMESSAGE , __tr2qs ( " Sent QUIT, waiting for the server to close the connection... " ) ) ;
// else it was already sent anyway
}
}
break ;
case Connecting :
case LoggingIn :
// was waiting for connection or login, just abort it: it will trigger an error anyway
connection ( ) - > abort ( ) ;
break ;
default :
// should never end here!
__ASSERT ( false ) ;
break ;
}
}
void KviIrcContext : : timerEvent ( TQTimerEvent * e )
{
if ( e - > timerId ( ) ! = m_iHeartbeatTimerId )
{
TQObject : : timerEvent ( e ) ;
return ;
}
// our heartbeat
kvi_time_t tNow = kvi_unixTime ( ) ;
if ( m_pConnection )
m_pConnection - > heartbeat ( tNow ) ;
}