/***************************************************************************
- * Copyright (C) 2005-2019 by the Quassel Project *
+ * Copyright (C) 2005-2020 by the Quassel Project *
* devel@quassel-irc.org *
* *
* This program is free software; you can redistribute it and/or modify *
#include <QDebug>
#include <QHostInfo>
+#include <QTextBoundaryFinder>
#include "core.h"
#include "coreidentity.h"
: Network(networkid, session)
, _coreSession(session)
, _userInputHandler(new CoreUserInputHandler(this))
+ , _metricsServer(Core::instance()->metricsServer())
, _autoReconnectCount(0)
, _quitRequested(false)
, _disconnectExpected(false)
connect(&socket, selectOverload<QAbstractSocket::SocketError>(&QAbstractSocket::error), this, &CoreNetwork::onSocketError);
connect(&socket, &QAbstractSocket::stateChanged, this, &CoreNetwork::onSocketStateChanged);
connect(&socket, &QIODevice::readyRead, this, &CoreNetwork::onSocketHasData);
-#ifdef HAVE_SSL
connect(&socket, &QSslSocket::encrypted, this, &CoreNetwork::onSocketInitialized);
connect(&socket, selectOverload<const QList<QSslError>&>(&QSslSocket::sslErrors), this, &CoreNetwork::onSslErrors);
-#endif
connect(this, &CoreNetwork::newEvent, coreSession()->eventManager(), &EventManager::postEvent);
// Custom rate limiting
_socketId = Core::instance()->identServer()->addWaitingSocket();
}
+ if (_metricsServer) {
+ _metricsServer->addNetwork(userId());
+ }
+
if (!reconnecting && useAutoReconnect() && _autoReconnectCount == 0) {
_autoReconnectTimer.setInterval(autoReconnectInterval() * 1000);
if (unlimitedReconnectRetries())
// hostname of the server. Qt's DNS cache also isn't used by the proxy so we don't need to refresh the entry.
QHostInfo::fromName(server.host);
}
-#ifdef HAVE_SSL
if (server.useSsl) {
CoreIdentity* identity = identityPtr();
if (identity) {
else {
socket.connectToHost(server.host, server.port);
}
-#else
- socket.connectToHost(server.host, server.port);
-#endif
}
void CoreNetwork::disconnectFromIrc(bool requested, const QString& reason, bool withReconnect)
}
disablePingTimeout();
_msgQueue.clear();
+ if (_metricsServer) {
+ _metricsServer->messageQueue(userId(), 0);
+ }
IrcUser* me_ = me();
if (me_) {
// Add to back, waiting in order
_msgQueue.append(s);
}
+ if (_metricsServer) {
+ _metricsServer->messageQueue(userId(), _msgQueue.size());
+ }
}
}
{
while (socket.canReadLine()) {
QByteArray s = socket.readLine();
+ if (_metricsServer) {
+ _metricsServer->receiveDataNetwork(userId(), s.size());
+ }
if (s.endsWith("\r\n"))
s.chop(2);
else if (s.endsWith("\n"))
Server server = usedServer();
-#ifdef HAVE_SSL
// Non-SSL connections enter here only once, always emit socketInitialized(...) in these cases
// SSL connections call socketInitialized() twice, only emit socketInitialized(...) on the first (not yet encrypted) run
if (!server.useSsl || !socket.isEncrypted()) {
// We'll finish setup once we're encrypted, and called again
return;
}
-#else
- emit socketInitialized(identity, localAddress(), localPort(), peerAddress(), peerPort(), _socketId);
-#endif
socket.setSocketOption(QAbstractSocket::KeepAliveOption, true);
{
disablePingTimeout();
_msgQueue.clear();
+ if (_metricsServer) {
+ _metricsServer->messageQueue(userId(), 0);
+ }
_autoWhoCycleTimer.stop();
_autoWhoTimer.stop();
else
_autoReconnectTimer.start();
}
+
+ if (_metricsServer) {
+ _metricsServer->removeNetwork(userId());
+ }
}
void CoreNetwork::onSocketStateChanged(QAbstractSocket::SocketState socketState)
// If SASL mechanisms specified, limit to what's accepted for authentication
// if the current identity has a cert set, use SASL EXTERNAL
// FIXME use event
-#ifdef HAVE_SSL
if (!identityPtr()->sslCert().isNull()) {
if (saslMaybeSupports(IrcCap::SaslMech::EXTERNAL)) {
// EXTERNAL authentication supported, send request
}
}
else {
-#endif
if (saslMaybeSupports(IrcCap::SaslMech::PLAIN)) {
// PLAIN authentication supported, send request
// Only working with PLAIN atm, blowfish later
));
sendNextCap();
}
-#ifdef HAVE_SSL
}
-#endif
}
}
void CoreNetwork::beginCapNegotiation()
{
- // Don't begin negotiation if no capabilities are queued to request
- if (!capNegotiationInProgress()) {
- // If the server doesn't have any capabilities, but supports CAP LS, continue on with the
- // normal connection.
+ if (!capsPendingNegotiation()) {
+ // No capabilities are queued for request, determine the reason why
+ QString capStatusMsg;
+ if (caps().empty()) {
+ // The server doesn't provide any capabilities, but supports CAP LS
+ capStatusMsg = tr("No capabilities available");
+ }
+ else if (capsEnabled().empty()) {
+ // The server supports capabilities (caps() is not empty) but Quassel doesn't support
+ // anything offered. This should be uncommon.
+ capStatusMsg =
+ tr("None of the capabilities provided by the server are supported (found: %1)")
+ .arg(caps().join(", "));
+ }
+ else {
+ // Quassel has enabled some capabilities, but there are no further capabilities that can
+ // be negotiated.
+ // (E.g. the user has manually run "/cap ls 302" after initial negotiation.)
+ capStatusMsg =
+ tr("No additional capabilities are supported (found: %1; currently enabled: %2)")
+ .arg(caps().join(", "), capsEnabled().join(", "));
+ }
+ // Inform the user of the situation
showMessage(NetworkInternalMessage(
Message::Server,
BufferInfo::StatusBuffer,
"",
- tr("No capabilities available")
+ capStatusMsg
));
+
+ // End any ongoing capability negotiation, allowing connection to continue
endCapNegotiation();
return;
}
void CoreNetwork::sendNextCap()
{
- if (capNegotiationInProgress()) {
+ if (capsPendingNegotiation()) {
// Request the next set of capabilities and remove them from the list
putRawLine(serverEncode(QString("CAP REQ :%1").arg(takeQueuedCaps())));
}
// And https://github.com/quakenet/snircd/blob/master/doc/readme.who
// And https://github.com/hexchat/hexchat/blob/57478b65758e6b697b1d82ce21075e74aa475efc/src/common/proto-irc.c#L752
putRawLine(serverEncode(
- QString("WHO %1 n%chtsunfra,%2").arg(serverEncode(chanOrNick), QString::number(IrcCap::ACCOUNT_NOTIFY_WHOX_NUM))));
+ QString("WHO %1 n%chtsunfra,%2")
+ .arg(chanOrNick, QString::number(IrcCap::ACCOUNT_NOTIFY_WHOX_NUM))
+ ));
}
else {
// Fall back to normal WHO
}
}
-#ifdef HAVE_SSL
void CoreNetwork::onSslErrors(const QList<QSslError>& sslErrors)
{
Server server = usedServer();
}
}
-#endif // HAVE_SSL
-
void CoreNetwork::checkTokenBucket()
{
if (_skipMessageRates) {
// As long as there's tokens available and messages remaining, sending messages from the queue
while (!_msgQueue.empty() && _tokenBucket > 0) {
writeToSocket(_msgQueue.takeFirst());
+ if (_metricsServer) {
+ _metricsServer->messageQueue(userId(), _msgQueue.size());
+ }
}
}
}
socket.write(data);
socket.write("\r\n");
+ if (_metricsServer) {
+ _metricsServer->transmitDataNetwork(userId(), data.size() + 2);
+ }
if (!_skipMessageRates) {
// Only subtract from the token bucket if message rate limiting is enabled
_tokenBucket--;