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"