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.
581 lines
18 KiB
581 lines
18 KiB
/*
|
|
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.
|
|
*/
|
|
|
|
/*
|
|
Copyright (C) 2008 Eike Hein <hein@kde.org>
|
|
*/
|
|
|
|
#include "connectionmanager.h"
|
|
#include "connectionsettings.h"
|
|
#include "serversettings.h"
|
|
#include "servergroupsettings.h"
|
|
#include "config/preferences.h"
|
|
#include "konversationapplication.h"
|
|
#include "konversationmainwindow.h"
|
|
#include "statuspanel.h"
|
|
|
|
#include <tqregexp.h>
|
|
|
|
#include <klocale.h>
|
|
#include <kmessagebox.h>
|
|
|
|
|
|
ConnectionManager::ConnectionManager(TQObject* parent) : TQObject(parent)
|
|
{
|
|
connect(this, TQT_SIGNAL(requestReconnect(Server*)), this, TQT_SLOT(handleReconnect(Server*)));
|
|
}
|
|
|
|
ConnectionManager::~ConnectionManager()
|
|
{
|
|
}
|
|
|
|
void ConnectionManager::connectTo(Konversation::ConnectionFlag flag, const TQString& target,
|
|
const TQString& port, const TQString& password, const TQString& nick, const TQString& channel,
|
|
bool useSSL)
|
|
{
|
|
ConnectionSettings settings;
|
|
|
|
if (target.startsWith("irc://"))
|
|
decodeIrcUrl(target, settings);
|
|
else
|
|
{
|
|
decodeAddress(target, settings);
|
|
|
|
Konversation::ServerSettings server = settings.server();
|
|
|
|
if (!port.isEmpty()) server.setPort(port.toInt());
|
|
|
|
if (!password.isEmpty()) server.setPassword(password);
|
|
|
|
if (useSSL) server.setSSLEnabled(true);
|
|
|
|
settings.setServer(server);
|
|
|
|
if (!nick.isEmpty()) settings.setInitialNick(nick);
|
|
|
|
if (!channel.isEmpty())
|
|
{
|
|
Konversation::ChannelSettings channelSettings(channel);
|
|
|
|
settings.setInitialChannel(channelSettings);
|
|
}
|
|
}
|
|
|
|
connectTo(flag, settings);
|
|
}
|
|
|
|
void ConnectionManager::connectTo(Konversation::ConnectionFlag flag, int serverGroupId)
|
|
{
|
|
ConnectionSettings settings;
|
|
|
|
Konversation::ServerGroupSettingsPtr serverGroup;
|
|
|
|
serverGroup = Preferences::serverGroupById(serverGroupId);
|
|
|
|
if (serverGroup)
|
|
{
|
|
settings.setServerGroup(serverGroup);
|
|
|
|
if (serverGroup->serverList().size() > 0)
|
|
settings.setServer(serverGroup->serverList()[0]);
|
|
}
|
|
|
|
connectTo(flag, settings);
|
|
}
|
|
|
|
void ConnectionManager::connectTo(Konversation::ConnectionFlag flag, ConnectionSettings& settings)
|
|
{
|
|
if (!settings.isValid()) return;
|
|
|
|
emit closeServerList();
|
|
|
|
if (flag != Konversation::CreateNewConnection
|
|
&& reuseExistingConnection(settings, (flag == Konversation::PromptToReuseConnection)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
IdentityPtr identity = settings.identity();
|
|
|
|
if (!identity || !validateIdentity(identity)) return;
|
|
|
|
KonversationApplication* konvApp = static_cast<KonversationApplication *>(kapp);
|
|
KonversationMainWindow* mainWindow = konvApp->getMainWindow();
|
|
|
|
Server* server = new Server(this, settings);
|
|
|
|
enlistConnection(server->connectionId(), server);
|
|
|
|
connect(server, TQT_SIGNAL(destroyed(int)), this, TQT_SLOT(delistConnection(int)));
|
|
|
|
connect(server, TQT_SIGNAL(connectionStateChanged(Server*, Konversation::ConnectionState)),
|
|
this, TQT_SLOT(handleConnectionStateChange(Server*, Konversation::ConnectionState)));
|
|
|
|
connect(server, TQT_SIGNAL(awayState(bool)), this, TQT_SIGNAL(connectionChangedAwayState(bool)));
|
|
|
|
connect(server, TQT_SIGNAL(nicksNowOnline(Server*, const TQStringList&, bool)),
|
|
mainWindow, TQT_SLOT(setOnlineList(Server*, const TQStringList&,bool)));
|
|
connect(server, TQT_SIGNAL(awayInsertRememberLine(Server*)),
|
|
mainWindow, TQT_SIGNAL(triggerRememberLines(Server*)));
|
|
|
|
connect(mainWindow, TQT_SIGNAL(startNotifyTimer(int)), server, TQT_SLOT(startNotifyTimer(int)));
|
|
|
|
connect(server, TQT_SIGNAL(multiServerCommand(const TQString&, const TQString&)),
|
|
konvApp, TQT_SLOT(sendMultiServerCommand(const TQString&, const TQString&)));
|
|
}
|
|
|
|
void ConnectionManager::enlistConnection(int connectionId, Server* server)
|
|
{
|
|
m_connectionList.insert(connectionId, server);
|
|
}
|
|
|
|
void ConnectionManager::delistConnection(int connectionId)
|
|
{
|
|
m_connectionList.remove(connectionId);
|
|
}
|
|
|
|
void ConnectionManager::handleConnectionStateChange(Server* server, Konversation::ConnectionState state)
|
|
{
|
|
emit connectionChangedState(server, state);
|
|
|
|
int identityId = server->getIdentity()->id();
|
|
|
|
if (state == Konversation::SSConnected)
|
|
{
|
|
if (!m_activeIdentities.contains(identityId))
|
|
{
|
|
m_activeIdentities.append(identityId);
|
|
|
|
emit identityOnline(identityId);
|
|
}
|
|
}
|
|
else if (state != Konversation::SSConnecting)
|
|
{
|
|
if (m_activeIdentities.contains(identityId))
|
|
{
|
|
m_activeIdentities.remove(identityId);
|
|
|
|
emit identityOffline(identityId);
|
|
}
|
|
}
|
|
|
|
if (state == Konversation::SSInvoluntarilyDisconnected)
|
|
{
|
|
// The asynchronous invocation of handleReconnect() makes sure that
|
|
// connectionChangedState() is emitted and delivered before it runs
|
|
// (and causes the next connection state change to occur).
|
|
emit requestReconnect(server);
|
|
}
|
|
}
|
|
|
|
void ConnectionManager::handleReconnect(Server* server)
|
|
{
|
|
if (!Preferences::autoReconnect()) return;
|
|
|
|
ConnectionSettings settings = server->getConnectionSettings();
|
|
|
|
uint reconnectCount = Preferences::reconnectCount();
|
|
|
|
// For server groups, one iteration over their server list shall count as one
|
|
// connection attempt.
|
|
if (settings.serverGroup())
|
|
reconnectCount = reconnectCount * settings.serverGroup()->serverList().size();
|
|
|
|
if (reconnectCount == 0 || settings.reconnectCount() < reconnectCount)
|
|
{
|
|
if (settings.serverGroup() && settings.serverGroup()->serverList().size() > 1)
|
|
{
|
|
Konversation::ServerList serverList = settings.serverGroup()->serverList();
|
|
|
|
int index = serverList.findIndex(settings.server());
|
|
int size = serverList.size();
|
|
|
|
if (index == size - 1 || index == -1)
|
|
settings.setServer(serverList[0]);
|
|
else if (index < size - 1)
|
|
settings.setServer(serverList[index+1]);
|
|
|
|
server->setConnectionSettings(settings);
|
|
|
|
server->getStatusView()->appendServerMessage(i18n("Info"),
|
|
i18n("Trying to connect to %1 in %2 seconds.")
|
|
.arg(settings.server().host())
|
|
.arg(Preferences::reconnectDelay()));
|
|
}
|
|
else
|
|
{
|
|
server->getStatusView()->appendServerMessage(i18n("Info"),
|
|
i18n("Trying to reconnect to %1 in %2 seconds.")
|
|
.arg(settings.server().host())
|
|
.arg(Preferences::reconnectDelay()));
|
|
}
|
|
|
|
server->getConnectionSettings().incrementReconnectCount();
|
|
|
|
TQTimer::singleShot(Preferences::reconnectDelay() * 1000, server, TQT_SLOT(connectToIRCServer()));
|
|
}
|
|
else
|
|
server->getStatusView()->appendServerMessage(i18n("Error"), i18n("Reconnection attempts exceeded."));
|
|
}
|
|
|
|
void ConnectionManager::quitServers()
|
|
{
|
|
TQMap<int, Server*>::ConstIterator it;
|
|
|
|
for (it = m_connectionList.begin(); it != m_connectionList.end(); ++it)
|
|
it.data()->quitServer();
|
|
}
|
|
|
|
void ConnectionManager::decodeIrcUrl(const TQString& url, ConnectionSettings& settings)
|
|
{
|
|
if (!url.startsWith("irc://")) return;
|
|
|
|
TQString mangledUrl = url;
|
|
|
|
mangledUrl.remove(TQRegExp("^irc:/+"));
|
|
|
|
if (mangledUrl.isEmpty()) return;
|
|
|
|
// Parsing address and channel.
|
|
TQStringList mangledUrlSegments;
|
|
|
|
mangledUrlSegments = TQStringList::split('/', mangledUrl, false);
|
|
|
|
// Check for ",isserver".
|
|
if (mangledUrlSegments[0].contains(','))
|
|
{
|
|
TQStringList addressSegments;
|
|
bool checkIfServerGroup = true;
|
|
|
|
addressSegments = TQStringList::split(',', mangledUrlSegments[0], false);
|
|
|
|
if (addressSegments.grep("isserver", false).size() > 0)
|
|
checkIfServerGroup = false;
|
|
|
|
decodeAddress(addressSegments[0], settings, checkIfServerGroup);
|
|
}
|
|
else
|
|
decodeAddress(mangledUrlSegments[0], settings);
|
|
|
|
TQString channel;
|
|
Konversation::ChannelSettings channelSettings;
|
|
|
|
// Grabbing channel from in front of potential ?key=value parameters.
|
|
if (mangledUrlSegments.size() > 1)
|
|
channel = mangledUrlSegments[1].section('?', 0, 0);
|
|
|
|
if (!channel.isEmpty())
|
|
{
|
|
// Add default prefix to channel if necessary.
|
|
if (!channel.contains(TQRegExp("^[#+&]{1}")))
|
|
channel = '#' + channel;
|
|
|
|
channelSettings.setName(channel);
|
|
}
|
|
|
|
// Parsing ?key=value parameters.
|
|
TQString parameterString;
|
|
|
|
if (mangledUrlSegments.size() > 1)
|
|
parameterString = mangledUrlSegments[1].section('?', 1);
|
|
|
|
if (parameterString.isEmpty() && mangledUrlSegments.size() > 2)
|
|
parameterString = mangledUrlSegments[2];
|
|
|
|
if (!parameterString.isEmpty())
|
|
{
|
|
TQRegExp parameterCatcher;
|
|
|
|
parameterCatcher.setPattern("pass=([^&]+)");
|
|
|
|
if (parameterCatcher.search(parameterString) != -1)
|
|
{
|
|
Konversation::ServerSettings server = settings.server();
|
|
|
|
server.setPassword(parameterCatcher.cap(1));
|
|
|
|
settings.setServer(server);
|
|
}
|
|
|
|
parameterCatcher.setPattern("key=([^&]+)");
|
|
|
|
if (parameterCatcher.search(parameterString) != -1)
|
|
channelSettings.setPassword(parameterCatcher.cap(1));
|
|
}
|
|
|
|
// Assigning channel.
|
|
if (!channelSettings.name().isEmpty())
|
|
settings.setInitialChannel(channelSettings);
|
|
}
|
|
|
|
void ConnectionManager::decodeAddress(const TQString& address, ConnectionSettings& settings,
|
|
bool checkIfServerGroup)
|
|
{
|
|
TQString host;
|
|
TQString port = "6667";
|
|
|
|
// Full-length IPv6 address with port
|
|
// Example: RFC 2732 notation: [2001:0DB8:0000:0000:0000:0000:1428:57ab]:6666
|
|
// Example: Non-RFC 2732 notation: 2001:0DB8:0000:0000:0000:0000:1428:57ab:6666
|
|
if (address.contains(':')==8)
|
|
{
|
|
host = address.section(':',0,-2).remove("[").remove("]");
|
|
port = address.section(':',-1);
|
|
}
|
|
// Full-length IPv6 address without port or not-full-length IPv6 address with port
|
|
// Example: Without port, RFC 2732 notation: [2001:0DB8:0000:0000:0000:0000:1428:57ab]
|
|
// Example: Without port, Non-RFC 2732 notation: 2001:0DB8:0000:0000:0000:0000:1428:57ab
|
|
// Example: With port, RFC 2732 notation: [2001:0DB8::1428:57ab]:6666
|
|
else if (address.contains(':')>=4)
|
|
{
|
|
// Last segment does not end with ], but the next to last does;
|
|
// Assume not-full-length IPv6 address with port
|
|
// Example: [2001:0DB8::1428:57ab]:6666
|
|
if (address.section(':',0,-2).endsWith("]") && !address.section(':',-1).endsWith("]"))
|
|
{
|
|
host = address.section(':',0,-2).remove("[").remove("]");
|
|
port = address.section(':',-1);
|
|
}
|
|
else
|
|
{
|
|
TQString addressCopy = address;
|
|
host = addressCopy.remove("[").remove("]");
|
|
}
|
|
}
|
|
// IPv4 address or ordinary hostname with port
|
|
// Example: IPv4 address with port: 123.123.123.123:6666
|
|
// Example: Hostname with port: irc.bla.org:6666
|
|
else if (address.contains(':')==1)
|
|
{
|
|
host = address.section(':',0,-2);
|
|
port = address.section(':',-1);
|
|
}
|
|
else
|
|
host = address;
|
|
|
|
// Try to assign server group.
|
|
if (checkIfServerGroup && Preferences::isServerGroup(host))
|
|
{
|
|
// If host is found to be the name of a server group.
|
|
|
|
int serverGroupId = Preferences::serverGroupIdByName(host);
|
|
|
|
Konversation::ServerGroupSettingsPtr serverGroup;
|
|
|
|
serverGroup = Preferences::serverGroupById(serverGroupId);
|
|
|
|
settings.setServerGroup(serverGroup);
|
|
|
|
if (serverGroup->serverList().size() > 0)
|
|
settings.setServer(serverGroup->serverList()[0]);
|
|
}
|
|
else
|
|
{
|
|
if (Preferences::serverGroupByServer(host))
|
|
{
|
|
// If the host is found to be part of a server group's server list.
|
|
|
|
Konversation::ServerGroupSettingsPtr serverGroup;
|
|
|
|
serverGroup = Preferences::serverGroupByServer(host);
|
|
|
|
settings.setServerGroup(serverGroup);
|
|
}
|
|
|
|
Konversation::ServerSettings server;
|
|
|
|
server.setHost(host);
|
|
server.setPort(port.toInt());
|
|
|
|
settings.setServer(server);
|
|
}
|
|
}
|
|
|
|
bool ConnectionManager::reuseExistingConnection(ConnectionSettings& settings, bool interactive)
|
|
{
|
|
Server* dupe = 0;
|
|
ConnectionDupe dupeType;
|
|
bool doReuse = true;
|
|
|
|
KonversationApplication* konvApp = static_cast<KonversationApplication *>(kapp);
|
|
KonversationMainWindow* mainWindow = konvApp->getMainWindow();
|
|
|
|
TQMap<int, Server*>::ConstIterator it;
|
|
|
|
for (it = m_connectionList.begin(); it != m_connectionList.end(); ++it)
|
|
{
|
|
if (it.data()->getServerGroup() && settings.serverGroup()
|
|
&& it.data()->getServerGroup() == settings.serverGroup())
|
|
{
|
|
dupe = it.data();
|
|
dupeType = SameServerGroup;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!dupe)
|
|
{
|
|
for (it = m_connectionList.begin(); it != m_connectionList.end(); ++it)
|
|
{
|
|
if (it.data()->getConnectionSettings().server() == settings.server())
|
|
{
|
|
dupe = it.data();
|
|
dupeType = SameServer;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dupe && interactive)
|
|
{
|
|
int result = KMessageBox::warningContinueCancel(
|
|
mainWindow,
|
|
i18n("You are already connected to %1. Do you want to open another connection?")
|
|
.arg(dupe->getDisplayName()),
|
|
i18n("Already connected to %1").arg(dupe->getDisplayName()),
|
|
i18n("Create connection"),
|
|
"ReuseExistingConnection");
|
|
|
|
if (result == KMessageBox::Continue) doReuse = false;
|
|
}
|
|
|
|
if (dupe && doReuse)
|
|
{
|
|
if (interactive && dupeType == SameServerGroup
|
|
&& !(dupe->getConnectionSettings().server() == settings.server()))
|
|
{
|
|
int result = KMessageBox::warningContinueCancel(
|
|
mainWindow,
|
|
i18n("You are presently connected to %1 via '%2' (port %3). Do you want to switch to '%4' (port %5) instead?")
|
|
.arg(dupe->getDisplayName())
|
|
.arg(dupe->getServerName())
|
|
.arg(dupe->getPort())
|
|
.arg(settings.server().host())
|
|
.arg(settings.server().port()),
|
|
i18n("Already connected to %1").arg(dupe->getDisplayName()),
|
|
i18n("Switch Server"),
|
|
"ReconnectWithDifferentServer");
|
|
|
|
if (result == KMessageBox::Continue)
|
|
{
|
|
dupe->disconnect();
|
|
|
|
dupe->setConnectionSettings(settings);
|
|
}
|
|
}
|
|
|
|
if (!dupe->isConnected())
|
|
{
|
|
if (!settings.initialChannel().name().isEmpty())
|
|
dupe->updateAutoJoin(settings.initialChannel());
|
|
|
|
if (!dupe->isConnecting())
|
|
dupe->reconnect();
|
|
}
|
|
else
|
|
{
|
|
if (!settings.initialChannel().name().isEmpty())
|
|
{
|
|
dupe->sendJoinCommand(settings.initialChannel().name(),
|
|
settings.initialChannel().password());
|
|
}
|
|
}
|
|
}
|
|
|
|
return (dupe && doReuse);
|
|
}
|
|
|
|
bool ConnectionManager::validateIdentity(IdentityPtr identity, bool interactive)
|
|
{
|
|
KonversationApplication* konvApp = static_cast<KonversationApplication *>(kapp);
|
|
KonversationMainWindow* mainWindow = konvApp->getMainWindow();
|
|
|
|
TQString errors;
|
|
|
|
if (identity->getIdent().isEmpty())
|
|
errors+=i18n("Please fill in your <b>Ident</b>.<br>");
|
|
|
|
if (identity->getRealName().isEmpty())
|
|
errors+=i18n("Please fill in your <b>Real name</b>.<br>");
|
|
|
|
if (identity->getNickname(0).isEmpty())
|
|
errors+=i18n("Please provide at least one <b>Nickname</b>.<br>");
|
|
|
|
if (!errors.isEmpty())
|
|
{
|
|
if (interactive)
|
|
{
|
|
int result = KMessageBox::warningContinueCancel(mainWindow,
|
|
i18n("<qt>Your identity \"%1\" is not set up correctly:<br>%2</qt>")
|
|
.arg(identity->getName()).arg(errors),
|
|
i18n("Identity Settings"),
|
|
i18n("Edit Identity..."));
|
|
|
|
if (result == KMessageBox::Continue)
|
|
{
|
|
identity = mainWindow->editIdentity(identity);
|
|
|
|
if (identity && validateIdentity(identity, false))
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
TQPtrList<Server> ConnectionManager::getServerList()
|
|
{
|
|
TQPtrList<Server> serverList;
|
|
|
|
TQMap<int, Server*>::ConstIterator it;
|
|
|
|
for (it = m_connectionList.begin(); it != m_connectionList.end(); ++it)
|
|
serverList.append(it.data());
|
|
|
|
return serverList;
|
|
}
|
|
|
|
Server* ConnectionManager::getServerByConnectionId(int connectionId)
|
|
{
|
|
if (m_connectionList.contains(connectionId))
|
|
return m_connectionList[connectionId];
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
Server* ConnectionManager::getServerByName(const TQString& name)
|
|
{
|
|
TQMap<int, Server*>::ConstIterator it;
|
|
|
|
for (it = m_connectionList.begin(); it != m_connectionList.end(); ++it)
|
|
{
|
|
if (it.data()->getDisplayName() == name || it.data()->getServerName() == name)
|
|
return it.data();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
Server* ConnectionManager::getAnyServer()
|
|
{
|
|
if ( m_connectionList.count() > 0)
|
|
return m_connectionList[0];
|
|
|
|
return 0;
|
|
}
|
|
|
|
#include "connectionmanager.moc"
|