File indexing completed on 2024-11-24 04:44:52
0001 /* 0002 Copyright (C) 2008 Omat Holding B.V. <info@omat.nl> 0003 0004 Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com> 0005 Author: Kevin Ottens <kevin@kdab.com> 0006 0007 This program is free software; you can redistribute it and/or 0008 modify it under the terms of the GNU General Public 0009 License as published by the Free Software Foundation; either 0010 version 2 of the License, or (at your option) any later version. 0011 0012 This program is distributed in the hope that it will be useful, 0013 but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0015 General Public License for more details. 0016 0017 You should have received a copy of the GNU General Public License 0018 along with this program; if not, write to the Free Software 0019 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 0020 */ 0021 0022 // Own 0023 #include "fakeserver.h" 0024 #include "sslserver.h" 0025 0026 // Qt 0027 #include <QDebug> 0028 #include <QTcpServer> 0029 0030 #include <QDebug> 0031 #include <QFile> 0032 #include <qtest.h> 0033 0034 #include "imapstreamparser.h" 0035 0036 QByteArray FakeServer::preauth() 0037 { 0038 return "S: * PREAUTH localhost Test Library server ready"; 0039 } 0040 0041 QByteArray FakeServer::greeting() 0042 { 0043 return "S: * OK localhost Test Library server ready"; 0044 } 0045 0046 FakeServer::FakeServer(QObject *parent) : QThread(parent), m_encrypted(false), m_starttls(false), m_receivedStarttls(false) 0047 { 0048 moveToThread(this); 0049 } 0050 0051 FakeServer::~FakeServer() 0052 { 0053 quit(); 0054 wait(); 0055 } 0056 0057 void FakeServer::startAndWait() 0058 { 0059 start(); 0060 // this will block until the event queue starts 0061 QMetaObject::invokeMethod(this, "started", Qt::BlockingQueuedConnection); 0062 } 0063 0064 void FakeServer::dataAvailable() 0065 { 0066 QMutexLocker locker(&m_mutex); 0067 0068 QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender()); 0069 Q_ASSERT(socket); 0070 0071 int scenarioNumber = m_clientSockets.indexOf(socket); 0072 0073 readClientPart(scenarioNumber); 0074 writeServerPart(scenarioNumber); 0075 qDebug() << "start tls"; 0076 if (m_receivedStarttls) { 0077 m_receivedStarttls = false; 0078 static_cast<QSslSocket *>(socket)->startServerEncryption(); 0079 } 0080 } 0081 0082 void FakeServer::newConnection() 0083 { 0084 QMutexLocker locker(&m_mutex); 0085 0086 m_clientSockets << m_tcpServer->nextPendingConnection(); 0087 connect(m_clientSockets.last(), SIGNAL(readyRead()), this, SLOT(dataAvailable())); 0088 m_clientParsers << new KIMAP2::ImapStreamParser(m_clientSockets.last(), true); 0089 0090 QVERIFY(m_clientSockets.size() <= m_scenarios.size()); 0091 0092 writeServerPart(m_clientSockets.size() - 1); 0093 } 0094 0095 void FakeServer::setEncrypted(QSsl::SslProtocol protocol, bool startTls) 0096 { 0097 m_encrypted = true; 0098 m_starttls = startTls; 0099 m_sslProtocol = protocol; 0100 } 0101 0102 void FakeServer::run() 0103 { 0104 if (m_encrypted) { 0105 m_tcpServer = new SslServer(m_sslProtocol, m_starttls); 0106 } else { 0107 m_tcpServer = new QTcpServer(); 0108 } 0109 if (!m_tcpServer->listen(QHostAddress(QHostAddress::LocalHost), 5989)) { 0110 qFatal("Unable to start the server"); 0111 } 0112 0113 connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection())); 0114 0115 exec(); 0116 0117 qDeleteAll(m_clientParsers); 0118 qDeleteAll(m_clientSockets); 0119 0120 delete m_tcpServer; 0121 } 0122 0123 void FakeServer::started() 0124 { 0125 // do nothing: this is a dummy slot used by startAndWait() 0126 } 0127 0128 void FakeServer::setScenario(const QList<QByteArray> &scenario) 0129 { 0130 QMutexLocker locker(&m_mutex); 0131 0132 m_scenarios.clear(); 0133 m_scenarios << scenario; 0134 } 0135 0136 void FakeServer::addScenario(const QList<QByteArray> &scenario) 0137 { 0138 QMutexLocker locker(&m_mutex); 0139 0140 m_scenarios << scenario; 0141 } 0142 0143 void FakeServer::addScenarioFromFile(const QString &fileName) 0144 { 0145 QFile file(fileName); 0146 file.open(QFile::ReadOnly); 0147 0148 QList<QByteArray> scenario; 0149 0150 // When loading from files we never have the authentication phase 0151 // force jumping directly to authenticated state. 0152 scenario << preauth(); 0153 0154 while (!file.atEnd()) { 0155 scenario << file.readLine().trimmed(); 0156 } 0157 0158 file.close(); 0159 0160 addScenario(scenario); 0161 } 0162 0163 bool FakeServer::isScenarioDone(int scenarioNumber) const 0164 { 0165 QMutexLocker locker(&m_mutex); 0166 0167 if (scenarioNumber < m_scenarios.size()) { 0168 return m_scenarios[scenarioNumber].isEmpty(); 0169 } else { 0170 return true; // Non existent hence empty, right? 0171 } 0172 } 0173 0174 bool FakeServer::isAllScenarioDone() const 0175 { 0176 QMutexLocker locker(&m_mutex); 0177 0178 foreach (const QList<QByteArray> &scenario, m_scenarios) { 0179 if (!scenario.isEmpty()) { 0180 return false; 0181 } 0182 } 0183 0184 return true; 0185 } 0186 0187 void FakeServer::writeServerPart(int scenarioNumber) 0188 { 0189 QList<QByteArray> scenario = m_scenarios[scenarioNumber]; 0190 QTcpSocket *clientSocket = m_clientSockets[scenarioNumber]; 0191 0192 while (!scenario.isEmpty() && 0193 (scenario.first().startsWith("S: ") || scenario.first().startsWith("W: "))) { 0194 QByteArray rule = scenario.takeFirst(); 0195 0196 if (rule.startsWith("S: ")) { 0197 QByteArray payload = rule.mid(3); 0198 clientSocket->write(payload + "\r\n"); 0199 } else { 0200 int timeout = rule.mid(3).toInt(); 0201 QTest::qWait(timeout); 0202 } 0203 } 0204 0205 if (!scenario.isEmpty() && 0206 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 KIMAP2::ImapStreamParser *clientParser = m_clientParsers[scenarioNumber]; 0228 0229 while (!scenario.isEmpty() && 0230 scenario.first().startsWith("C: ")) { 0231 QByteArray received = "C: " + clientParser->readUntilCommandEnd().trimmed(); 0232 QByteArray expected = scenario.takeFirst(); 0233 if (expected.contains("C: SKIP")) { 0234 continue; 0235 } 0236 compareReceived(received, expected); 0237 if (received.contains("STARTTLS")) { 0238 m_receivedStarttls = true; 0239 } 0240 } 0241 0242 if (!scenario.isEmpty()) { 0243 QVERIFY(scenario.first().startsWith("S: ")); 0244 } 0245 0246 m_scenarios[scenarioNumber] = scenario; 0247 }