smtptest.cpp 8.34 KB
Newer Older
1
/*
2
3
4
  SPDX-FileCopyrightText: 2010 BetterInbox <contact@betterinbox.com>
  SPDX-FileContributor: Christophe Laveault <christophe@betterinbox.com>
  SPDX-FileContributor: Gregory Schlomoff <gregory.schlomoff@gmail.com>
5

6
  SPDX-License-Identifier: LGPL-2.1-or-later
7
8
*/

Gregory Schlomoff's avatar
Gregory Schlomoff committed
9
10
11
12
#include "smtptest.h"

#include <QTest>
#include "fakeserver.h"
Daniel Vrátil's avatar
Daniel Vrátil committed
13
14
15
#include "session.h"
#include "loginjob.h"
#include "sendjob.h"
Gregory Schlomoff's avatar
Gregory Schlomoff committed
16
17
18

void SmtpTest::testHello_data()
{
Daniel Vrátil's avatar
Daniel Vrátil committed
19
    QTest::addColumn<QList<QByteArray> >("scenario");
20
    QTest::addColumn<QString>("hostname");
Daniel Vrátil's avatar
Daniel Vrátil committed
21
22
23
24

    QList<QByteArray> scenario;
    scenario << FakeServer::greeting()
             << "C: EHLO 127.0.0.1"
25
26
             << "S: 250 Localhost ready to roll"
             << FakeServer::bye();
Sandro Knauß's avatar
Sandro Knauß committed
27
    QTest::newRow("EHLO OK") << scenario << QStringLiteral("127.0.0.1");
Daniel Vrátil's avatar
Daniel Vrátil committed
28
29
30
31
32
33

    scenario.clear();
    scenario << FakeServer::greeting()
             << "C: EHLO 127.0.0.1"
             << "S: 500 Command was not recognized"
             << "C: HELO 127.0.0.1"
34
35
             << "S: 250 Localhost ready to roll"
             << FakeServer::bye();
Sandro Knauß's avatar
Sandro Knauß committed
36
    QTest::newRow("EHLO unknown") << scenario << QStringLiteral("127.0.0.1");
Daniel Vrátil's avatar
Daniel Vrátil committed
37
38
39
40
41
42

    scenario.clear();
    scenario << FakeServer::greeting()
             << "C: EHLO 127.0.0.1"
             << "S: 502 Command not implemented"
             << "C: HELO 127.0.0.1"
43
44
             << "S: 250 Localhost ready to roll"
             << FakeServer::bye();
Sandro Knauß's avatar
Sandro Knauß committed
45
    QTest::newRow("EHLO not implemented") << scenario << QStringLiteral("127.0.0.1");
Daniel Vrátil's avatar
Daniel Vrátil committed
46
47
48
49
50
51

    scenario.clear();
    scenario << FakeServer::greeting()
             << "C: EHLO 127.0.0.1"
             << "S: 502 Command not implemented"
             << "C: HELO 127.0.0.1"
52
53
             << "S: 500 Command was not recognized"
             << FakeServer::bye();
Sandro Knauß's avatar
Sandro Knauß committed
54
    QTest::newRow("ERROR") << scenario << QStringLiteral("127.0.0.1");
Gregory Schlomoff's avatar
Gregory Schlomoff committed
55

56
57
58
59
60
61
    scenario.clear();
    scenario << FakeServer::greeting()
             << "C: EHLO random.stranger"
             << "S: 250 random.stranger ready to roll"
             << FakeServer::bye();
    QTest::newRow("EHLO hostname") << scenario << QStringLiteral("random.stranger");
Gregory Schlomoff's avatar
Gregory Schlomoff committed
62
63
64
65
}

void SmtpTest::testHello()
{
Daniel Vrátil's avatar
Daniel Vrátil committed
66
    QFETCH(QList<QByteArray>, scenario);
67
    QFETCH(QString, hostname);
Daniel Vrátil's avatar
Daniel Vrátil committed
68
69
70
71
72

    FakeServer fakeServer;
    fakeServer.setScenario(scenario);
    fakeServer.startAndWait();
    KSmtp::Session session(QStringLiteral("127.0.0.1"), 5989);
73
    session.setCustomHostname(hostname);
Daniel Vrátil's avatar
Daniel Vrátil committed
74
75
76
77
    session.openAndWait();

    qDebug() << "### Session state is:" << session.state();

Laurent Montel's avatar
Laurent Montel committed
78
    QEXPECT_FAIL("ERROR", "Expected failure if HELO command not recognized", Continue);
Daniel Vrátil's avatar
Daniel Vrátil committed
79
80
    QVERIFY2(session.state() == KSmtp::Session::NotAuthenticated, "Handshake failed");

81
82
83
    session.quitAndWait();

    QVERIFY(fakeServer.isAllScenarioDone());
Daniel Vrátil's avatar
Daniel Vrátil committed
84
    fakeServer.quit();
Gregory Schlomoff's avatar
Gregory Schlomoff committed
85
86
87
88
}

