File indexing completed on 2024-05-12 05:21:35

0001 /*
0002   SPDX-FileCopyrightText: 2010 BetterInbox <contact@betterinbox.com>
0003   SPDX-FileContributor: Christophe Laveault <christophe@betterinbox.com>
0004   SPDX-FileContributor: Gregory Schlomoff <gregory.schlomoff@gmail.com>
0005 
0006   SPDX-License-Identifier: LGPL-2.1-or-later
0007 */
0008 
0009 #include "smtptest.h"
0010 
0011 #include "fakeserver.h"
0012 #include "loginjob.h"
0013 #include "sendjob.h"
0014 #include "session.h"
0015 #include <QTest>
0016 
0017 void SmtpTest::testHello_data()
0018 {
0019     QTest::addColumn<QList<QByteArray>>("scenario");
0020     QTest::addColumn<QString>("hostname");
0021 
0022     QList<QByteArray> scenario;
0023     scenario << FakeServer::greeting() << "C: EHLO 127.0.0.1"
0024              << "S: 250 Localhost ready to roll" << FakeServer::bye();
0025     QTest::newRow("EHLO OK") << scenario << QStringLiteral("127.0.0.1");
0026 
0027     scenario.clear();
0028     scenario << FakeServer::greeting() << "C: EHLO 127.0.0.1"
0029              << "S: 500 Command was not recognized"
0030              << "C: HELO 127.0.0.1"
0031              << "S: 250 Localhost ready to roll" << FakeServer::bye();
0032     QTest::newRow("EHLO unknown") << scenario << QStringLiteral("127.0.0.1");
0033 
0034     scenario.clear();
0035     scenario << FakeServer::greeting() << "C: EHLO 127.0.0.1"
0036              << "S: 502 Command not implemented"
0037              << "C: HELO 127.0.0.1"
0038              << "S: 250 Localhost ready to roll" << FakeServer::bye();
0039     QTest::newRow("EHLO not implemented") << scenario << QStringLiteral("127.0.0.1");
0040 
0041     scenario.clear();
0042     scenario << FakeServer::greeting() << "C: EHLO 127.0.0.1"
0043              << "S: 502 Command not implemented"
0044              << "C: HELO 127.0.0.1"
0045              << "S: 500 Command was not recognized" << FakeServer::bye();
0046     QTest::newRow("ERROR") << scenario << QStringLiteral("127.0.0.1");
0047 
0048     scenario.clear();
0049     scenario << FakeServer::greeting() << "C: EHLO random.stranger"
0050              << "S: 250 random.stranger ready to roll" << FakeServer::bye();
0051     QTest::newRow("EHLO hostname") << scenario << QStringLiteral("random.stranger");
0052 }
0053 
0054 void SmtpTest::testHello()
0055 {
0056     QFETCH(QList<QByteArray>, scenario);
0057     QFETCH(QString, hostname);
0058 
0059     FakeServer fakeServer;
0060     fakeServer.setScenario(scenario);
0061     fakeServer.startAndWait();
0062     KSmtp::Session session(QStringLiteral("127.0.0.1"), 5989);
0063     session.setCustomHostname(hostname);
0064     QEventLoop loop;
0065     connect(&session, &KSmtp::Session::stateChanged, this, [&loop](auto state) {
0066         qDebug() << state;
0067         if (state == KSmtp::Session::NotAuthenticated || state == KSmtp::Session::Disconnected) {
0068             loop.quit();
0069         }
0070     });
0071     session.open();
0072     loop.exec();
0073 
0074     qDebug() << "### Session state is:" << session.state();
0075 
0076     QEXPECT_FAIL("ERROR", "Expected failure if HELO command not recognized", Continue);
0077     QVERIFY2(session.state() == KSmtp::Session::NotAuthenticated, "Handshake failed");
0078 
0079     session.quit();
0080     if (session.state() != KSmtp::Session::Disconnected) {
0081         loop.exec();
0082     }
0083 
0084     QVERIFY(fakeServer.isAllScenarioDone());
0085     fakeServer.quit();
0086 }
0087 
0088 void SmtpTest::testLoginJob_data()
0089 {
0090     QTest::addColumn<QList<QByteArray>>("scenario");
0091     QTest::addColumn<QString>("authMode");
0092     QTest::addColumn<int>("errorCode");
0093 
0094     QList<QByteArray> scenario;
0095     scenario << FakeServer::greetingAndEhlo() << "S: 250 AUTH PLAIN LOGIN"
0096              << "C: AUTH PLAIN AGxvZ2luAHBhc3N3b3Jk" // [\0 + "login" + \0 + "password"].toBase64()
0097              << "S: 235 Authenticated" << FakeServer::bye();
0098     QTest::newRow("Plain auth ok") << scenario << "Plain" << 0;
0099 
0100     scenario.clear();
0101     scenario << FakeServer::greetingAndEhlo() << "S: 250 AUTH PLAIN LOGIN"
0102              << "C: AUTH LOGIN"
0103              << "S: 334 VXNlcm5hbWU6" // "Username:".toBase64()
0104              << "C: bG9naW4=" // "login".toBase64()
0105              << "S: 334 UGFzc3dvcmQ6" // "Password:".toBase64()
0106              << "C: cGFzc3dvcmQ=" // "password".toBase64()
0107              << "S: 235 Authenticated" << FakeServer::bye();
0108     QTest::newRow("Login auth ok") << scenario << "Login" << 0;
0109 
0110     scenario.clear();
0111     scenario << FakeServer::greetingAndEhlo() << "S: 250 AUTH PLAIN"
0112              << "C: AUTH PLAIN AGxvZ2luAHBhc3N3b3Jk" // [\0 + "login" + \0 + "password"].toBase64()
0113              << "S: 235 Authenticated" << FakeServer::bye();
0114     QTest::newRow("Login not supported") << scenario << "Login" << 0;
0115 
0116     scenario.clear();
0117     scenario << FakeServer::greetingAndEhlo(false)
0118              // The login job won't even try to send AUTH, because it does not
0119              // have any mechanisms to use
0120              //<< "C: AUTH PLAIN AGxvZ2luAHBhc3N3b3Jk" // [\0 + "login" + \0 + "password"].toBase64()
0121              //<< "S: 235 Authenticated"
0122              << FakeServer::bye();
0123     QTest::newRow("Auth not supported") << scenario << "Login" << 100;
0124 
0125     scenario.clear();
0126     scenario << FakeServer::greetingAndEhlo() << "S: 250 AUTH PLAIN"
0127              << "C: AUTH PLAIN AGxvZ2luAHBhc3N3b3Jk" // [\0 + "login" + \0 + "password"].toBase64()
0128              << "S: 535 Authorization failed" << FakeServer::bye();
0129     QTest::newRow("Wrong password") << scenario << "Plain" << 100;
0130 }
0131 
0132 void SmtpTest::testLoginJob()
0133 {
0134     QFETCH(QList<QByteArray>, scenario);
0135     QFETCH(QString, authMode);
0136     QFETCH(int, errorCode);
0137 
0138     KSmtp::LoginJob::AuthMode mode = KSmtp::LoginJob::UnknownAuth;
0139     if (authMode == QLatin1StringView("Plain")) {
0140         mode = KSmtp::LoginJob::Plain;
0141     } else if (authMode == QLatin1StringView("Login")) {
0142         mode = KSmtp::LoginJob::Login;
0143     }
0144 
0145     FakeServer fakeServer;
0146     fakeServer.setScenario(scenario);
0147     fakeServer.startAndWait();
0148     KSmtp::Session session(QStringLiteral("127.0.0.1"), 5989);
0149     session.setCustomHostname(QStringLiteral("127.0.0.1"));
0150     QEventLoop loop;
0151     connect(&session, &KSmtp::Session::stateChanged, this, [&loop](auto state) {
0152         if (state == KSmtp::Session::NotAuthenticated || state == KSmtp::Session::Disconnected) {
0153             loop.quit();
0154         }
0155     });
0156     session.open();
0157     loop.exec();
0158 
0159     auto login = new KSmtp::LoginJob(&session);
0160     login->setPreferedAuthMode(mode);
0161     login->setUserName(QStringLiteral("login"));
0162     login->setPassword(QStringLiteral("password"));
0163     login->exec();
0164 
0165     // Checking job error code:
0166     QVERIFY2(login->error() == errorCode, "Unexpected LoginJob error code");
0167 
0168     // Checking session state:
0169     QEXPECT_FAIL("Auth not supported", "Expected failure if not authentication method supported", Continue);
0170     QEXPECT_FAIL("Wrong password", "Expected failure if wrong password", Continue);
0171     QVERIFY2(session.state() == KSmtp::Session::Authenticated, "Authentication failed");
0172 
0173     session.quit();
0174     loop.exec();
0175 
0176     QVERIFY(fakeServer.isAllScenarioDone());
0177 
0178     fakeServer.quit();
0179 }
0180 
0181 void SmtpTest::testSendJob_data()
0182 {
0183     QTest::addColumn<QList<QByteArray>>("scenario");
0184     QTest::addColumn<int>("errorCode");
0185 
0186     QList<QByteArray> scenario;
0187     scenario << FakeServer::greetingAndEhlo(false) << "C: MAIL FROM:<foo@bar.com>"
0188              << "S: 530 Not allowed" << FakeServer::bye();
0189     QTest::newRow("Send not allowed") << scenario << 100;
0190 
0191     scenario.clear();
0192     scenario << FakeServer::greetingAndEhlo(false) << "C: MAIL FROM:<foo@bar.com>"
0193              << "S: 250 ok"
0194              << "C: RCPT TO:<bar@foo.com>"
0195              << "S: 250 ok"
0196              << "C: DATA"
0197              << "S: 354 Ok go ahead"
0198              << "C: From: foo@bar.com"
0199              << "C: To: bar@foo.com"
0200              << "C: Hello world."
0201              << "C: .." // Single dot becomes two
0202              << "C: .." // Single dot becomes two
0203              << "C: ..." // Two dots become three
0204              << "C: ..Foo" // .Foo becomes ..Foo
0205              << "C: End"
0206              << "C: "
0207              << "C: ."
0208              << "S: 250 Ok transfer done" << FakeServer::bye();
0209     QTest::newRow("ok") << scenario << 0;
0210 
0211     scenario.clear();
0212 }
0213 
0214 void SmtpTest::testSendJob()
0215 {
0216     QFETCH(QList<QByteArray>, scenario);
0217     QFETCH(int, errorCode);
0218 
0219     FakeServer fakeServer;
0220     fakeServer.setScenario(scenario);
0221     fakeServer.startAndWait();
0222     KSmtp::Session session(QStringLiteral("127.0.0.1"), 5989);
0223     session.setCustomHostname(QStringLiteral("127.0.0.1"));
0224     QEventLoop loop;
0225     connect(&session, &KSmtp::Session::stateChanged, this, [&loop](auto state) {
0226         if (state == KSmtp::Session::NotAuthenticated || state == KSmtp::Session::Disconnected) {
0227             loop.quit();
0228         }
0229     });
0230     session.open();
0231     loop.exec();
0232 
0233     auto send = new KSmtp::SendJob(&session);
0234     send->setData("From: foo@bar.com\r\nTo: bar@foo.com\r\nHello world.\r\n.\r\n.\r\n..\r\n.Foo\r\nEnd");
0235     send->setFrom(QStringLiteral("foo@bar.com"));
0236     send->setTo({QStringLiteral("bar@foo.com")});
0237     send->exec();
0238 
0239     // Checking job error code:
0240     QVERIFY2(send->error() == errorCode, qPrintable(QStringLiteral("Unexpected LoginJob error: ") + send->errorString()));
0241 
0242     session.quit();
0243     loop.exec();
0244 
0245     QVERIFY(fakeServer.isAllScenarioDone());
0246     fakeServer.quit();
0247 }
0248 
0249 SmtpTest::SmtpTest() = default;
0250 
0251 void SmtpTest::initTestCase()
0252 {
0253 }
0254 
0255 void SmtpTest::cleanupTestCase()
0256 {
0257 }
0258 
0259 QTEST_MAIN(SmtpTest)
0260 
0261 #include "moc_smtptest.cpp"