X-Git-Url: https://git.quassel-irc.org/?a=blobdiff_plain;f=src%2Fcore%2Fcorenetwork.cpp;h=ecf2a2d9431ca5dbe153b9daf37450326e7bde6d;hb=adc18a7284e7124639fa4b354251d6b102dcf6b7;hp=c42325f9173fd2f632977901bf7dc7259809d344;hpb=b8edbda019eeb99da8663193e224efc9d1265dc7;p=quassel.git diff --git a/src/core/corenetwork.cpp b/src/core/corenetwork.cpp index c42325f9..ecf2a2d9 100644 --- a/src/core/corenetwork.cpp +++ b/src/core/corenetwork.cpp @@ -158,6 +158,11 @@ void CoreNetwork::connectToIrc(bool reconnecting) // cleaning up old quit reason _quitReason.clear(); + // reset capability negotiation in case server changes during a reconnect + _capsQueued.clear(); + _capsPending.clear(); + _capsSupported.clear(); + // use a random server? if (useRandomServer()) { _lastUsedServerIndex = qrand() % serverList().size(); @@ -457,16 +462,24 @@ void CoreNetwork::socketInitialized() return; } - emit socketOpen(identity, localAddress(), localPort(), peerAddress(), peerPort()); - Server server = usedServer(); + #ifdef HAVE_SSL - if (server.useSsl && !socket.isEncrypted()) + // 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()) { + emit socketInitialized(identity, localAddress(), localPort(), peerAddress(), peerPort()); + } + + 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()); #endif - socket.setSocketOption(QAbstractSocket::KeepAliveOption, true); - emit socketInitialized(identity, localAddress(), localPort(), peerAddress(), peerPort()); + socket.setSocketOption(QAbstractSocket::KeepAliveOption, true); // TokenBucket to avoid sending too much at once _messageDelay = 2200; // this seems to be a safe value (2.2 seconds delay) @@ -474,9 +487,11 @@ void CoreNetwork::socketInitialized() _tokenBucket = _burstSize; // init with a full bucket _tokenBucketTimer.start(_messageDelay); - if (networkInfo().useSasl) { - putRawLine(serverEncode(QString("CAP REQ :sasl"))); - } + // Request capabilities as per IRCv3.2 specifications + // Older servers should ignore this; newer servers won't downgrade to RFC1459 + displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("Requesting capability list...")); + putRawLine(serverEncode(QString("CAP LS 302"))); + if (!server.password.isEmpty()) { putRawLine(serverEncode(QString("PASS %1").arg(server.password))); } @@ -488,7 +503,7 @@ void CoreNetwork::socketInitialized() else { nick = identity->nicks()[0]; } - putRawLine(serverEncode(QString("NICK :%1").arg(nick))); + putRawLine(serverEncode(QString("NICK %1").arg(nick))); putRawLine(serverEncode(QString("USER %1 8 * :%2").arg(identity->ident(), identity->realName()))); } @@ -850,6 +865,73 @@ void CoreNetwork::setPingInterval(int interval) _pingTimer.setInterval(interval * 1000); } +/******** IRCv3 Capability Negotiation ********/ + +void CoreNetwork::addCap(const QString &capability, const QString &value) +{ + // Clear from pending list, add to supported list + if (!_capsSupported.contains(capability)) { + if (value != "") { + // Value defined, just use it + _capsSupported[capability] = value; + } else if (_capsPending.contains(capability)) { + // Value not defined, but a pending capability had a value. + // E.g. CAP * LS :sasl=PLAIN multi-prefix + // Preserve the capability value for later use. + _capsSupported[capability] = _capsPending[capability]; + } else { + // No value ever given, assign to blank + _capsSupported[capability] = QString(); + } + } + if (_capsPending.contains(capability)) + _capsPending.remove(capability); + + // Handle special cases here + // TODO Use events if it makes sense +} + +void CoreNetwork::removeCap(const QString &capability) +{ + // Clear from pending list, remove from supported list + if (_capsPending.contains(capability)) + _capsPending.remove(capability); + if (_capsSupported.contains(capability)) + _capsSupported.remove(capability); + + // Handle special cases here + // TODO Use events if it makes sense +} + +QString CoreNetwork::capValue(const QString &capability) const +{ + // If a supported capability exists, good; if not, return pending value. + // If capability isn't supported after all, the pending entry will be removed. + if (_capsSupported.contains(capability)) + return _capsSupported[capability]; + else if (_capsPending.contains(capability)) + return _capsPending[capability]; + else + return QString(); +} + +void CoreNetwork::queuePendingCap(const QString &capability, const QString &value) +{ + if (!_capsQueued.contains(capability)) { + _capsQueued.append(capability); + // Some capabilities may have values attached, preserve them as pending + _capsPending[capability] = value; + } +} + +QString CoreNetwork::takeQueuedCap() +{ + if (!_capsQueued.empty()) { + return _capsQueued.takeFirst(); + } else { + return QString(); + } +} /******** AutoWHO ********/