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"