Warning, file /pim/kmailtransport/src/plugins/smtp/autotests/fakeserver.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

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 "fakeserver.h"
0010 
0011 #include <QDebug>
0012 #include <QFile>
0013 #include <QTcpServer>
0014 #include <QTest>
0015 
0016 FakeServer::FakeServer(QObject *parent)
0017     : QThread(parent)
0018 {
0019     moveToThread(this);
0020 }
0021 
0022 QByteArray FakeServer::greeting()
0023 {
0024     return "S: 220 localhost ESMTP xx777xx";
0025 }
0026 
0027 QList<QByteArray> FakeServer::greetingAndEhlo(bool multiline)
0028 {
0029     return QList<QByteArray>() << greeting() << "C: EHLO 127.0.0.1" << QByteArray("S: 250") + (multiline ? '-' : ' ') + "Localhost ready to roll";
0030 }
0031 
0032 QList<QByteArray> FakeServer::bye()
0033 {
0034     return {"C: QUIT", "S: 221 So long, and thanks for all the fish", "X: "};
0035 }
0036 
0037 FakeServer::~FakeServer()
0038 {
0039     quit();
0040     wait();
0041 }
0042 
0043 void FakeServer::startAndWait()
0044 {
0045     start();
0046     // this will block until the event queue starts
0047     QMetaObject::invokeMethod(this, &FakeServer::started, Qt::BlockingQueuedConnection);
0048 }
0049 
0050 void FakeServer::dataAvailable()
0051 {
0052     QMutexLocker locker(&m_mutex);
0053 
0054     auto socket = qobject_cast<QTcpSocket *>(sender());
0055     Q_ASSERT(socket != nullptr);
0056 
0057     int scenarioNumber = m_clientSockets.indexOf(socket);
0058 
0059     QVERIFY(!m_scenarios[scenarioNumber].isEmpty());
0060 
0061     readClientPart(scenarioNumber);
0062     writeServerPart(scenarioNumber);
0063 }
0064 
0065 void FakeServer::newConnection()
0066 {
0067     QMutexLocker locker(&m_mutex);
0068 
0069     m_clientSockets << m_tcpServer->nextPendingConnection();
0070     connect(m_clientSockets.last(), &QIODevice::readyRead, this, &FakeServer::dataAvailable);
0071     // m_clientParsers << new KIMAP::ImapStreamParser( m_clientSockets.last(), true );
0072 
0073     QVERIFY(m_clientSockets.size() <= m_scenarios.size());
0074 
0075     writeServerPart(m_clientSockets.size() - 1);
0076 }
0077 
0078 void FakeServer::run()
0079 {
0080     m_tcpServer = new QTcpServer();
0081     if (!m_tcpServer->listen(QHostAddress(QHostAddress::LocalHost), 5989)) {
0082         qFatal("Unable to start the server");
0083         return;
0084     }
0085 
0086     connect(m_tcpServer, &QTcpServer::newConnection, this, &FakeServer::newConnection);
0087 
0088     exec();
0089 
0090     qDeleteAll(m_clientSockets);
0091 
0092     delete m_tcpServer;
0093 }
0094 
0095 void FakeServer::started()
0096 {
0097     // do nothing: this is a dummy slot used by startAndWait()
0098 }
0099 
0100 void FakeServer::setScenario(const QList<QByteArray> &scenario)
0101 {
0102     QMutexLocker locker(&m_mutex);
0103 
0104     m_scenarios.clear();
0105     m_scenarios << scenario;
0106 }
0107 
0108 void FakeServer::addScenario(const QList<QByteArray> &scenario)
0109 {
0110     QMutexLocker locker(&m_mutex);
0111 
0112     m_scenarios << scenario;
0113 }
0114 
0115 void FakeServer::addScenarioFromFile(const QString &fileName)
0116 {
0117     QFile file(fileName);
0118     file.open(QFile::ReadOnly);
0119 
0120     QList<QByteArray> scenario;
0121 
0122     // When loading from files we never have the authentication phase
0123     // force jumping directly to authenticated state.
0124     // scenario << preauth();
0125 
0126     while (!file.atEnd()) {
0127         scenario << file.readLine().trimmed();
0128     }
0129 
0130     file.close();
0131 
0132     addScenario(scenario);
0133 }
0134 
0135 bool FakeServer::isScenarioDone(int scenarioNumber) const
0136 {
0137     QMutexLocker locker(&m_mutex);
0138 
0139     if (scenarioNumber < m_scenarios.size()) {
0140         return m_scenarios[scenarioNumber].isEmpty();
0141     } else {
0142         return true; // Non existent hence empty, right?
0143     }
0144 }
0145 
0146 bool FakeServer::isAllScenarioDone() const
0147 {
0148     QMutexLocker locker(&m_mutex);
0149 
0150     for (const auto &scenario : std::as_const(m_scenarios)) {
0151         if (!scenario.isEmpty()) {
0152             qDebug() << scenario;
0153             return false;
0154         }
0155     }
0156 
0157     return true;
0158 }
0159 
0160 void FakeServer::writeServerPart(int scenarioNumber)
0161 {
0162     QList<QByteArray> scenario = m_scenarios[scenarioNumber];
0163     QTcpSocket *clientSocket = m_clientSockets[scenarioNumber];
0164 
0165     while (!scenario.isEmpty() && (scenario.first().startsWith("S: ") || scenario.first().startsWith("W: "))) {
0166         QByteArray rule = scenario.takeFirst();
0167 
0168         if (rule.startsWith("S: ")) {
0169             QByteArray payload = rule.mid(3);
0170             clientSocket->write(payload + "\r\n");
0171         } else {
0172             int timeout = rule.mid(3).toInt();
0173             QTest::qWait(timeout);
0174         }
0175     }
0176 
0177     if (!scenario.isEmpty() && scenario.first().startsWith('X')) {
0178         scenario.takeFirst();
0179         clientSocket->close();
0180     }
0181 
0182     if (!scenario.isEmpty()) {
0183         QVERIFY(scenario.first().startsWith("C: "));
0184     }
0185 
0186     m_scenarios[scenarioNumber] = scenario;
0187 }
0188 
0189 void FakeServer::readClientPart(int scenarioNumber)
0190 {
0191     QList<QByteArray> scenario = m_scenarios[scenarioNumber];
0192     QTcpSocket *clientSocket = m_clientSockets[scenarioNumber];
0193 
0194     while (!scenario.isEmpty() && scenario.first().startsWith("C: ")) {
0195         QByteArray line = clientSocket->readLine();
0196         QByteArray received = "C: " + line.trimmed();
0197         QByteArray expected = scenario.takeFirst();
0198 
0199         if (expected == "C: SKIP" && !scenario.isEmpty()) {
0200             expected = scenario.takeFirst();
0201             while (received != expected) {
0202                 received = "C: " + clientSocket->readLine().trimmed();
0203             }
0204         }
0205 
0206         QCOMPARE(QString::fromUtf8(received), QString::fromUtf8(expected));
0207         QCOMPARE(received, expected);
0208     }
0209 
0210     if (!scenario.isEmpty()) {
0211         QVERIFY(scenario.first().startsWith("S: "));
0212     }
0213 
0214     m_scenarios[scenarioNumber] = scenario;
0215 }
0216 
0217 #include "moc_fakeserver.cpp"