void SmtpTest::testLoginJob_data()
{
Daniel Vrátil's avatar
Daniel Vrátil committed
89
90
91
92
93
94
95
96
    QTest::addColumn<QList<QByteArray> >("scenario");
    QTest::addColumn<QString>("authMode");
    QTest::addColumn<int>("errorCode");

    QList<QByteArray> scenario;
    scenario << FakeServer::greetingAndEhlo()
             << "S: 250 AUTH PLAIN LOGIN"
             << "C: AUTH PLAIN AGxvZ2luAHBhc3N3b3Jk" // [\0 + "login" + \0 + "password"].toBase64()
97
98
             << "S: 235 Authenticated"
             << FakeServer::bye();
Daniel Vrátil's avatar
Daniel Vrátil committed
99
100
101
102
103
104
105
106
107
108
    QTest::newRow("Plain auth ok") << scenario << "Plain" << 0;

    scenario.clear();
    scenario << FakeServer::greetingAndEhlo()
             << "S: 250 AUTH PLAIN LOGIN"
             << "C: AUTH LOGIN"
             << "S: 334 VXNlcm5hbWU6"   // "Username:".toBase64()
             << "C: bG9naW4="           // "login".toBase64()
             << "S: 334 UGFzc3dvcmQ6"   // "Password:".toBase64()
             << "C: cGFzc3dvcmQ="       // "password".toBase64()
109
110
             << "S: 235 Authenticated"
             << FakeServer::bye();
Daniel Vrátil's avatar
Daniel Vrátil committed
111
112
113
114
115
116
    QTest::newRow("Login auth ok") << scenario << "Login" << 0;

    scenario.clear();
    scenario << FakeServer::greetingAndEhlo()
             << "S: 250 AUTH PLAIN"
             << "C: AUTH PLAIN AGxvZ2luAHBhc3N3b3Jk" // [\0 + "login" + \0 + "password"].toBase64()
117
118
             << "S: 235 Authenticated"
             << FakeServer::bye();
Daniel Vrátil's avatar
Daniel Vrátil committed
119
120
121
    QTest::newRow("Login not supported") << scenario << "Login" << 0;

    scenario.clear();
122
    scenario << FakeServer::greetingAndEhlo(false)
Laurent Montel's avatar
Laurent Montel committed
123
124
125
126
        // The login job won't even try to send AUTH, because it does not
        // have any mechanisms to use
        //<< "C: AUTH PLAIN AGxvZ2luAHBhc3N3b3Jk" // [\0 + "login" + \0 + "password"].toBase64()
        //<< "S: 235 Authenticated"
127
             << FakeServer::bye();
Daniel Vrátil's avatar
Daniel Vrátil committed
128
129
130
131
132
133
    QTest::newRow("Auth not supported") << scenario << "Login" << 100;

    scenario.clear();
    scenario << FakeServer::greetingAndEhlo()
             << "S: 250 AUTH PLAIN"
             << "C: AUTH PLAIN AGxvZ2luAHBhc3N3b3Jk" // [\0 + "login" + \0 + "password"].toBase64()
134
135
             << "S: 535 Authorization failed"
             << FakeServer::bye();
Daniel Vrátil's avatar
Daniel Vrátil committed
136
    QTest::newRow("Wrong password") << scenario << "Plain" << 100;
Gregory Schlomoff's avatar
Gregory Schlomoff committed
137
138
139
140
}

void SmtpTest::testLoginJob()
{
Daniel Vrátil's avatar
Daniel Vrátil committed
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
    QFETCH(QList<QByteArray>, scenario);
    QFETCH(QString, authMode);
    QFETCH(int, errorCode);

    KSmtp::LoginJob::AuthMode mode = KSmtp::LoginJob::UnknownAuth;
    if (authMode == QLatin1String("Plain")) {
        mode = KSmtp::LoginJob::Plain;
    } else if (authMode == QLatin1String("Login")) {
        mode = KSmtp::LoginJob::Login;
    }

    FakeServer fakeServer;
    fakeServer.setScenario(scenario);
    fakeServer.startAndWait();
    KSmtp::Session session(QStringLiteral("127.0.0.1"), 5989);
Sandro Knauß's avatar
Sandro Knauß committed
156
    session.setCustomHostname(QStringLiteral("127.0.0.1"));
Daniel Vrátil's avatar
Daniel Vrátil committed
157
158
    session.openAndWait();

Laurent Montel's avatar
Laurent Montel committed
159
    auto *login = new KSmtp::LoginJob(&session);
Daniel Vrátil's avatar
Daniel Vrátil committed
160
161
162
163
164
165
166
167
168
    login->setPreferedAuthMode(mode);
    login->setUserName(QStringLiteral("login"));
    login->setPassword(QStringLiteral("password"));
    login->exec();

    // Checking job error code:
    QVERIFY2(login->error() == errorCode, "Unexpected LoginJob error code");

    // Checking session state:
Laurent Montel's avatar
Laurent Montel committed
169
170
    QEXPECT_FAIL("Auth not supported", "Expected failure if not authentication method suported", Continue);
    QEXPECT_FAIL("Wrong password", "Expected failure if wrong password", Continue);
Daniel Vrátil's avatar
Daniel Vrátil committed
171
172
    QVERIFY2(session.state() == KSmtp::Session::Authenticated, "Authentication failed");

173
174
175
176
    session.quitAndWait();

    QVERIFY(fakeServer.isAllScenarioDone());

Daniel Vrátil's avatar
Daniel Vrátil committed
177
    fakeServer.quit();
Gregory Schlomoff's avatar
Gregory Schlomoff committed
178
179
180
181
}

