#include "corenetwork.h"
+#include <algorithm>
+
#include <QDebug>
#include <QHostInfo>
#include <QTextBoundaryFinder>
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
// 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)
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);
void CoreNetwork::serverCapAdded(const QString& capability)
{
+ // Exclude skipped capabilities
+ if (skipCaps().contains(capability)) {
+ return;
+ }
+
// Check if it's a known capability; if so, add it to the list
// Handle special cases first
if (capability == IrcCap::SASL) {
// Only request SASL if it's enabled
- if (networkInfo().useSasl)
+ if (useSasl()) {
queueCap(capability);
+ }
}
else if (IrcCap::knownCaps.contains(capability)) {
// Handling for general known capabilities
}
// Handle capabilities that require further messages sent to the IRC server
- // If you change this list, ALSO change the list in CoreNetwork::capsRequiringServerMessages
+ // If you change this list, ALSO change the list in CoreNetwork::capsRequiringConfiguration
if (capability == IrcCap::SASL) {
// 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()
{
+ // Check if any available capabilities have been disabled
+ QStringList capsSkipped;
+ if (!skipCaps().isEmpty() && !caps().isEmpty()) {
+ // Find the entries that are common to skipCaps() and caps(). This represents any
+ // capabilities supported by the server that were skipped.
+
+ // Both skipCaps() and caps() are already lowercase
+ // std::set_intersection requires sorted lists, and we can't modify the original lists.
+ //
+ // skipCaps() should already be sorted. caps() is intentionally not sorted elsewhere so
+ // Quassel can show the capabilities in the order transmitted by the network.
+ auto sortedCaps = caps();
+ sortedCaps.sort();
+
+ // Find the intersection between skipped caps and server-supplied caps
+ std::set_intersection(skipCaps().cbegin(), skipCaps().cend(),
+ sortedCaps.cbegin(), sortedCaps.cend(),
+ std::back_inserter(capsSkipped));
+ }
+
if (!capsPendingNegotiation()) {
// No capabilities are queued for request, determine the reason why
QString capStatusMsg;
capStatusMsg
));
+ if (!capsSkipped.isEmpty()) {
+ // Mention that some capabilities are skipped
+ showMessage(NetworkInternalMessage(
+ Message::Server,
+ BufferInfo::StatusBuffer,
+ "",
+ tr("Quassel is configured to ignore some capabilities (skipped: %1)").arg(capsSkipped.join(", "))
+ ));
+ }
+
// End any ongoing capability negotiation, allowing connection to continue
endCapNegotiation();
return;
tr("Ready to negotiate (found: %1)").arg(caps().join(", "))
));
+ if (!capsSkipped.isEmpty()) {
+ // Mention that some capabilities are skipped
+ showMessage(NetworkInternalMessage(
+ Message::Server,
+ BufferInfo::StatusBuffer,
+ "",
+ tr("Quassel is configured to ignore some capabilities (skipped: %1)").arg(capsSkipped.join(", "))
+ ));
+ }
+
// Build a list of queued capabilities, starting with individual, then bundled, only adding the
// comma separator between the two if needed (both individual and bundled caps exist).
QString queuedCapsDisplay = _capsQueuedIndividual.join(", ")
else {
// No pending desired capabilities, capability negotiation finished
// If SASL requested but not available, print a warning
- if (networkInfo().useSasl && !capEnabled(IrcCap::SASL))
+ if (useSasl() && !capEnabled(IrcCap::SASL))
showMessage(NetworkInternalMessage(
Message::Error,
BufferInfo::StatusBuffer,
}
}
-#ifdef HAVE_SSL
void CoreNetwork::onSslErrors(const QList<QSslError>& sslErrors)
{
Server server = usedServer();
}
}
-#endif // HAVE_SSL
-
void CoreNetwork::checkTokenBucket()
{
if (_skipMessageRates) {