mirror of
https://github.com/znc/znc.git
synced 2026-03-28 17:42:41 +01:00
ZNC remembers that it should join these channels, and will join them after registration. But if client automatically joins some channels, we don't want it to be added to send queue before parts of registration itself (CAP, AUTHENTICATE), because server will just disconnect with "Registration timeout". After registration is complete, using /join still joins the channel immediately. Only limiting this to joins, because server may request some input from user to finish registration, and joins are the ones which are prone to be sent automatically by client to cause issues. Fix #1949
1191 lines
41 KiB
C++
1191 lines
41 KiB
C++
/*
|
|
* Copyright (C) 2004-2025 ZNC, see the NOTICE file for details.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <gtest/gtest.h>
|
|
#include <gmock/gmock.h>
|
|
|
|
#include "znctest.h"
|
|
#include "znctestconfig.h"
|
|
|
|
using testing::HasSubstr;
|
|
using testing::ContainsRegex;
|
|
using testing::SizeIs;
|
|
using testing::Lt;
|
|
using testing::Gt;
|
|
using testing::AllOf;
|
|
|
|
namespace znc_inttest {
|
|
namespace {
|
|
|
|
TEST(Config, AlreadyExists) {
|
|
QTemporaryDir dir;
|
|
WriteConfig(dir.path());
|
|
Process p(ZNC_BIN_DIR "/znc", QStringList() << "--debug"
|
|
<< "--datadir" << dir.path()
|
|
<< "--makeconf");
|
|
p.ReadUntil("already exists");
|
|
p.CanDie();
|
|
}
|
|
|
|
TEST_F(ZNCTest, Connect) {
|
|
auto znc = Run();
|
|
|
|
auto ircd = ConnectIRCd();
|
|
ircd.ReadUntil("CAP LS");
|
|
|
|
auto client = ConnectClient();
|
|
client.Write("PASS :hunter2");
|
|
client.Write("NICK nick");
|
|
client.Write("USER user/test x x :x");
|
|
client.ReadUntil("Welcome");
|
|
client.Close();
|
|
|
|
client = ConnectClient();
|
|
client.Write("PASS :user:hunter2");
|
|
client.Write("NICK nick");
|
|
client.Write("USER u x x x");
|
|
client.ReadUntil("Welcome");
|
|
client.Close();
|
|
|
|
client = ConnectClient();
|
|
client.Write("NICK nick");
|
|
client.Write("USER user x x x");
|
|
client.ReadUntil("Configure your client to send a server password");
|
|
client.Close();
|
|
|
|
ircd.Write(":server 001 nick :Hello");
|
|
ircd.ReadUntil("WHO");
|
|
}
|
|
|
|
TEST_F(ZNCTest, Channel) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
|
|
auto client = LoginClient();
|
|
client.ReadUntil("Welcome");
|
|
client.Write("JOIN #znc");
|
|
client.Close();
|
|
|
|
ircd.Write(":server 001 nick :Hello");
|
|
ircd.ReadUntil("JOIN #znc");
|
|
ircd.Write(":nick JOIN :#znc");
|
|
ircd.Write(":server 353 nick #znc :nick");
|
|
ircd.Write(":server 366 nick #znc :End of /NAMES list");
|
|
ircd.Write(":server PING :1");
|
|
ircd.ReadUntil("PONG 1");
|
|
|
|
client = LoginClient();
|
|
client.ReadUntil(":nick JOIN :#znc");
|
|
}
|
|
|
|
TEST_F(ZNCTest, HTTP) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
|
|
auto client = LoginClient();
|
|
int port = PickPortNumber();
|
|
client.Write(QStringLiteral("znc addport %1 all all").arg(port).toUtf8());
|
|
client.ReadUntil(":Port added");
|
|
|
|
auto reply = HttpGet(QNetworkRequest(
|
|
QUrl(QStringLiteral("http://127.0.0.1:%1/").arg(port))));
|
|
EXPECT_THAT(reply->rawHeader("Server").toStdString(), HasSubstr("ZNC"));
|
|
}
|
|
|
|
TEST_F(ZNCTest, FixCVE20149403) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
ircd.Write(":server 001 nick :Hello");
|
|
ircd.Write(":server 005 nick CHANTYPES=# :supports");
|
|
ircd.Write(":server PING :1");
|
|
ircd.ReadUntil("PONG 1");
|
|
|
|
auto client = LoginClient();
|
|
int port = PickPortNumber();
|
|
client.Write(QStringLiteral("znc addport %1 all all").arg(port).toUtf8());
|
|
client.ReadUntil(":Port added");
|
|
|
|
QNetworkRequest request;
|
|
request.setRawHeader("Authorization",
|
|
"Basic " + QByteArray("user:hunter2").toBase64());
|
|
request.setUrl(
|
|
QUrl(QStringLiteral("http://127.0.0.1:%1/mods/global/webadmin/addchan")
|
|
.arg(port)));
|
|
HttpPost(request, {
|
|
{"user", "user"},
|
|
{"network", "test"},
|
|
{"submitted", "1"},
|
|
{"name", "znc"},
|
|
{"enabled", "1"},
|
|
});
|
|
EXPECT_THAT(HttpPost(request,
|
|
{
|
|
{"user", "user"},
|
|
{"network", "test"},
|
|
{"submitted", "1"},
|
|
{"name", "znc"},
|
|
{"enabled", "1"},
|
|
})
|
|
->readAll()
|
|
.toStdString(),
|
|
HasSubstr("Channel [#znc] already exists"));
|
|
}
|
|
|
|
TEST_F(ZNCTest, FixFixOfCVE20149403) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
ircd.Write(":server 001 nick :Hello");
|
|
ircd.Write(":nick JOIN @#znc");
|
|
ircd.ReadUntil("MODE @#znc");
|
|
ircd.Write(":server 005 nick STATUSMSG=@ :supports");
|
|
ircd.Write(":server PING :12345");
|
|
ircd.ReadUntil("PONG 12345");
|
|
|
|
auto client = LoginClient();
|
|
int port = PickPortNumber();
|
|
client.Write(QStringLiteral("znc addport %1 all all").arg(port).toUtf8());
|
|
client.ReadUntil(":Port added");
|
|
|
|
QNetworkRequest request;
|
|
request.setRawHeader("Authorization",
|
|
"Basic " + QByteArray("user:hunter2").toBase64());
|
|
request.setUrl(
|
|
QUrl(QStringLiteral("http://127.0.0.1:%1/mods/global/webadmin/addchan")
|
|
.arg(port)
|
|
.toUtf8()));
|
|
auto reply = HttpPost(request, {
|
|
{"user", "user"},
|
|
{"network", "test"},
|
|
{"submitted", "1"},
|
|
{"name", "@#znc"},
|
|
{"enabled", "1"},
|
|
});
|
|
EXPECT_THAT(reply->readAll().toStdString(),
|
|
HasSubstr("Could not add channel [@#znc]"));
|
|
}
|
|
|
|
TEST_F(ZNCTest, InvalidConfigInChan) {
|
|
QFile conf(m_dir.path() + "/configs/znc.conf");
|
|
ASSERT_TRUE(conf.open(QIODevice::Append | QIODevice::Text));
|
|
QTextStream out(&conf);
|
|
out << R"(
|
|
<User foo>
|
|
<Network bar>
|
|
<Chan #baz>
|
|
Invalid = Line
|
|
</Chan>
|
|
</Network>
|
|
</User>
|
|
)";
|
|
out.flush();
|
|
auto znc = Run();
|
|
znc->ShouldFinishItself(1);
|
|
}
|
|
|
|
TEST_F(ZNCTest, Encoding) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
auto client = LoginClient();
|
|
ircd.Write(":server 001 nick :hello");
|
|
// legacy
|
|
ircd.Write(":n!u@h PRIVMSG nick :Hello\xE6world");
|
|
client.ReadUntil("Hello\xE6world");
|
|
client.Write("PRIVMSG *controlpanel :SetNetwork Encoding $me $net UTF-8");
|
|
client.ReadUntil("Encoding = UTF-8");
|
|
ircd.Write(":n!u@h PRIVMSG nick :Hello\xE6world");
|
|
client.ReadUntil("Hello\xEF\xBF\xBDworld");
|
|
client.Write(
|
|
"PRIVMSG *controlpanel :SetNetwork Encoding $me $net ^CP-1251");
|
|
client.ReadUntil("Encoding = ^CP-1251");
|
|
ircd.Write(":n!u@h PRIVMSG nick :Hello\xE6world");
|
|
client.ReadUntil("Hello\xD0\xB6world");
|
|
ircd.Write(":n!u@h PRIVMSG nick :Hello\xD0\xB6world");
|
|
client.ReadUntil("Hello\xD0\xB6world");
|
|
}
|
|
|
|
TEST_F(ZNCTest, BuildMod) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
auto client = LoginClient();
|
|
QTemporaryDir srcd;
|
|
QDir srcdir(srcd.path());
|
|
QFile file(srcdir.filePath("testmod.cpp"));
|
|
ASSERT_TRUE(file.open(QIODevice::WriteOnly | QIODevice::Text));
|
|
QTextStream out(&file);
|
|
out << R"(
|
|
#include <znc/Modules.h>
|
|
class TestModule : public CModule {
|
|
public:
|
|
MODCONSTRUCTOR(TestModule) {}
|
|
void OnModCommand(const CString& sLine) override {
|
|
PutModule("Lorem ipsum");
|
|
}
|
|
};
|
|
MODULEDEFS(TestModule, "Test")
|
|
)";
|
|
file.close();
|
|
QDir dir(m_dir.path());
|
|
EXPECT_TRUE(dir.mkdir("modules"));
|
|
EXPECT_TRUE(dir.cd("modules"));
|
|
{
|
|
Process p(ZNC_BIN_DIR "/znc-buildmod",
|
|
QStringList() << srcdir.filePath("file-not-found.cpp"),
|
|
[&](QProcess* proc) {
|
|
proc->setWorkingDirectory(dir.absolutePath());
|
|
proc->setProcessChannelMode(QProcess::ForwardedChannels);
|
|
});
|
|
p.ShouldFinishItself(1);
|
|
p.ShouldFinishInSec(300);
|
|
}
|
|
{
|
|
Process p(ZNC_BIN_DIR "/znc-buildmod",
|
|
QStringList() << srcdir.filePath("testmod.cpp"),
|
|
[&](QProcess* proc) {
|
|
proc->setWorkingDirectory(dir.absolutePath());
|
|
proc->setProcessChannelMode(QProcess::ForwardedChannels);
|
|
});
|
|
p.ShouldFinishItself();
|
|
p.ShouldFinishInSec(300);
|
|
}
|
|
client.Write("znc loadmod testmod");
|
|
client.Write("PRIVMSG *testmod :hi");
|
|
client.ReadUntil("Lorem ipsum");
|
|
}
|
|
|
|
TEST_F(ZNCTest, AwayNotify) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
auto client = ConnectClient();
|
|
client.Write("CAP LS");
|
|
client.Write("PASS :hunter2");
|
|
client.Write("NICK nick");
|
|
client.Write("USER user/test x x :x");
|
|
QByteArray cap_ls;
|
|
client.ReadUntilAndGet(" LS :", cap_ls);
|
|
ASSERT_THAT(cap_ls.toStdString(),
|
|
AllOf(HasSubstr("cap-notify"), Not(HasSubstr("away-notify"))));
|
|
client.Write("CAP REQ :cap-notify");
|
|
client.ReadUntil("ACK :cap-notify");
|
|
client.Write("CAP END");
|
|
client.ReadUntil(" 001 ");
|
|
ircd.ReadUntil("USER");
|
|
ircd.Write("CAP user LS :away-notify");
|
|
ircd.ReadUntil("CAP REQ :away-notify");
|
|
ircd.Write("CAP user ACK :away-notify");
|
|
ircd.ReadUntil("CAP END");
|
|
ircd.Write(":server 001 user :welcome");
|
|
client.ReadUntil("CAP user NEW :away-notify");
|
|
client.Write("CAP REQ :away-notify");
|
|
client.ReadUntil("ACK :away-notify");
|
|
ircd.Write(":x!y@z AWAY :reason");
|
|
client.ReadUntil(":x!y@z AWAY :reason");
|
|
ircd.Close();
|
|
client.ReadUntil("DEL :away-notify");
|
|
// This test often fails on macos due to ZNC process not finishing.
|
|
// No idea why. Let's try to shutdown it more explicitly...
|
|
client.Write("znc shutdown");
|
|
}
|
|
|
|
TEST_F(ZNCTest, ExtendedJoin) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
auto client = LoginClient();
|
|
ircd.Write(":server 001 user :welcome");
|
|
client.ReadUntil(" 001 ");
|
|
ircd.Write(":nick!user@host JOIN #channel account :Real Name");
|
|
// Not sure why it is like this when server sends such format unexpectedly.
|
|
client.ReadUntil("JOIN #channel account :Real Name");
|
|
ircd.Write("CAP nick ACK extended-join");
|
|
ircd.Write(":nick!user@host JOIN #channel2 account :Real Name");
|
|
QByteArray line;
|
|
client.ReadUntilAndGet("JOIN", line);
|
|
EXPECT_EQ(line.toStdString(), "JOIN #channel2");
|
|
client.Write("CAP REQ extended-join");
|
|
client.ReadUntil("CAP user ACK :extended-join");
|
|
ircd.Write(":nick!user@host JOIN #channel3 account :Real Name");
|
|
client.ReadUntil("JOIN #channel3 account :Real Name");
|
|
}
|
|
|
|
TEST_F(ZNCTest, CAP302LSWaitFull) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
ircd.ReadUntil("CAP LS 302");
|
|
ircd.Write("CAP user LS * :away-notify");
|
|
ASSERT_THAT(ircd.ReadRemainder().toStdString(), Not(HasSubstr("away-notify")));
|
|
ircd.Write("CAP user LS :blahblah");
|
|
ircd.ReadUntil("CAP REQ :away-notify");
|
|
}
|
|
|
|
TEST_F(ZNCTest, CAP302NewDel) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
auto client = LoginClient();
|
|
ircd.Write("CAP nick LS :blahblah");
|
|
ircd.ReadUntil("CAP END");
|
|
ircd.Write(":server 001 nick :Hello");
|
|
client.Write("CAP REQ :away-notify");
|
|
client.ReadUntil("NAK :away-notify");
|
|
client.Write("CAP REQ :cap-notify");
|
|
client.ReadUntil("ACK :cap-notify");
|
|
ircd.Write("CAP nick NEW :away-notify");
|
|
ircd.ReadUntil("CAP REQ :away-notify");
|
|
ircd.Write("CAP nick ACK :away-notify");
|
|
client.ReadUntil("CAP nick NEW :away-notify");
|
|
ircd.Write("CAP nick DEL :away-notify");
|
|
client.ReadUntil("CAP nick DEL :away-notify");
|
|
}
|
|
|
|
TEST_F(ZNCTest, JoinKey) {
|
|
QFile conf(m_dir.path() + "/configs/znc.conf");
|
|
ASSERT_TRUE(conf.open(QIODevice::Append | QIODevice::Text));
|
|
QTextStream(&conf) << "ServerThrottle = 1\n";
|
|
auto znc = Run();
|
|
|
|
auto ircd = ConnectIRCd();
|
|
auto client = LoginClient();
|
|
ircd.Write(":server 001 nick :Hello");
|
|
client.Write("JOIN #znc secret");
|
|
ircd.ReadUntil("JOIN #znc secret");
|
|
ircd.Write(":nick JOIN :#znc");
|
|
client.ReadUntil("JOIN :#znc");
|
|
ircd.Close();
|
|
|
|
ircd = ConnectIRCd();
|
|
ircd.Write(":server 001 nick :Hello");
|
|
ircd.ReadUntil("JOIN #znc secret");
|
|
}
|
|
|
|
TEST_F(ZNCTest, StatusEchoMessage) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
auto client = LoginClient();
|
|
client.Write("CAP REQ :echo-message");
|
|
client.Write("PRIVMSG *status :blah");
|
|
client.ReadUntil(":nick!user@irc.znc.in PRIVMSG *status :blah");
|
|
client.ReadUntil(":*status!status@znc.in PRIVMSG nick :Unknown command");
|
|
client.Write("znc delnetwork test");
|
|
client.ReadUntil("Network deleted");
|
|
auto client2 = LoginClient();
|
|
client2.Write("PRIVMSG *status :blah2");
|
|
client2.ReadUntil(":*status!status@znc.in PRIVMSG nick :Unknown command");
|
|
auto client3 = LoginClient();
|
|
client3.Write("PRIVMSG *status :blah3");
|
|
client3.ReadUntil(":*status!status@znc.in PRIVMSG nick :Unknown command");
|
|
}
|
|
|
|
TEST_F(ZNCTest, MoveChannels) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
|
|
auto client = LoginClient();
|
|
client.Write("JOIN #foo,#bar");
|
|
client.Close();
|
|
|
|
ircd.Write(":server 001 nick :Hello");
|
|
ircd.ReadUntil("JOIN #foo,#bar");
|
|
ircd.Write(":nick JOIN :#foo");
|
|
ircd.Write(":server 353 nick #foo :nick");
|
|
ircd.Write(":server 366 nick #foo :End of /NAMES list");
|
|
ircd.Write(":nick JOIN :#bar");
|
|
ircd.Write(":server 353 nick #bar :nick");
|
|
ircd.Write(":server 366 nick #bar :End of /NAMES list");
|
|
|
|
client = LoginClient();
|
|
client.ReadUntil(":nick JOIN :#foo");
|
|
client.ReadUntil(":nick JOIN :#bar");
|
|
client.Write("znc movechan #foo 2");
|
|
client.ReadUntil("Moved channel #foo to index 2");
|
|
client.Close();
|
|
|
|
client = LoginClient();
|
|
client.ReadUntil(":nick JOIN :#bar");
|
|
client.ReadUntil(":nick JOIN :#foo");
|
|
client.Write("znc swapchans #foo #bar");
|
|
client.ReadUntil("Swapped channels #foo and #bar");
|
|
client.Close();
|
|
|
|
client = LoginClient();
|
|
client.ReadUntil(":nick JOIN :#foo");
|
|
client.ReadUntil(":nick JOIN :#bar");
|
|
}
|
|
|
|
TEST_F(ZNCTest, DenyOptions) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
|
|
auto client1 = LoginClient();
|
|
client1.Write("PRIVMSG *controlpanel :CloneUser user user2");
|
|
client1.ReadUntil("User user2 added!");
|
|
client1.Write("PRIVMSG *controlpanel :Set Admin user2 false");
|
|
client1.ReadUntil("Admin = false");
|
|
client1.Write("PRIVMSG *controlpanel :Set MaxNetworks user2 5");
|
|
client1.ReadUntil("MaxNetworks = 5");
|
|
|
|
auto client2 = ConnectClient();
|
|
client2.Write("PASS :hunter2");
|
|
client2.Write("NICK nick2");
|
|
client2.Write("USER user2/test x x :x");
|
|
|
|
// DenySetNetwork
|
|
// This is false by default so we should be able to add/delete networks/servers
|
|
client2.Write("PRIVMSG *controlpanel :AddNetwork user2 test2");
|
|
client2.ReadUntil("Network test2 added to user user2.");
|
|
client2.Write("PRIVMSG *controlpanel :AddServer user2 test2 127.0.0.1");
|
|
client2.ReadUntil("Added IRC Server 127.0.0.1 to network test2 for user user2.");
|
|
client2.Write("PRIVMSG *controlpanel :DelServer user2 test2 127.0.0.1");
|
|
client2.ReadUntil("Deleted IRC Server 127.0.0.1 from network test2 for user user2.");
|
|
client2.Write("PRIVMSG *controlpanel :DelNetwork user2 test2");
|
|
client2.ReadUntil("Network test2 deleted for user user2.");
|
|
|
|
// Set it to true
|
|
client1.Write("PRIVMSG *controlpanel :Set DenySetNetwork user2 true");
|
|
client1.ReadUntil("DenySetNetwork = true");
|
|
|
|
// Now we should be denied
|
|
client2.Write("PRIVMSG *controlpanel :AddNetwork user2 test2");
|
|
client2.ReadUntil("Access denied!");
|
|
client2.Write("PRIVMSG *controlpanel :AddServer user2 test 127.0.0.2");
|
|
client2.ReadUntil("Access denied!");
|
|
client2.Write("PRIVMSG *controlpanel :DelServer user2 test 127.0.0.1");
|
|
client2.ReadUntil("Access denied!");
|
|
client2.Write("PRIVMSG *controlpanel :DelNetwork user2 test");
|
|
client2.ReadUntil("Access denied!");
|
|
|
|
// DenySetBindHost
|
|
client2.Write("PRIVMSG *controlpanel :Set BindHost user2 127.0.0.1");
|
|
client2.ReadUntil("BindHost = 127.0.0.1");
|
|
client1.Write("PRIVMSG *controlpanel :Set DenySetBindHost user2 true");
|
|
client1.ReadUntil("DenySetBindHost = true");
|
|
client2.Write("PRIVMSG *controlpanel :Set BindHost user2 127.0.0.2");
|
|
client2.ReadUntil("Access denied!");
|
|
|
|
// DenySetIdent
|
|
client2.Write("PRIVMSG *controlpanel :Set Ident user2 test");
|
|
client2.ReadUntil("Ident = test");
|
|
client1.Write("PRIVMSG *controlpanel :Set DenySetIdent user2 true");
|
|
client1.ReadUntil("DenySetIdent = true");
|
|
client2.Write("PRIVMSG *controlpanel :Set Ident user2 test2");
|
|
client2.ReadUntil("Access denied!");
|
|
|
|
// DenySetRealName
|
|
client2.Write("PRIVMSG *controlpanel :Set RealName user2 test");
|
|
client2.ReadUntil("RealName = test");
|
|
client1.Write("PRIVMSG *controlpanel :Set DenySetRealName user2 true");
|
|
client1.ReadUntil("DenySetRealName = true");
|
|
client2.Write("PRIVMSG *controlpanel :Set RealName user2 test2");
|
|
client2.ReadUntil("Access denied!");
|
|
|
|
// DenySetQuitMsg
|
|
client2.Write("PRIVMSG *controlpanel :Set QuitMsg user2 test");
|
|
client2.ReadUntil("QuitMsg = test");
|
|
client1.Write("PRIVMSG *controlpanel :Set DenySetQuitMsg user2 true");
|
|
client1.ReadUntil("DenySetQuitMsg = true");
|
|
client2.Write("PRIVMSG *controlpanel :Set QuitMsg user2 test2");
|
|
client2.ReadUntil("Access denied!");
|
|
|
|
// DenySetCTCPReplies
|
|
client2.Write("PRIVMSG *controlpanel :AddCTCP user2 FOO BAR");
|
|
client2.ReadUntil("CTCP requests FOO to user user2 will now get reply: BAR");
|
|
client2.Write("PRIVMSG *controlpanel :DelCTCP user2 FOO");
|
|
client2.ReadUntil("CTCP requests FOO to user user2 will now be sent to IRC clients");
|
|
client1.Write("PRIVMSG *controlpanel :Set DenySetCTCPReplies user2 true");
|
|
client1.ReadUntil("DenySetCTCPReplies = true");
|
|
client2.Write("PRIVMSG *controlpanel :AddCTCP user2 FOO BAR");
|
|
client2.ReadUntil("Access denied!");
|
|
client2.Write("PRIVMSG *controlpanel :DelCTCP user2 FOO");
|
|
client2.ReadUntil("Access denied!");
|
|
}
|
|
|
|
TEST_F(ZNCTest, CAP302MultiLS) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
auto client = LoginClient();
|
|
InstallModule("testmod.cpp", R"(
|
|
#include <znc/Client.h>
|
|
#include <znc/Modules.h>
|
|
class TestModule : public CModule {
|
|
public:
|
|
MODCONSTRUCTOR(TestModule) {}
|
|
void OnClientCapLs(CClient* pClient, SCString& ssCaps) override {
|
|
for (int i = 0; i < 100; ++i) {
|
|
ssCaps.insert("testcap-" + CString(i));
|
|
}
|
|
}
|
|
};
|
|
GLOBALMODULEDEFS(TestModule, "Test")
|
|
)");
|
|
client.Write("znc loadmod testmod");
|
|
client.ReadUntil("Loaded module testmod");
|
|
|
|
auto client2 = ConnectClient();
|
|
client2.Write("CAP LS");
|
|
client2.ReadUntil("LS :");
|
|
auto rem = client2.ReadRemainder();
|
|
ASSERT_GT(rem.indexOf("testcap-10"), 10);
|
|
ASSERT_EQ(rem.indexOf("testcap-80"), -1);
|
|
ASSERT_EQ(rem.indexOf("LS"), -1);
|
|
|
|
client2 = ConnectClient();
|
|
client2.Write("CAP LS 302");
|
|
client2.ReadUntil("LS * :");
|
|
rem = client2.ReadRemainder();
|
|
int w = 0;
|
|
ASSERT_GT(w = rem.indexOf("testcap-10"), 1);
|
|
ASSERT_GT(w = rem.indexOf("testcap-22", w), 1);
|
|
ASSERT_GT(w = rem.indexOf("LS * :", w), 1);
|
|
ASSERT_GT(rem.indexOf("testcap-80", w), 1);
|
|
ASSERT_GT(rem.indexOf("LS :", w), 1);
|
|
}
|
|
|
|
TEST_F(ZNCTest, CAP302LSValue) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
auto client = LoginClient();
|
|
InstallModule("testmod.cpp", R"(
|
|
#include <znc/Client.h>
|
|
#include <znc/Modules.h>
|
|
class TestModule : public CModule {
|
|
public:
|
|
MODCONSTRUCTOR(TestModule) {}
|
|
void OnClientCapLs(CClient* pClient, SCString& ssCaps) override {
|
|
if (pClient->HasCap302()) {
|
|
ssCaps.insert("testcap=blah");
|
|
} else {
|
|
ssCaps.insert("testcap");
|
|
}
|
|
}
|
|
};
|
|
GLOBALMODULEDEFS(TestModule, "Test")
|
|
)");
|
|
client.Write("znc loadmod testmod");
|
|
client.ReadUntil("Loaded module testmod");
|
|
|
|
auto client2 = ConnectClient();
|
|
client2.Write("CAP LS");
|
|
client2.ReadUntil("testcap ");
|
|
|
|
client2 = ConnectClient();
|
|
client2.Write("CAP LS 302");
|
|
client2.ReadUntil("testcap=");
|
|
}
|
|
|
|
class AllLanguages : public ZNCTest, public testing::WithParamInterface<int> {};
|
|
|
|
INSTANTIATE_TEST_CASE_P(LanguagesTests, AllLanguages, testing::Values(1, 2, 3));
|
|
|
|
TEST_P(AllLanguages, ServerDependentCapInModule) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
auto client = LoginClient();
|
|
switch (GetParam()) {
|
|
case 1:
|
|
InstallModule("testmod.cpp", R"(
|
|
#include <znc/Modules.h>
|
|
class TestModule : public CModule {
|
|
class TestCap : public CCapability {
|
|
void OnServerChangedSupport(CIRCNetwork* pNetwork, bool bState) override {
|
|
GetModule()->PutModule("Server changed support: " + CString(bState));
|
|
}
|
|
void OnClientChangedSupport(CClient* pClient, bool bState) override {
|
|
GetModule()->PutModule("Client changed support: " + CString(bState));
|
|
}
|
|
};
|
|
public:
|
|
MODCONSTRUCTOR(TestModule) {
|
|
AddServerDependentCapability("testcap", std::make_unique<TestCap>());
|
|
}
|
|
};
|
|
MODULEDEFS(TestModule, "Test")
|
|
)");
|
|
break;
|
|
case 2:
|
|
#ifndef WANT_PYTHON
|
|
GTEST_SKIP() << "Modpython is disabled";
|
|
#endif
|
|
znc->CanLeak();
|
|
InstallModule("testmod.py", R"(
|
|
import znc
|
|
class testmod(znc.Module):
|
|
def OnLoad(self, args, ret):
|
|
def server_change(net, state):
|
|
self.PutModule('Server changed support: ' + ('true' if state else 'false'))
|
|
def client_change(client, state):
|
|
self.PutModule('Client changed support: ' + ('true' if state else 'false'))
|
|
self.AddServerDependentCapability('testcap', server_change, client_change)
|
|
return True
|
|
)");
|
|
client.Write("znc loadmod modpython");
|
|
break;
|
|
case 3:
|
|
#ifndef WANT_PERL
|
|
GTEST_SKIP() << "Modperl is disabled";
|
|
#endif
|
|
znc->CanLeak();
|
|
InstallModule("testmod.pm", R"(
|
|
package testmod;
|
|
use base 'ZNC::Module';
|
|
sub OnLoad {
|
|
my $self = shift;
|
|
$self->AddServerDependentCapability('testcap', sub {
|
|
my ($net, $state) = @_;
|
|
$self->PutModule('Server changed support: ' . ($state ? 'true' : 'false'));
|
|
}, sub {
|
|
my ($client, $state) = @_;
|
|
$self->PutModule('Client changed support: ' . ($state ? 'true' : 'false'));
|
|
});
|
|
return 1;
|
|
}
|
|
1;
|
|
)");
|
|
client.Write("znc loadmod modperl");
|
|
break;
|
|
}
|
|
client.Write("znc loadmod testmod");
|
|
client.ReadUntil("Loaded module testmod");
|
|
client.Write("znc addnetwork net2");
|
|
client.Close();
|
|
|
|
client = ConnectClient();
|
|
client.Write("CAP LS 302");
|
|
client.Write("PASS :hunter2");
|
|
client.Write("NICK nick");
|
|
client.Write("USER user/test x x :x");
|
|
client.Write("CAP END");
|
|
client.ReadUntil("Welcome");
|
|
|
|
ircd.Write("001 nick Welcome");
|
|
ircd.Write("CAP nick NEW testcap=value");
|
|
ircd.ReadUntil("CAP REQ :testcap");
|
|
ircd.Write("CAP nick ACK :testcap");
|
|
client.ReadUntil(":Server changed support: true");
|
|
client.ReadUntil("CAP nick NEW :testcap=value");
|
|
client.Write("CAP REQ testcap");
|
|
client.ReadUntil(":Client changed support: true");
|
|
client.ReadUntil("CAP nick ACK :testcap");
|
|
|
|
client.Write("CAP LS");
|
|
client.ReadUntil(" testcap=value ");
|
|
|
|
ircd.Write("CAP nick DEL testcap");
|
|
client.ReadUntil(":Server changed support: false");
|
|
client.ReadUntil("CAP nick DEL :testcap");
|
|
client.ReadUntil(":Client changed support: false");
|
|
|
|
ircd.Close();
|
|
// TODO combine multiple DELs to single line
|
|
client.ReadUntil("CAP nick DEL :testcap");
|
|
client.ReadUntil(":Client changed support: false");
|
|
|
|
ircd = ConnectIRCd();
|
|
ircd.ReadUntil("CAP LS 302");
|
|
ircd.Write("CAP nick LS :testcap=new");
|
|
ircd.ReadUntil("CAP REQ :testcap");
|
|
ircd.Write("CAP nick ACK :testcap");
|
|
client.ReadUntil(":Server changed support: true");
|
|
ircd.ReadUntil("CAP END");
|
|
// NEW waits until 001
|
|
ASSERT_THAT(ircd.ReadRemainder().toStdString(), Not(HasSubstr("testcap")));
|
|
ircd.Write("001 nick Welcome");
|
|
// TODO combine multiple NEWs to single line
|
|
client.ReadUntil("CAP nick NEW :testcap=new");
|
|
client.ReadUntil("Welcome");
|
|
|
|
// NEW with new value without DEL
|
|
ircd.Write("CAP nick NEW testcap=another");
|
|
client.ReadUntil("CAP nick NEW :testcap=another");
|
|
|
|
client.Write("znc jumpnetwork net2");
|
|
client.ReadUntil("CAP nick DEL :testcap");
|
|
client.ReadUntil(":Client changed support: false");
|
|
|
|
client.Write("znc jumpnetwork test");
|
|
client.ReadUntil("CAP nick NEW :testcap=another");
|
|
}
|
|
|
|
TEST_F(ZNCTest, HashUpgrade) {
|
|
QFile conf(m_dir.path() + "/configs/znc.conf");
|
|
ASSERT_TRUE(conf.open(QIODevice::Append | QIODevice::Text));
|
|
QTextStream out(&conf);
|
|
out << R"(
|
|
<User foo>
|
|
<Pass pass>
|
|
Method = MD5
|
|
Salt = abc
|
|
Hash = defdf93cef7fa7a8ee88e65d0e277b99
|
|
</Pass>
|
|
</User>
|
|
)";
|
|
out.flush();
|
|
conf.close();
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
|
|
auto client = ConnectClient();
|
|
client.Write("PASS :hunter2");
|
|
client.Write("NICK nick");
|
|
client.Write("USER foo x x :x");
|
|
client.ReadUntil("Welcome");
|
|
client.Close();
|
|
|
|
client = LoginClient();
|
|
client.Write("znc saveconfig");
|
|
client.ReadUntil("Wrote config");
|
|
|
|
ASSERT_TRUE(conf.open(QIODevice::ReadOnly | QIODevice::Text));
|
|
QTextStream in(&conf);
|
|
QString config = in.readAll();
|
|
// It was upgraded to either Argon2 or SHA256
|
|
EXPECT_THAT(config.toStdString(), Not(ContainsRegex("Method.*MD5")));
|
|
|
|
// Check that still can login after the upgrade
|
|
client = ConnectClient();
|
|
client.Write("PASS :hunter2");
|
|
client.Write("NICK nick");
|
|
client.Write("USER foo x x :x");
|
|
client.ReadUntil("Welcome");
|
|
client.Close();
|
|
}
|
|
|
|
TEST_F(ZNCTest, CapReqWithoutLs) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
|
|
auto client = ConnectClient();
|
|
client.Write("CAP REQ nonono");
|
|
client.Write("PASS :hunter2");
|
|
client.Write("NICK nick");
|
|
client.Write("USER foo x x :x");
|
|
ASSERT_THAT(client.ReadRemainder().toStdString(), Not(HasSubstr("Welcome")));
|
|
}
|
|
|
|
TEST_F(ZNCTest, ChgHostEmulation) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
ircd.Write("CAP user LS :chghost");
|
|
ircd.ReadUntil("CAP REQ :chghost");
|
|
ircd.Write("CAP user ACK :chghost");
|
|
|
|
auto client1 = LoginClient();
|
|
auto client2 = LoginClient();
|
|
client2.Write("CAP REQ :chghost");
|
|
client2.ReadUntil("ACK");
|
|
|
|
ircd.Write(":user!oldident@oldhost JOIN #chan");
|
|
|
|
ircd.Write(":user!oldident@oldhost CHGHOST newident newhost");
|
|
client1.ReadUntil(":user!oldident@oldhost QUIT :Changing hostname");
|
|
client1.ReadUntil(":user!newident@newhost JOIN #chan");
|
|
ASSERT_THAT(client1.ReadRemainder().toStdString(), Not(HasSubstr("MODE")));
|
|
client2.ReadUntil(":user!oldident@oldhost CHGHOST newident newhost");
|
|
client2.Close();
|
|
|
|
ircd.Write(":server MODE #chan +v user");
|
|
client1.ReadUntil("MODE");
|
|
ircd.Write(":user!newident@newhost CHGHOST ident-2 host-2");
|
|
client1.ReadUntil(":irc.znc.in MODE #chan +v user");
|
|
|
|
ircd.Write(":server MODE #chan +o user");
|
|
client1.ReadUntil("MODE");
|
|
ircd.Write(":user!ident-2@host-2 CHGHOST ident-3 host-3");
|
|
client1.ReadUntil(":irc.znc.in MODE #chan +ov user user");
|
|
|
|
// Only attached channel should receive emulation
|
|
ircd.Write(":user!ident-3@host-3 JOIN #chan2");
|
|
client1.ReadUntil("JOIN #chan2");
|
|
client1.Write("DETACH #chan2");
|
|
client1.ReadUntil("Detached 1 channel");
|
|
ircd.Write(":user!ident-3@host-3 CHGHOST ident host");
|
|
ASSERT_THAT(client1.ReadRemainder().toStdString(),
|
|
Not(HasSubstr("#chan2")));
|
|
}
|
|
|
|
TEST_F(ZNCTest, ChgHostOnce) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
ircd.Write("CAP user LS :chghost");
|
|
ircd.ReadUntil("CAP REQ :chghost");
|
|
ircd.Write("CAP user ACK :chghost");
|
|
|
|
auto client = LoginClient();
|
|
client.Write("CAP REQ :chghost");
|
|
client.ReadUntil("ACK");
|
|
|
|
ircd.Write(":user!oldident@oldhost JOIN #chan");
|
|
ircd.Write(":user!oldident@oldhost JOIN #chan2");
|
|
ircd.Write(":user!oldident@oldhost CHGHOST newident newhost");
|
|
client.ReadUntil("CHGHOST");
|
|
ASSERT_THAT(client.ReadRemainder().toStdString(),
|
|
Not(HasSubstr("CHGHOST")));
|
|
}
|
|
|
|
TEST_F(ZNCTest, ChgHostOnlyNicksAlreadyOnChannels) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
ircd.Write("CAP user LS :chghost");
|
|
ircd.ReadUntil("CAP REQ :chghost");
|
|
ircd.Write("CAP user ACK :chghost");
|
|
|
|
auto client = LoginClient();
|
|
ircd.Write(":user!ident@host JOIN #chan1");
|
|
ircd.Write(":user!ident@host JOIN #chan2");
|
|
ircd.Write(":another!ident@host JOIN #chan1");
|
|
client.ReadUntil("another");
|
|
|
|
ircd.Write(":another!ident@host CHGHOST i2 h2");
|
|
ASSERT_THAT(client.ReadRemainder().toStdString(),
|
|
AllOf(HasSubstr("JOIN #chan1"),
|
|
Not(HasSubstr("JOIN #chan2"))));
|
|
}
|
|
|
|
TEST_F(ZNCTest, SaslAuthPlainSimple) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
auto client = ConnectClient();
|
|
client.Write("NICK foo");
|
|
client.Write("CAP LS");
|
|
client.ReadUntil(" sasl ");
|
|
client.Write("CAP REQ :sasl");
|
|
client.ReadUntil(":irc.znc.in CAP foo ACK :sasl");
|
|
client.Write("USER bar");
|
|
client.Write("AUTHENTICATE PLAIN");
|
|
client.ReadUntil("AUTHENTICATE +");
|
|
client.Write("AUTHENTICATE " + QByteArrayLiteral("\0user\0hunter2").toBase64());
|
|
client.ReadUntil(":irc.znc.in 903 foo :SASL authentication successful");
|
|
}
|
|
|
|
TEST_F(ZNCTest, SaslAuthPlainCopyInZ) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
auto client = ConnectClient();
|
|
client.Write("NICK foo");
|
|
client.Write("CAP LS");
|
|
client.ReadUntil(" sasl ");
|
|
client.Write("CAP REQ :sasl");
|
|
client.ReadUntil(":irc.znc.in CAP foo ACK :sasl");
|
|
client.Write("USER bar");
|
|
client.Write("AUTHENTICATE PLAIN");
|
|
client.ReadUntil("AUTHENTICATE +");
|
|
client.Write("AUTHENTICATE " + QByteArrayLiteral("user@phone\0user@phone\0hunter2").toBase64());
|
|
client.ReadUntil(":irc.znc.in 903 foo :SASL authentication successful");
|
|
client.Write("CAP END");
|
|
client.Write("znc listclients");
|
|
client.ReadUntil("phone");
|
|
}
|
|
|
|
TEST_F(ZNCTest, SaslAuthPlainPartialInZ) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
auto client = ConnectClient();
|
|
client.Write("NICK foo");
|
|
client.Write("CAP LS");
|
|
client.ReadUntil(" sasl ");
|
|
client.Write("CAP REQ :sasl");
|
|
client.ReadUntil(":irc.znc.in CAP foo ACK :sasl");
|
|
client.Write("USER bar");
|
|
client.Write("AUTHENTICATE PLAIN");
|
|
client.ReadUntil("AUTHENTICATE +");
|
|
client.Write("AUTHENTICATE " + QByteArrayLiteral("user@phone\0user\0hunter2").toBase64());
|
|
client.ReadUntil(":irc.znc.in 903 foo :SASL authentication successful");
|
|
client.Write("CAP END");
|
|
client.Write("znc listclients");
|
|
client.ReadUntil("phone");
|
|
}
|
|
|
|
TEST_F(ZNCTest, SaslAuthPlainDifferentZ) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
auto client = ConnectClient();
|
|
client.Write("NICK foo");
|
|
client.Write("CAP LS");
|
|
client.ReadUntil(" sasl ");
|
|
client.Write("CAP REQ :sasl");
|
|
client.ReadUntil(":irc.znc.in CAP foo ACK :sasl");
|
|
client.Write("USER bar");
|
|
client.Write("AUTHENTICATE PLAIN");
|
|
client.ReadUntil("AUTHENTICATE +");
|
|
client.Write("AUTHENTICATE " + QByteArrayLiteral("user@phone\0user@tablet\0hunter2").toBase64());
|
|
client.ReadUntil(":irc.znc.in 904 foo :No support for custom AuthzId");
|
|
}
|
|
|
|
TEST_F(ZNCTest, SaslAuthPlainWrongPassword) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
auto client = ConnectClient();
|
|
client.Write("NICK foo");
|
|
client.Write("CAP LS");
|
|
client.ReadUntil(" sasl ");
|
|
client.Write("CAP REQ :sasl");
|
|
client.ReadUntil(":irc.znc.in CAP foo ACK :sasl");
|
|
client.Write("USER bar");
|
|
client.Write("AUTHENTICATE PLAIN");
|
|
client.ReadUntil("AUTHENTICATE +");
|
|
client.Write("AUTHENTICATE " + QByteArrayLiteral("\0user\0hunter3").toBase64());
|
|
client.ReadUntil(":irc.znc.in 904 foo :Invalid Password");
|
|
|
|
// Try again on the same connection
|
|
client.Write("AUTHENTICATE PLAIN");
|
|
client.ReadUntil(":irc.znc.in 904 foo :SASL authentication failed");
|
|
}
|
|
|
|
TEST_F(ZNCTest, SaslAuthPlainWrongUser) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
auto client = ConnectClient();
|
|
client.Write("NICK foo");
|
|
client.Write("CAP LS");
|
|
client.ReadUntil(" sasl ");
|
|
client.Write("CAP REQ :sasl");
|
|
client.ReadUntil(":irc.znc.in CAP foo ACK :sasl");
|
|
client.Write("USER bar");
|
|
client.Write("AUTHENTICATE PLAIN");
|
|
client.ReadUntil("AUTHENTICATE +");
|
|
client.Write("AUTHENTICATE " + QByteArrayLiteral("\0anotheruser\0hunter2").toBase64());
|
|
client.ReadUntil(":irc.znc.in 904 foo :Invalid Password");
|
|
}
|
|
|
|
TEST_F(ZNCTest, SaslAuthUserAfterCapEnd) {
|
|
// kvirc sends this
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
auto client = ConnectClient();
|
|
client.Write("CAP LS");
|
|
client.Write("PING :::1");
|
|
client.Write("CAP REQ :sasl");
|
|
client.Write("AUTHENTICATE PLAIN");
|
|
client.Write("AUTHENTICATE " +
|
|
QByteArrayLiteral("\0user\0hunter2").toBase64());
|
|
client.Write("CAP END");
|
|
client.ReadUntil("903 unknown-nick :SASL authentication successful");
|
|
client.Write("NICK nick");
|
|
client.Write("USER user 0 1 :2");
|
|
client.ReadUntil("001");
|
|
}
|
|
|
|
TEST_F(ZNCTest, SaslAuthAbort) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
auto client = ConnectClient();
|
|
client.Write("NICK foo");
|
|
client.Write("CAP LS");
|
|
client.ReadUntil(" sasl ");
|
|
client.Write("CAP REQ :sasl");
|
|
client.ReadUntil(":irc.znc.in CAP foo ACK :sasl");
|
|
client.Write("USER bar");
|
|
client.Write("AUTHENTICATE PLAIN");
|
|
client.ReadUntil("AUTHENTICATE +");
|
|
client.Write("AUTHENTICATE *");
|
|
client.ReadUntil(":irc.znc.in 906 foo :SASL authentication aborted");
|
|
}
|
|
|
|
TEST_F(ZNCTest, SpacedServerPassword) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
auto client = LoginClient();
|
|
client.Write(("znc delserver unix:" + m_dir.path() + "/inttest.ircd").toUtf8());
|
|
client.Write(("znc addserver unix:" + m_dir.path() + "/inttest.ircd a b").toUtf8());
|
|
client.Write("znc jump");
|
|
auto ircd2 = ConnectIRCd();
|
|
ircd2.ReadUntil("PASS :a b");
|
|
client.Write(("znc delserver unix:" + m_dir.path() + "/inttest.ircd").toUtf8());
|
|
client.Write(("znc addserver unix:" + m_dir.path() + "/inttest.ircd a").toUtf8());
|
|
client.Write("znc jump");
|
|
auto ircd3 = ConnectIRCd();
|
|
// No :
|
|
ircd3.ReadUntil("PASS a");
|
|
}
|
|
|
|
TEST_F(ZNCTest, TagMsg) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
auto client = LoginClient();
|
|
ircd.Write("001 nick Welcome");
|
|
|
|
client.Write("@foo=bar PRIVMSG #foo hi");
|
|
ircd.ReadUntil("\nPRIVMSG");
|
|
|
|
client.Write("TAGMSG #foo");
|
|
ASSERT_THAT(ircd.ReadRemainder().toStdString(), Not(HasSubstr("TAGMSG")));
|
|
|
|
ircd.Write("@foo=bar :friend PRIVMSG #foo hi");
|
|
client.ReadUntil("\n:friend PRIVMSG");
|
|
|
|
ircd.Write("TAGMSG #foo");
|
|
ASSERT_THAT(client.ReadRemainder().toStdString(), Not(HasSubstr("TAGMSG")));
|
|
|
|
client.Write("CAP REQ :message-tags");
|
|
client.ReadUntil("ACK");
|
|
|
|
ircd.Write("@foo TAGMSG #foo");
|
|
client.ReadUntil("@foo TAGMSG #foo");
|
|
|
|
ircd.Write("CAP * ACK message-tags");
|
|
// barrier to make client wait before sending TAGMSG
|
|
ircd.Write(":friend PRIVMSG #foo hi");
|
|
client.ReadUntil("friend");
|
|
|
|
client.Write("@foo=bar TAGMSG #foo");
|
|
ircd.ReadUntil("@foo=bar TAGMSG #foo");
|
|
|
|
client.Write("CAP REQ echo-message");
|
|
client.Write("@baz TAGMSG #foo");
|
|
client.ReadUntil("@baz :nick TAGMSG #foo");
|
|
|
|
// Check buffers
|
|
client.Write("znc addnetwork other");
|
|
client.Write("znc jumpnetwork other");
|
|
client.ReadUntil(":Switched to other");
|
|
|
|
ircd.Write(":nick JOIN #bar");
|
|
ircd.Write("@bar TAGMSG #bar");
|
|
ASSERT_THAT(client.ReadRemainder().toStdString(), Not(HasSubstr("TAGMSG")));
|
|
client.Write("znc jumpnetwork test");
|
|
client.ReadUntil("@bar TAGMSG #bar");
|
|
}
|
|
|
|
TEST_F(ZNCTest, StatusAction) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
auto client = LoginClient();
|
|
ircd.Write("001 nick Welcome");
|
|
|
|
client.Write("PRIVMSG *status :\1ACTION waves\1");
|
|
client.Write("PRIVMSG *status :\1VERSION\1");
|
|
ASSERT_THAT(ircd.ReadRemainder().toStdString(), Not(HasSubstr("PRIVMSG")));
|
|
}
|
|
|
|
TEST_F(ZNCTest, InviteNotify) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
auto client = LoginClient();
|
|
client.Write("CAP REQ invite-notify");
|
|
client.ReadUntil("ACK");
|
|
auto client2 = LoginClient();
|
|
|
|
InstallModule("testmod.cpp", R"(
|
|
#include <znc/Modules.h>
|
|
class TestModule : public CModule {
|
|
public:
|
|
MODCONSTRUCTOR(TestModule) {}
|
|
EModRet OnInviteMessage(CInviteMessage& Msg) override {
|
|
PutIRC("__MSG__ " + Msg.ToString());
|
|
return CONTINUE;
|
|
}
|
|
EModRet OnInvite(const CNick& Nick, const CString& sChan) override {
|
|
PutIRC("__INV__ " + Nick.GetNickMask() + " " + sChan);
|
|
return CONTINUE;
|
|
}
|
|
};
|
|
GLOBALMODULEDEFS(TestModule, "Test")
|
|
)");
|
|
client.Write("znc loadmod testmod");
|
|
client.ReadUntil("Loaded module testmod");
|
|
|
|
ircd.Write("001 nick Welcome");
|
|
|
|
ircd.Write(":source!id@ho INVITE nick #chan");
|
|
client.ReadUntil(":source!id@ho INVITE nick #chan");
|
|
client2.ReadUntil(":source!id@ho INVITE nick #chan");
|
|
ircd.ReadUntil("__MSG__ :source!id@ho INVITE nick #chan");
|
|
ircd.ReadUntil("__INV__ source!id@ho #chan");
|
|
|
|
ircd.Write(":source!id@ho INVITE someone #chan");
|
|
client.ReadUntil(":source!id@ho INVITE someone #chan");
|
|
ASSERT_THAT(client2.ReadRemainder().toStdString(), Not(HasSubstr("someone")));
|
|
ircd.ReadUntil("__MSG__ :source!id@ho INVITE someone #chan");
|
|
ASSERT_THAT(ircd.ReadRemainder().toStdString(), Not(HasSubstr("__INV__")));
|
|
}
|
|
|
|
TEST_F(ZNCTest, DisableCap) {
|
|
{
|
|
QFile conf(m_dir.path() + "/configs/znc.conf");
|
|
ASSERT_TRUE(conf.open(QIODevice::Append | QIODevice::Text));
|
|
QTextStream out(&conf);
|
|
out << R"(
|
|
DisableClientCap = sasl
|
|
DisableClientCap = away-notify
|
|
DisableServerCap = chghost
|
|
)";
|
|
}
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
|
|
ircd.Write("CAP user LS :chghost");
|
|
ASSERT_THAT(ircd.ReadRemainder().toStdString(), Not(HasSubstr("chghost")));
|
|
|
|
auto client = ConnectClient();
|
|
client.Write("NICK foo");
|
|
client.Write("CAP LS 302");
|
|
ASSERT_THAT(client.ReadRemainder().toStdString(), Not(HasSubstr("sasl")));
|
|
client.Write("CAP REQ sasl");
|
|
client.ReadUntil("CAP foo NAK :sasl");
|
|
|
|
client.Write("PASS :hunter2");
|
|
client.Write("USER user/test x x :x");
|
|
client.Write("CAP END");
|
|
client.ReadUntil("001");
|
|
|
|
// Server-dependent
|
|
ircd.Write("001 nick Welcome");
|
|
ircd.Write("CAP nick NEW away-notify");
|
|
ircd.ReadUntil("CAP REQ :away-notify");
|
|
ircd.Write("CAP nick ACK away-notify");
|
|
ASSERT_THAT(client.ReadRemainder().toStdString(),
|
|
Not(AllOf(HasSubstr("NEW"), HasSubstr("away-notify"))));
|
|
}
|
|
|
|
TEST_F(ZNCTest, ManyCapsInReq) {
|
|
constexpr int prefix = std::string_view("CAP NAK :").length();
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
ircd.Write(
|
|
"CAP user LS :away-notify invite-notify extended-join "
|
|
"userhost-in-names multi-prefix cap-notify "
|
|
"sasl=ANONYMOUS,EXTERNAL,PLAIN setname tls chghost account-notify "
|
|
"message-tags batch server-time account-tag echo-message "
|
|
"labeled-response");
|
|
QByteArray caps, caps2, caps3;
|
|
ircd.ReadUntilAndGet("CAP REQ :", caps);
|
|
caps.remove(0, prefix);
|
|
EXPECT_THAT(caps.toStdString(), SizeIs(AllOf(Gt(10), Lt(100))));
|
|
ircd.Write("CAP user NAK :" + caps);
|
|
ircd.ReadUntilAndGet("CAP REQ :", caps2);
|
|
caps2.remove(0, prefix);
|
|
EXPECT_THAT(caps2.toStdString(), SizeIs(AllOf(Gt(10), Lt(100))));
|
|
ircd.Write("CAP user ACK :" + caps2);
|
|
// Now it should retry one of the first batch
|
|
ircd.ReadUntilAndGet("CAP REQ :", caps3);
|
|
caps3.remove(0, prefix);
|
|
EXPECT_THAT(caps3.toStdString(), Not(HasSubstr(" ")));
|
|
EXPECT_TRUE(caps.split(' ').contains(caps3));
|
|
}
|
|
|
|
TEST_F(ZNCTest, JoinWhileRegistration) {
|
|
auto znc = Run();
|
|
auto ircd = ConnectIRCd();
|
|
auto client = LoginClient();
|
|
// First JOIN just adds channel to the list, second one updates the key and
|
|
// sends JOIN from CChan::JoinUser. Both should be delayed until
|
|
// registration ends.
|
|
client.Write("JOIN #foo");
|
|
client.Write("JOIN #foo");
|
|
EXPECT_THAT(ircd.ReadRemainder().toStdString(), Not(HasSubstr("JOIN")));
|
|
ircd.Write(":server 001 nick :Hello");
|
|
ircd.ReadUntil("JOIN #foo");
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace znc_inttest
|