void SmtpTest::testSendJob_data()
{
Daniel Vrátil's avatar
Daniel Vrátil committed
182
183
184
185
    QTest::addColumn<QList<QByteArray> >("scenario");
    QTest::addColumn<int>("errorCode");

    QList<QByteArray> scenario;
186
    scenario << FakeServer::greetingAndEhlo(false)
Daniel Vrátil's avatar
Daniel Vrátil committed
187
             << "C: MAIL FROM:<foo@bar.com>"
188
189
             << "S: 530 Not allowed"
             << FakeServer::bye();
Daniel Vrátil's avatar
Daniel Vrátil committed
190
191
192
    QTest::newRow("Send not allowed") << scenario << 100;

    scenario.clear();
193
    scenario << FakeServer::greetingAndEhlo(false)
Daniel Vrátil's avatar
Daniel Vrátil committed
194
195
196
197
198
199
             << "C: MAIL FROM:<foo@bar.com>"
             << "S: 250 ok"
             << "C: RCPT TO:<bar@foo.com>"
             << "S: 250 ok"
             << "C: DATA"
             << "S: 354 Ok go ahead"
200
201
             << "C: From: foo@bar.com"
             << "C: To: bar@foo.com"
202
203
204
205
206
207
             << "C: Hello world."
             << "C: .." // Single dot becomes two
             << "C: .." // Single dot becomes two
             << "C: ..." // Two dots become three
             << "C: ..Foo"  // .Foo becomes ..Foo
             << "C: End"
208
             << "C: "
Daniel Vrátil's avatar
Daniel Vrátil committed
209
             << "C: ."
210
211
             << "S: 250 Ok transfer done"
             << FakeServer::bye();
Daniel Vrátil's avatar
Daniel Vrátil committed
212
213
214
    QTest::newRow("ok") << scenario << 0;

    scenario.clear();
Gregory Schlomoff's avatar
Gregory Schlomoff committed
215
216
217
218
}

void SmtpTest::testSendJob()
{
Daniel Vrátil's avatar
Daniel Vrátil committed
219
220
221
222
223
224
225
    QFETCH(QList<QByteArray>, scenario);
    QFETCH(int, errorCode);

    FakeServer fakeServer;
    fakeServer.setScenario(scenario);
    fakeServer.startAndWait();
    KSmtp::Session session(QStringLiteral("127.0.0.1"), 5989);
Sandro Knauß's avatar
Sandro Knauß committed
226
    session.setCustomHostname(QStringLiteral("127.0.0.1"));
Daniel Vrátil's avatar
Daniel Vrátil committed
227
228
    session.openAndWait();

Laurent Montel's avatar
Laurent Montel committed
229
    auto *send = new KSmtp::SendJob(&session);
230
    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");
231
232
    send->setFrom(QStringLiteral("foo@bar.com"));
    send->setTo({QStringLiteral("bar@foo.com")});
Daniel Vrátil's avatar
Daniel Vrátil committed
233
234
235
    send->exec();

    // Checking job error code:
236
    QVERIFY2(send->error() == errorCode, qPrintable(QStringLiteral("Unexpected LoginJob error: ") + send->errorString()));
Daniel Vrátil's avatar
Daniel Vrátil committed
237

238
239
240
    session.quitAndWait();

    QVERIFY(fakeServer.isAllScenarioDone());
Daniel Vrátil's avatar
Daniel Vrátil committed
241
    fakeServer.quit();
Gregory Schlomoff's avatar
Gregory Schlomoff committed
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
}

SmtpTest::SmtpTest()
{
}

void SmtpTest::initTestCase()
{
}

void SmtpTest::cleanupTestCase()
{
}

QTEST_MAIN(SmtpTest)