You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kvirc/src/kvirc/kernel/kvi_irccontext.cpp

907 lines
26 KiB

//=============================================================================
//
// 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);
}