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 }