+#include "corenetworkconfig.h"
+#include "coresession.h"
+#include "coreuserinputhandler.h"
+#include "ircencoder.h"
+#include "irccap.h"
+#include "irctag.h"
+#include "networkevent.h"
+
+CoreNetwork::CoreNetwork(const NetworkId& networkid, CoreSession* session)
+ : Network(networkid, session)
+ , _coreSession(session)
+ , _userInputHandler(new CoreUserInputHandler(this))
+ , _metricsServer(Core::instance()->metricsServer())
+ , _autoReconnectCount(0)
+ , _quitRequested(false)
+ , _disconnectExpected(false)
+ , _previousConnectionAttemptFailed(false)
+ , _lastUsedServerIndex(0)
+ , _requestedUserModes('-')
+{
+ // Check if raw IRC logging is enabled
+ _debugLogRawIrc = (Quassel::isOptionSet("debug-irc") || Quassel::isOptionSet("debug-irc-id"));
+ _debugLogRawNetId = Quassel::optionValue("debug-irc-id").toInt();
+
+ _autoReconnectTimer.setSingleShot(true);
+ connect(&_socketCloseTimer, &QTimer::timeout, this, &CoreNetwork::onSocketCloseTimeout);
+
+ setPingInterval(networkConfig()->pingInterval());
+ connect(&_pingTimer, &QTimer::timeout, this, &CoreNetwork::sendPing);
+
+ setAutoWhoDelay(networkConfig()->autoWhoDelay());
+ setAutoWhoInterval(networkConfig()->autoWhoInterval());
+
+ QHash<QString, QString> channels = coreSession()->persistentChannels(networkId());
+ for (const QString& chan : channels.keys()) {
+ _channelKeys[chan.toLower()] = channels[chan];
+ }
+
+ QHash<QString, QByteArray> bufferCiphers = coreSession()->bufferCiphers(networkId());
+ for (const QString& buffer : bufferCiphers.keys()) {
+ storeChannelCipherKey(buffer.toLower(), bufferCiphers[buffer]);
+ }
+
+ connect(networkConfig(), &NetworkConfig::pingTimeoutEnabledSet, this, &CoreNetwork::enablePingTimeout);
+ connect(networkConfig(), &NetworkConfig::pingIntervalSet, this, &CoreNetwork::setPingInterval);
+ connect(networkConfig(), &NetworkConfig::autoWhoEnabledSet, this, &CoreNetwork::setAutoWhoEnabled);
+ connect(networkConfig(), &NetworkConfig::autoWhoIntervalSet, this, &CoreNetwork::setAutoWhoInterval);
+ connect(networkConfig(), &NetworkConfig::autoWhoDelaySet, this, &CoreNetwork::setAutoWhoDelay);
+
+ connect(&_autoReconnectTimer, &QTimer::timeout, this, &CoreNetwork::doAutoReconnect);
+ connect(&_autoWhoTimer, &QTimer::timeout, this, &CoreNetwork::sendAutoWho);
+ connect(&_autoWhoCycleTimer, &QTimer::timeout, this, &CoreNetwork::startAutoWhoCycle);
+ connect(&_tokenBucketTimer, &QTimer::timeout, this, &CoreNetwork::checkTokenBucket);
+
+ connect(&socket, &QAbstractSocket::connected, this, &CoreNetwork::onSocketInitialized);
+ connect(&socket, selectOverload<QAbstractSocket::SocketError>(&QAbstractSocket::error), this, &CoreNetwork::onSocketError);
+ connect(&socket, &QAbstractSocket::stateChanged, this, &CoreNetwork::onSocketStateChanged);
+ connect(&socket, &QIODevice::readyRead, this, &CoreNetwork::onSocketHasData);
+ connect(&socket, &QSslSocket::encrypted, this, &CoreNetwork::onSocketInitialized);
+ connect(&socket, selectOverload<const QList<QSslError>&>(&QSslSocket::sslErrors), this, &CoreNetwork::onSslErrors);
+ connect(this, &CoreNetwork::newEvent, coreSession()->eventManager(), &EventManager::postEvent);
+
+ // Custom rate limiting
+ // These react to the user changing settings in the client
+ connect(this, &Network::useCustomMessageRateSet, this, &CoreNetwork::updateRateLimiting);
+ connect(this, &Network::messageRateBurstSizeSet, this, &CoreNetwork::updateRateLimiting);
+ connect(this, &Network::messageRateDelaySet, this, &CoreNetwork::updateRateLimiting);
+ connect(this, &Network::unlimitedMessageRateSet, this, &CoreNetwork::updateRateLimiting);
+
+ // IRCv3 capability handling
+ // These react to CAP messages from the server
+ connect(this, &Network::capAdded, this, &CoreNetwork::serverCapAdded);
+ connect(this, &Network::capAcknowledged, this, &CoreNetwork::serverCapAcknowledged);
+ connect(this, &Network::capRemoved, this, &CoreNetwork::serverCapRemoved);
+
+ if (Quassel::isOptionSet("oidentd")) {
+ connect(this, &CoreNetwork::socketInitialized, Core::instance()->oidentdConfigGenerator(), &OidentdConfigGenerator::addSocket);
+ connect(this, &CoreNetwork::socketDisconnected, Core::instance()->oidentdConfigGenerator(), &OidentdConfigGenerator::removeSocket);
+ }
+
+ if (Quassel::isOptionSet("ident-daemon")) {
+ connect(this, &CoreNetwork::socketInitialized, Core::instance()->identServer(), &IdentServer::addSocket);
+ connect(this, &CoreNetwork::socketDisconnected, Core::instance()->identServer(), &IdentServer::removeSocket);
+ }
+}
+
+CoreNetwork::~CoreNetwork()
+{
+ // Ensure we don't get any more signals from the socket while shutting down
+ disconnect(&socket, nullptr, this, nullptr);
+ if (!forceDisconnect()) {
+ qWarning() << QString{"Could not disconnect from network %1 (network ID: %2, user ID: %3)"}
+ .arg(networkName())
+ .arg(networkId().toInt())
+ .arg(userId().toInt());
+ }
+}
+
+bool CoreNetwork::forceDisconnect(int msecs)
+{
+ if (socket.state() == QAbstractSocket::UnconnectedState) {
+ // Socket already disconnected.
+ return true;
+ }
+ // Request a socket-level disconnect if not already happened
+ socket.disconnectFromHost();
+ if (socket.state() != QAbstractSocket::UnconnectedState) {
+ return socket.waitForDisconnected(msecs);
+ }
+ return true;
+}
+
+QString CoreNetwork::channelDecode(const QString& bufferName, const QByteArray& string) const
+{
+ if (!bufferName.isEmpty()) {
+ IrcChannel* channel = ircChannel(bufferName);
+ if (channel)
+ return channel->decodeString(string);
+ }
+ return decodeString(string);
+}
+
+QString CoreNetwork::userDecode(const QString& userNick, const QByteArray& string) const
+{
+ IrcUser* user = ircUser(userNick);
+ if (user)
+ return user->decodeString(string);
+ return decodeString(string);
+}
+
+QByteArray CoreNetwork::channelEncode(const QString& bufferName, const QString& string) const
+{
+ if (!bufferName.isEmpty()) {
+ IrcChannel* channel = ircChannel(bufferName);
+ if (channel)
+ return channel->encodeString(string);
+ }
+ return encodeString(string);
+}
+
+QByteArray CoreNetwork::userEncode(const QString& userNick, const QString& string) const
+{
+ IrcUser* user = ircUser(userNick);
+ if (user)
+ return user->encodeString(string);
+ return encodeString(string);
+}
+
+void CoreNetwork::connectToIrc(bool reconnecting)
+{
+ if (_shuttingDown) {
+ return;
+ }
+
+ if (Core::instance()->identServer()) {
+ _socketId = Core::instance()->identServer()->addWaitingSocket();
+ }