From: Chris Fuenty Date: Sat, 13 Feb 2010 12:43:30 +0000 (+0100) Subject: SASL support X-Git-Tag: 0.6-beta1~28 X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=commitdiff_plain;h=e4972a0ebce0d555d752ab2f34ffd6a6dcdef1dd SASL support --- diff --git a/src/common/network.cpp b/src/common/network.cpp index 0dae357e..67ebe915 100644 --- a/src/common/network.cpp +++ b/src/common/network.cpp @@ -47,6 +47,7 @@ Network::Network(const NetworkId &networkid, QObject *parent) _prefixModes(QString()), _useRandomServer(false), _useAutoIdentify(false), + _useSasl(false), _useAutoReconnect(false), _autoReconnectInterval(60), _autoReconnectRetries(10), @@ -87,6 +88,9 @@ NetworkInfo Network::networkInfo() const { info.useAutoIdentify = useAutoIdentify(); info.autoIdentifyService = autoIdentifyService(); info.autoIdentifyPassword = autoIdentifyPassword(); + info.useSasl = useSasl(); + info.saslAccount = saslAccount(); + info.saslPassword = saslPassword(); info.useAutoReconnect = useAutoReconnect(); info.autoReconnectInterval = autoReconnectInterval(); info.autoReconnectRetries = autoReconnectRetries(); @@ -108,6 +112,9 @@ void Network::setNetworkInfo(const NetworkInfo &info) { if(info.useAutoIdentify != useAutoIdentify()) setUseAutoIdentify(info.useAutoIdentify); if(info.autoIdentifyService != autoIdentifyService()) setAutoIdentifyService(info.autoIdentifyService); if(info.autoIdentifyPassword != autoIdentifyPassword()) setAutoIdentifyPassword(info.autoIdentifyPassword); + if(info.useSasl != useSasl()) setUseSasl(info.useSasl); + if(info.saslAccount != saslAccount()) setSaslAccount(info.saslAccount); + if(info.saslPassword != saslPassword()) setSaslPassword(info.saslPassword); if(info.useAutoReconnect != useAutoReconnect()) setUseAutoReconnect(info.useAutoReconnect); if(info.autoReconnectInterval != autoReconnectInterval()) setAutoReconnectInterval(info.autoReconnectInterval); if(info.autoReconnectRetries != autoReconnectRetries()) setAutoReconnectRetries(info.autoReconnectRetries); @@ -566,6 +573,24 @@ void Network::setAutoIdentifyPassword(const QString &password) { emit configChanged(); } +void Network::setUseSasl(bool use) { + _useSasl = use; + SYNC(ARG(use)) + emit configChanged(); +} + +void Network::setSaslAccount(const QString &account) { + _saslAccount = account; + SYNC(ARG(account)) + emit configChanged(); +} + +void Network::setSaslPassword(const QString &password) { + _saslPassword = password; + SYNC(ARG(password)) + emit configChanged(); +} + void Network::setUseAutoReconnect(bool use) { _useAutoReconnect = use; SYNC(ARG(use)) @@ -758,6 +783,7 @@ NetworkInfo::NetworkInfo() useRandomServer(false), useAutoIdentify(false), autoIdentifyService("NickServ"), + useSasl(false), useAutoReconnect(true), autoReconnectInterval(60), autoReconnectRetries(20), @@ -780,6 +806,9 @@ bool NetworkInfo::operator==(const NetworkInfo &other) const { if(useAutoIdentify != other.useAutoIdentify) return false; if(autoIdentifyService != other.autoIdentifyService) return false; if(autoIdentifyPassword != other.autoIdentifyPassword) return false; + if(useSasl != other.useSasl) return false; + if(saslAccount != other.saslAccount) return false; + if(saslPassword != other.saslPassword) return false; if(useAutoReconnect != other.useAutoReconnect) return false; if(autoReconnectInterval != other.autoReconnectInterval) return false; if(autoReconnectRetries != other.autoReconnectRetries) return false; @@ -806,6 +835,9 @@ QDataStream &operator<<(QDataStream &out, const NetworkInfo &info) { i["UseAutoIdentify"] = info.useAutoIdentify; i["AutoIdentifyService"] = info.autoIdentifyService; i["AutoIdentifyPassword"] = info.autoIdentifyPassword; + i["UseSasl"] = info.useSasl; + i["SaslAccount"] = info.saslAccount; + i["SaslPassword"] = info.saslPassword; i["UseAutoReconnect"] = info.useAutoReconnect; i["AutoReconnectInterval"] = info.autoReconnectInterval; i["AutoReconnectRetries"] = info.autoReconnectRetries; @@ -830,6 +862,9 @@ QDataStream &operator>>(QDataStream &in, NetworkInfo &info) { info.useAutoIdentify = i["UseAutoIdentify"].toBool(); info.autoIdentifyService = i["AutoIdentifyService"].toString(); info.autoIdentifyPassword = i["AutoIdentifyPassword"].toString(); + info.useSasl = i["UseSasl"].toBool(); + info.saslAccount = i["SaslAccount"].toString(); + info.saslPassword = i["SaslPassword"].toString(); info.useAutoReconnect = i["UseAutoReconnect"].toBool(); info.autoReconnectInterval = i["AutoReconnectInterval"].toUInt(); info.autoReconnectRetries = i["AutoReconnectRetries"].toInt(); @@ -843,7 +878,8 @@ QDebug operator<<(QDebug dbg, const NetworkInfo &i) { << " codecForServer = " << i.codecForServer << " codecForEncoding = " << i.codecForEncoding << " codecForDecoding = " << i.codecForDecoding << " serverList = " << i.serverList << " useRandomServer = " << i.useRandomServer << " perform = " << i.perform << " useAutoIdentify = " << i.useAutoIdentify << " autoIdentifyService = " << i.autoIdentifyService << " autoIdentifyPassword = " << i.autoIdentifyPassword - << " useAutoReconnect = " << i.useAutoReconnect << " autoReconnectInterval = " << i.autoReconnectInterval + << " useSasl = " << i.useSasl << " saslAccount = " << i.saslAccount << " saslPassword = " << i.saslPassword + << " useAutoReconnect = " << i.useAutoReconnect << " autoReconnectInterval = " << i.autoReconnectInterval << " autoReconnectRetries = " << i.autoReconnectRetries << " unlimitedReconnectRetries = " << i.unlimitedReconnectRetries << " rejoinChannels = " << i.rejoinChannels << ")"; return dbg.space(); diff --git a/src/common/network.h b/src/common/network.h index 4c5baf6c..f0bd112c 100644 --- a/src/common/network.h +++ b/src/common/network.h @@ -64,6 +64,9 @@ class Network : public SyncableObject { Q_PROPERTY(bool useAutoIdentify READ useAutoIdentify WRITE setUseAutoIdentify STORED false) Q_PROPERTY(QString autoIdentifyService READ autoIdentifyService WRITE setAutoIdentifyService STORED false) Q_PROPERTY(QString autoIdentifyPassword READ autoIdentifyPassword WRITE setAutoIdentifyPassword STORED false) + Q_PROPERTY(bool useSasl READ useSasl WRITE setUseSasl STORED false) + Q_PROPERTY(QString saslAccount READ saslAccount WRITE setSaslAccount STORED false) + Q_PROPERTY(QString saslPassword READ saslPassword WRITE setSaslPassword STORED false) Q_PROPERTY(bool useAutoReconnect READ useAutoReconnect WRITE setUseAutoReconnect STORED false) Q_PROPERTY(quint32 autoReconnectInterval READ autoReconnectInterval WRITE setAutoReconnectInterval STORED false) Q_PROPERTY(quint16 autoReconnectRetries READ autoReconnectRetries WRITE setAutoReconnectRetries STORED false) @@ -153,6 +156,9 @@ public: inline bool useAutoIdentify() const { return _useAutoIdentify; } inline const QString &autoIdentifyService() const { return _autoIdentifyService; } inline const QString &autoIdentifyPassword() const { return _autoIdentifyPassword; } + inline bool useSasl() const { return _useSasl; } + inline const QString &saslAccount() const { return _saslAccount; } + inline const QString &saslPassword() const { return _saslPassword; } inline bool useAutoReconnect() const { return _useAutoReconnect; } inline quint32 autoReconnectInterval() const { return _autoReconnectInterval; } inline quint16 autoReconnectRetries() const { return _autoReconnectRetries; } @@ -223,6 +229,9 @@ public slots: void setUseAutoIdentify(bool); void setAutoIdentifyService(const QString &); void setAutoIdentifyPassword(const QString &); + void setUseSasl(bool); + void setSaslAccount(const QString &); + void setSaslPassword(const QString &); virtual void setUseAutoReconnect(bool); virtual void setAutoReconnectInterval(quint32); virtual void setAutoReconnectRetries(quint16); @@ -339,6 +348,10 @@ private: QString _autoIdentifyService; QString _autoIdentifyPassword; + bool _useSasl; + QString _saslAccount; + QString _saslPassword; + bool _useAutoReconnect; quint32 _autoReconnectInterval; quint16 _autoReconnectRetries; @@ -386,6 +399,10 @@ struct NetworkInfo { QString autoIdentifyService; QString autoIdentifyPassword; + bool useSasl; + QString saslAccount; + QString saslPassword; + bool useAutoReconnect; quint32 autoReconnectInterval; quint16 autoReconnectRetries; diff --git a/src/core/corenetwork.cpp b/src/core/corenetwork.cpp index 596a598c..959b3c44 100644 --- a/src/core/corenetwork.cpp +++ b/src/core/corenetwork.cpp @@ -333,6 +333,9 @@ void CoreNetwork::socketInitialized() { _tokenBucket = _burstSize; // init with a full bucket _tokenBucketTimer.start(_messageDelay); + if(networkInfo().useSasl) { + putRawLine(serverEncode(QString("CAP REQ :sasl"))); + } if(!server.password.isEmpty()) { putRawLine(serverEncode(QString("PASS %1").arg(server.password))); } diff --git a/src/core/ircserverhandler.cpp b/src/core/ircserverhandler.cpp index 97e00587..445ca7c6 100644 --- a/src/core/ircserverhandler.cpp +++ b/src/core/ircserverhandler.cpp @@ -84,6 +84,12 @@ void IrcServerHandler::handleServerMsg(QByteArray msg) { QString foo = serverDecode(params.takeFirst()); + // with SASL, the command is 'AUTHENTICATE +' and we should check for this here. + if(foo == QString("AUTHENTICATE +")) { + handleAuthenticate(); + return; + } + // a colon as the first chars indicates the existence of a prefix if(foo[0] == ':') { foo.remove(0, 1); @@ -160,6 +166,11 @@ void IrcServerHandler::defaultHandler(QString cmd, const QString &prefix, const case 321: case 366: case 376: break; + case 903: case 904: case 905: case 906: case 907: + { + network()->putRawLine("CAP END"); + emit displayMsg(Message::Info, BufferInfo::StatusBuffer, "", "CAP: " + params.join("")); + } // Everything else will be marked in red, so we can add them somewhere. default: if(_whois) { @@ -542,6 +553,31 @@ void IrcServerHandler::handleTopic(const QString &prefix, const QListname(), tr("%1 has changed topic for %2 to: \"%3\"").arg(ircuser->nick()).arg(channel->name()).arg(topic)); } +void IrcServerHandler::handleCap(const QString &prefix, const QList ¶ms) { + // for SASL, there will only be a single param of 'sasl', however you can check here for + // additional CAP messages (ls, multi-prefix, et cetera). + + Q_UNUSED(prefix); + + if(params.size() == 3) { + QString param = serverDecode(params[2]); + if(param == QString("sasl")) { // SASL Ready + network()->putRawLine(serverEncode("AUTHENTICATE PLAIN")); // Only working with PLAIN atm, blowfish later + } + } +} + +void IrcServerHandler::handleAuthenticate() { + QString construct = network()->saslAccount(); + construct.append(QChar(QChar::Null)); + construct.append(network()->saslAccount()); + construct.append(QChar(QChar::Null)); + construct.append(network()->saslPassword()); + QByteArray saslData = QByteArray(construct.toAscii().toBase64()); + saslData.prepend(QString("AUTHENTICATE ").toAscii()); + network()->putRawLine(saslData); +} + /* RPL_WELCOME */ void IrcServerHandler::handle001(const QString &prefix, const QList ¶ms) { network()->setCurrentServer(prefix); diff --git a/src/core/ircserverhandler.h b/src/core/ircserverhandler.h index 86eaff23..3b2b8750 100644 --- a/src/core/ircserverhandler.h +++ b/src/core/ircserverhandler.h @@ -45,7 +45,8 @@ public slots: void handlePrivmsg(const QString &prefix, const QList ¶ms); void handleQuit(const QString &prefix, const QList ¶ms); void handleTopic(const QString &prefix, const QList ¶ms); - + void handleCap(const QString &prefix, const QList ¶ms); // CAP framework + void handleAuthenticate(); // SASL auth - no params void handle001(const QString &prefix, const QList ¶ms); // RPL_WELCOME void handle005(const QString &prefix, const QList ¶ms); // RPL_ISUPPORT void handle221(const QString &prefix, const QList ¶ms); // RPL_UMODEIS diff --git a/src/core/postgresqlstorage.cpp b/src/core/postgresqlstorage.cpp index 25053735..c942b4a7 100644 --- a/src/core/postgresqlstorage.cpp +++ b/src/core/postgresqlstorage.cpp @@ -568,6 +568,9 @@ void PostgreSqlStorage::bindNetworkInfo(QSqlQuery &query, const NetworkInfo &inf query.bindValue(":useautoidentify", info.useAutoIdentify); query.bindValue(":autoidentifyservice", info.autoIdentifyService); query.bindValue(":autoidentifypassword", info.autoIdentifyPassword); + query.bindValue(":usesasl", info.useSasl); + query.bindValue(":saslaccount", info.saslAccount); + query.bindValue(":saslpassword", info.saslPassword); query.bindValue(":useautoreconnect", info.useAutoReconnect); query.bindValue(":autoreconnectinterval", info.autoReconnectInterval); query.bindValue(":autoreconnectretries", info.autoReconnectRetries); @@ -707,6 +710,9 @@ QList PostgreSqlStorage::networks(UserId user) { net.autoReconnectRetries = networksQuery.value(13).toInt(); net.unlimitedReconnectRetries = networksQuery.value(14).toBool(); net.rejoinChannels = networksQuery.value(15).toBool(); + net.useSasl = networksQuery.value(16).toBool(); + net.saslAccount = networksQuery.value(17).toString(); + net.saslPassword = networksQuery.value(18).toString(); serversQuery.bindValue(":networkid", net.networkId.toInt()); safeExec(serversQuery); diff --git a/src/core/sqlitestorage.cpp b/src/core/sqlitestorage.cpp index 6462a97e..c962e8f6 100644 --- a/src/core/sqlitestorage.cpp +++ b/src/core/sqlitestorage.cpp @@ -591,6 +591,9 @@ void SqliteStorage::bindNetworkInfo(QSqlQuery &query, const NetworkInfo &info) { query.bindValue(":useautoidentify", info.useAutoIdentify ? 1 : 0); query.bindValue(":autoidentifyservice", info.autoIdentifyService); query.bindValue(":autoidentifypassword", info.autoIdentifyPassword); + query.bindValue(":usesasl", info.useSasl ? 1 : 0); + query.bindValue(":saslaccount", info.saslAccount); + query.bindValue(":saslpassword", info.saslPassword); query.bindValue(":useautoreconnect", info.useAutoReconnect ? 1 : 0); query.bindValue(":autoreconnectinterval", info.autoReconnectInterval); query.bindValue(":autoreconnectretries", info.autoReconnectRetries); @@ -780,6 +783,9 @@ QList SqliteStorage::networks(UserId user) { net.autoReconnectRetries = networksQuery.value(13).toInt(); net.unlimitedReconnectRetries = networksQuery.value(14).toInt() == 1 ? true : false; net.rejoinChannels = networksQuery.value(15).toInt() == 1 ? true : false; + net.useSasl = networksQuery.value(16).toInt() == 1 ? true : false; + net.saslAccount = networksQuery.value(17).toString(); + net.saslPassword = networksQuery.value(18).toString(); serversQuery.bindValue(":networkid", net.networkId.toInt()); safeExec(serversQuery); diff --git a/src/qtui/settingspages/networkssettingspage.cpp b/src/qtui/settingspages/networkssettingspage.cpp index 1b20c2a1..2ea60dba 100644 --- a/src/qtui/settingspages/networkssettingspage.cpp +++ b/src/qtui/settingspages/networkssettingspage.cpp @@ -78,6 +78,9 @@ NetworksSettingsPage::NetworksSettingsPage(QWidget *parent) connect(ui.autoIdentify, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged())); connect(ui.autoIdentifyService, SIGNAL(textEdited(const QString &)), this, SLOT(widgetHasChanged())); connect(ui.autoIdentifyPassword, SIGNAL(textEdited(const QString &)), this, SLOT(widgetHasChanged())); + connect(ui.sasl, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged())); + connect(ui.saslAccount, SIGNAL(textEdited(QString)), this, SLOT(widgetHasChanged())); + connect(ui.saslPassword, SIGNAL(textEdited(QString)), this, SLOT(widgetHasChanged())); connect(ui.useCustomEncodings, SIGNAL(clicked(bool)), this, SLOT(widgetHasChanged())); connect(ui.sendEncoding, SIGNAL(currentIndexChanged(int)), this, SLOT(widgetHasChanged())); connect(ui.recvEncoding, SIGNAL(currentIndexChanged(int)), this, SLOT(widgetHasChanged())); @@ -429,6 +432,9 @@ void NetworksSettingsPage::displayNetwork(NetworkId id) { ui.autoIdentify->setChecked(info.useAutoIdentify); ui.autoIdentifyService->setText(info.autoIdentifyService); ui.autoIdentifyPassword->setText(info.autoIdentifyPassword); + ui.sasl->setChecked(info.useSasl); + ui.saslAccount->setText(info.saslAccount); + ui.saslPassword->setText(info.saslPassword); if(info.codecForEncoding.isEmpty()) { ui.sendEncoding->setCurrentIndex(ui.sendEncoding->findText(Network::defaultCodecForEncoding())); ui.recvEncoding->setCurrentIndex(ui.recvEncoding->findText(Network::defaultCodecForDecoding())); @@ -452,6 +458,8 @@ void NetworksSettingsPage::displayNetwork(NetworkId id) { ui.performEdit->clear(); ui.autoIdentifyService->clear(); ui.autoIdentifyPassword->clear(); + ui.saslAccount->clear(); + ui.saslPassword->clear(); setWidgetStates(); } _ignoreWidgetChanges = false; @@ -465,6 +473,9 @@ void NetworksSettingsPage::saveToNetworkInfo(NetworkInfo &info) { info.useAutoIdentify = ui.autoIdentify->isChecked(); info.autoIdentifyService = ui.autoIdentifyService->text(); info.autoIdentifyPassword = ui.autoIdentifyPassword->text(); + info.useSasl = ui.sasl->isChecked(); + info.saslAccount = ui.saslAccount->text(); + info.saslPassword = ui.saslPassword->text(); if(!ui.useCustomEncodings->isChecked()) { info.codecForEncoding.clear(); info.codecForDecoding.clear(); diff --git a/src/qtui/settingspages/networkssettingspage.ui b/src/qtui/settingspages/networkssettingspage.ui index 41bcfb9f..4bbafbaa 100644 --- a/src/qtui/settingspages/networkssettingspage.ui +++ b/src/qtui/settingspages/networkssettingspage.ui @@ -7,7 +7,7 @@ 0 0 515 - 453 + 503 @@ -547,6 +547,64 @@ Note that Quassel IRC automatically rejoins channels, so /join will rarely be ne + + + + true + + + Use SASL Authentication + + + true + + + true + + + + + + true + + + + + + + + + + true + + + QLineEdit::Password + + + + + + + true + + + Password: + + + + + + + true + + + Account: + + + + + + @@ -718,7 +776,6 @@ Unless you *really* know what you do, leave this as ISO-8859-1! reconnectRetries unlimitedRetries rejoinOnReconnect - autoIdentify autoIdentifyService autoIdentifyPassword useCustomEncodings @@ -735,12 +792,12 @@ Unless you *really* know what you do, leave this as ISO-8859-1! setDisabled(bool) - 173 - 296 + 317 + 331 - 118 - 294 + 209 + 334