File indexing completed on 2024-11-24 04:44:44

0001 /*
0002    SPDX-FileCopyrightText: 2008 Omat Holding B.V. <info@omat.nl>
0003 
0004    SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
0005    SPDX-FileContributor: Kevin Ottens <kevin@kdab.com>
0006 
0007    SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 // Own
0011 #include "fakeserver.h"
0012 #include "sslserver.h"
0013 
0014 // Qt
0015 #include <QDebug>
0016 
0017 #include <QFile>
0018 #include <QTcpServer>
0019 #include <QTcpSocket>
0020 #include <QTest>
0021 
0022 #include "imapstreamparser.h"
0023 
0024 QByteArray FakeServer::preauth()
0025 {
0026     return "S: * PREAUTH localhost Test Library server ready";
0027 }
0028 
0029 QByteArray FakeServer::greeting()
0030 {
0031     return "S: * OK localhost Test Library server ready";
0032 }
0033 
0034 FakeServer::FakeServer(QObject *parent)
0035     : QThread(parent)
0036     , m_encrypted(false)
0037     , m_starttls(false)
0038 {
0039     moveToThread(this);
0040 }
0041 
0042 FakeServer::~FakeServer()
0043 {
0044     quit();
0045     wait();
0046 }
0047 
0048 void FakeServer::startAndWait()
0049 {
0050     start();
0051     // this will block until the event queue starts
0052     QMetaObject::invokeMethod(this, &FakeServer::started, Qt::BlockingQueuedConnection);
0053 }
0054 
0055 void FakeServer::dataAvailable()
0056 {
0057     QMutexLocker locker(&m_mutex);
0058 
0059     auto socket = qobject_cast<QTcpSocket *>(sender());
0060     QVERIFY(socket);
0061 
0062     int scenarioNumber = m_clientSockets.indexOf(socket);
0063 
0064     if (m_scenarios[scenarioNumber].isEmpty()) {
0065         KIMAP::ImapStreamParser *clientParser = m_clientParsers[scenarioNumber];
0066         QByteArray received = "C: " + clientParser->readUntilCommandEnd().trimmed();
0067         qWarning() << "Scenario" << scenarioNumber << "finished, but we got command" << received;
0068         QVERIFY(false);
0069     }
0070 
0071     readClientPart(scenarioNumber);
0072     writeServerPart(scenarioNumber);
0073     if (m_starttls) {
0074         m_starttls = false;
0075         qDebug() << "start tls";
0076         static_cast<QSslSocket *>(socket)->startServerEncryption();
0077     }
0078 }
0079 
0080 void FakeServer::newConnection()
0081 {
0082     QMutexLocker locker(&m_mutex);
0083 
0084     m_clientSockets << m_tcpServer->nextPendingConnection();
0085     connect(m_clientSockets.last(), &QTcpSocket::readyRead, this, &FakeServer::dataAvailable);
0086     m_clientParsers << new KIMAP::ImapStreamParser(m_clientSockets.last(), true);
0087 
0088     QVERIFY(m_clientSockets.size() <= m_scenarios.size());
0089 
0090     writeServerPart(m_clientSockets.size() - 1);
0091 }
0092 
0093 void FakeServer::setEncrypted(QSsl::SslProtocol protocol)
0094 {
0095     m_encrypted = true;
0096     m_sslProtocol = protocol;
0097 }
0098 
0099 void FakeServer::setWaitForStartTls(bool wait)
0100 {
0101     m_waitForStartTls = wait;
0102 }
0103 
0104 void FakeServer::run()
0105 {
0106     if (m_encrypted) {
0107         m_tcpServer = new SslServer(m_sslProtocol, m_waitForStartTls);
0108     } else {
0109         m_tcpServer = new QTcpServer();
0110     }
0111     if (!m_tcpServer->listen(QHostAddress(QHostAddress::LocalHost), 5989)) {
0112         qFatal("Unable to start the server");
0113     }
0114 
0115     connect(m_tcpServer, &QTcpServer::newConnection, this, &FakeServer::newConnection);
0116 
0117     exec();
0118 
0119     qDeleteAll(m_clientParsers);
0120     qDeleteAll(m_clientSockets);
0121 
0122     delete m_tcpServer;
0123 }
0124 
0125 void FakeServer::started()
0126 {
0127     // do nothing: this is a dummy slot used by startAndWait()
0128 }
0129 
0130 void FakeServer::setScenario(const QList<QByteArray> &scenario)
0131 {
0132     QMutexLocker locker(&m_mutex);
0133 
0134     m_scenarios.clear();
0135     m_scenarios << scenario;
0136 }
0137 
0138 void FakeServer::addScenario(const QList<QByteArray> &scenario)
0139 {
0140     QMutexLocker locker(&m_mutex);
0141 
0142     m_scenarios << scenario;
0143 }
0144 
0145 void FakeServer::addScenarioFromFile(const QString &fileName)
0146 {
0147     QFile file(fileName);
0148     file.open(QFile::ReadOnly);
0149 
0150     QList<QByteArray> scenario;
0151 
0152     // When loading from files we never have the authentication phase
0153     // force jumping directly to authenticated state.
0154     scenario << preauth();
0155 
0156     while (!file.atEnd()) {
0157         scenario << file.readLine().trimmed();
0158     }
0159 
0160     file.close();
0161 
0162     addScenario(scenario);
0163 }
0164 
0165 bool FakeServer::isScenarioDone(int scenarioNumber) const
0166 {
0167     QMutexLocker locker(&m_mutex);
0168 
0169     if (scenarioNumber < m_scenarios.size()) {
0170         return m_scenarios[scenarioNumber].isEmpty();
0171     } else {
0172         return true; // Non existent hence empty, right?
0173     }
0174 }
0175 
0176 bool FakeServer::isAllScenarioDone() const
0177 {
0178     QMutexLocker locker(&m_mutex);
0179 
0180     for (const QList<QByteArray> &scenario : std::as_const(m_scenarios)) {
0181         if (!scenario.isEmpty()) {
0182             return false;
0183         }
0184     }
0185 
0186     return true;
0187 }
0188 
0189 void FakeServer::writeServerPart(int scenarioNumber)
0190 {
0191     QList<QByteArray> scenario = m_scenarios[scenarioNumber];
0192     QTcpSocket *clientSocket = m_clientSockets[scenarioNumber];
0193 
0194     while (!scenario.isEmpty() && (scenario.first().startsWith("S: ") || scenario.first().startsWith("W: "))) {
0195         QByteArray rule = scenario.takeFirst();
0196 
0197         if (rule.startsWith("S: ")) {
0198             QByteArray payload = rule.mid(3);
0199             clientSocket->write(payload + "\r\n");
0200         } else {
0201             int timeout = rule.mid(3).toInt();
0202             QTest::qWait(timeout);
0203         }
0204     }
0205 
0206     if (!scenario.isEmpty() && scenario.first().startsWith("X")) {
0207         scenario.takeFirst();
0208         clientSocket->close();
0209     }
0210 
0211     if (!scenario.isEmpty()) {
0212         QVERIFY(scenario.first().startsWith("C: "));
0213     }
0214 
0215     m_scenarios[scenarioNumber] = scenario;
0216 }
0217 
0218 void FakeServer::compareReceived(const QByteArray &received, const QByteArray &expected) const
0219 {
0220     QCOMPARE(QString::fromUtf8(received), QString::fromUtf8(expected));
0221     QCOMPARE(received, expected);
0222 }
0223 
0224 void FakeServer::readClientPart(int scenarioNumber)
0225 {
0226     QList<QByteArray> scenario = m_scenarios[scenarioNumber];
0227     KIMAP::ImapStreamParser *clientParser = m_clientParsers[scenarioNumber];
0228 
0229     while (!scenario.isEmpty() && scenario.first().startsWith("C: ")) {
0230         QByteArray received = "C: " + clientParser->readUntilCommandEnd().trimmed();
0231         QByteArray expected = scenario.takeFirst();
0232         if (expected.contains("C: SKIP")) {
0233             continue;
0234         }
0235         compareReceived(received, expected);
0236         if (received.contains("STARTTLS")) {
0237             m_starttls = true;
0238         }
0239     }
0240 
0241     if (!scenario.isEmpty()) {
0242         QVERIFY(scenario.first().startsWith("S: ") || scenario.first().startsWith("X"));
0243     }
0244 
0245     m_scenarios[scenarioNumber] = scenario;
0246 }
0247 
0248 #include "moc_fakeserver.cpp"