10#include "servertest.h"
14#include <transportbase.h>
16#include <KLocalizedString>
21#include <QProgressBar>
22#include <QRegularExpression>
27#include "mailtransport_debug.h"
29using namespace MailTransport;
31namespace MailTransport
33class ServerTestPrivate
50 QTimer *normalSocketTimer =
nullptr;
51 QTimer *secureSocketTimer =
nullptr;
52 QTimer *progressTimer =
nullptr;
56 bool secureSocketFinished =
false;
57 bool normalSocketFinished =
false;
58 bool tlsFinished =
false;
64 bool normalPossible =
true;
65 bool securePossible =
true;
68 void handleSMTPIMAPResponse(
int type,
const QString &text);
74 [[nodiscard]]
inline bool isSupportedXOAuthServer(
const QString &server)
const
81 void slotNormalPossible();
82 void slotNormalNotPossible();
83 void slotSslPossible();
84 void slotSslNotPossible();
86 void slotReadNormal(
const QString &text);
87 void slotReadSecure(
const QString &text);
88 void slotUpdateProgress();
92ServerTestPrivate::ServerTestPrivate(
ServerTest *test)
97void ServerTestPrivate::finalResult()
99 if (!secureSocketFinished || !normalSocketFinished || !tlsFinished) {
103 qCDebug(MAILTRANSPORT_LOG) <<
"Modes:" << connectionResults;
104 qCDebug(MAILTRANSPORT_LOG) <<
"Capabilities:" << capabilityResults;
107 qCDebug(MAILTRANSPORT_LOG) <<
"TLS:" << q->
tlsProtocols();
110 testProgress->
hide();
112 progressTimer->
stop();
113 secureSocketFinished =
false;
114 normalSocketFinished =
false;
119 for (
int res : std::as_const(connectionResults)) {
120 resultsAsVector.
append(res);
123 Q_EMIT q->
finished(resultsAsVector);
132 result << Transport::EnumAuthenticationType::LOGIN;
134 result << Transport::EnumAuthenticationType::PLAIN;
136 result << Transport::EnumAuthenticationType::CRAM_MD5;
138 result << Transport::EnumAuthenticationType::DIGEST_MD5;
140 result << Transport::EnumAuthenticationType::NTLM;
142 result << Transport::EnumAuthenticationType::GSSAPI;
144 result << Transport::EnumAuthenticationType::ANONYMOUS;
146 if (isSupportedXOAuthServer(server)) {
147 result << Transport::EnumAuthenticationType::XOAUTH2;
152 qCDebug(MAILTRANSPORT_LOG) << authentications << result;
157 if (result.contains(Transport::EnumAuthenticationType::PLAIN)) {
158 result.removeAll(Transport::EnumAuthenticationType::LOGIN);
164void ServerTestPrivate::handleSMTPIMAPResponse(
int type,
const QString &text)
167 qCDebug(MAILTRANSPORT_LOG) <<
"No authentication possible";
172 if (isSupportedXOAuthServer(server)) {
173 protocols << QStringLiteral(
"XOAUTH2");
176 protocols << QStringLiteral(
"LOGIN") << QStringLiteral(
"PLAIN") << QStringLiteral(
"CRAM-MD5") << QStringLiteral(
"DIGEST-MD5") << QStringLiteral(
"NTLM")
177 << QStringLiteral(
"GSSAPI") << QStringLiteral(
"ANONYMOUS");
180 for (
int i = 0; i < protocols.
count(); ++i) {
186 authenticationResults[
type] = parseAuthenticationList(results);
189 if (authenticationResults[type].isEmpty()) {
190 authenticationResults[
type] << Transport::EnumAuthenticationType::CLEAR;
193 qCDebug(MAILTRANSPORT_LOG) <<
"For type" <<
type <<
", we have:" << authenticationResults[
type];
196void ServerTestPrivate::slotNormalPossible()
198 normalSocketTimer->
stop();
199 connectionResults << Transport::EnumEncryption::None;
204 if (testProtocol == IMAP_PROTOCOL) {
205 socket->
write(QStringLiteral(
"1 CAPABILITY"));
206 }
else if (testProtocol == SMTP_PROTOCOL) {
212 if (!fakeHostname.
isNull()) {
217 hostname = QStringLiteral(
"localhost.invalid");
222 qCDebug(MAILTRANSPORT_LOG) <<
"Hostname for EHLO is" <<
hostname;
228void ServerTestPrivate::slotTlsDone()
235bool ServerTestPrivate::handlePopConversation(
MailTransport::Socket *socket,
int type,
int stage,
const QString &response,
bool *shouldStartTLS)
237 Q_ASSERT(shouldStartTLS !=
nullptr);
244 if (responseWithoutCRLF.
indexOf(re) != -1) {
245 authenticationResults[
type] << Transport::EnumAuthenticationType::APOP;
249 authenticationResults[
type] << Transport::EnumAuthenticationType::CLEAR;
253 if (type == Transport::EnumEncryption::TLS
254 && authenticationResults[Transport::EnumEncryption::None].contains(Transport::EnumAuthenticationType::APOP)) {
255 authenticationResults[Transport::EnumEncryption::TLS] << Transport::EnumAuthenticationType::APOP;
258 socket->
write(QStringLiteral(
"CAPA"));
262 else if (stage == 1) {
282 connectionResults << Transport::EnumEncryption::TLS;
283 popSupportsTLS =
true;
285 socket->
write(QStringLiteral(
"AUTH"));
289 else if (stage == 2) {
296 QString formattedReply = response;
299 formattedReply.
chop(3);
308 *shouldStartTLS = popSupportsTLS;
312bool ServerTestPrivate::handleNntpConversation(
MailTransport::Socket *socket,
int type,
int *stage,
const QString &response,
bool *shouldStartTLS)
314 Q_ASSERT(shouldStartTLS !=
nullptr);
315 Q_ASSERT(stage !=
nullptr);
326 socket->
write(QStringLiteral(
"CAPABILITIES"));
330 else if (*stage == 1) {
352 *shouldStartTLS =
true;
355 const QString s(QStringLiteral(
"USER"));
358 authenticationResults[
type].append(Transport::EnumAuthenticationType::CLEAR);
362 authenticationResults[
type] += parseAuthenticationList(auths);
380void ServerTestPrivate::slotReadNormal(
const QString &text)
382 Q_ASSERT(encryptionMode != Transport::EnumEncryption::SSL);
383 static const int tlsHandshakeStage = 42;
385 qCDebug(MAILTRANSPORT_LOG) <<
"Stage" << normalStage + 1 <<
", Mode" << encryptionMode;
391 if (normalStage == tlsHandshakeStage) {
392 Q_ASSERT(encryptionMode == Transport::EnumEncryption::TLS);
398 bool shouldStartTLS =
false;
403 if (testProtocol == POP_PROTOCOL) {
404 if (handlePopConversation(normalSocket, encryptionMode, normalStage, text, &shouldStartTLS)) {
407 }
else if (testProtocol == NNTP_PROTOCOL) {
408 if (handleNntpConversation(normalSocket, encryptionMode, &normalStage, text, &shouldStartTLS)) {
414 if (normalStage == 0) {
415 sendInitialCapabilityQuery(normalSocket);
420 connectionResults << Transport::EnumEncryption::TLS;
421 shouldStartTLS =
true;
423 handleSMTPIMAPResponse(encryptionMode, text);
428 normalSocketFinished =
true;
432 if (shouldStartTLS && encryptionMode == Transport::EnumEncryption::None) {
433 qCDebug(MAILTRANSPORT_LOG) <<
"Trying TLS...";
434 connectionResults << Transport::EnumEncryption::TLS;
435 if (testProtocol == POP_PROTOCOL) {
436 normalSocket->
write(QStringLiteral(
"STLS"));
437 }
else if (testProtocol == IMAP_PROTOCOL) {
438 normalSocket->
write(QStringLiteral(
"2 STARTTLS"));
440 normalSocket->
write(QStringLiteral(
"STARTTLS"));
442 encryptionMode = Transport::EnumEncryption::TLS;
443 normalStage = tlsHandshakeStage;
453void ServerTestPrivate::slotReadSecure(
const QString &text)
456 if (testProtocol == POP_PROTOCOL) {
458 if (handlePopConversation(secureSocket, Transport::EnumEncryption::SSL, secureStage, text, &dummy)) {
461 }
else if (testProtocol == NNTP_PROTOCOL) {
463 if (handleNntpConversation(secureSocket, Transport::EnumEncryption::SSL, &secureStage, text, &dummy)) {
467 if (secureStage == 0) {
468 sendInitialCapabilityQuery(secureSocket);
471 handleSMTPIMAPResponse(Transport::EnumEncryption::SSL, text);
473 secureSocketFinished =
true;
477void ServerTestPrivate::slotNormalNotPossible()
479 if (testProtocol == SMTP_PROTOCOL && normalSocket->
port() == SMTP_PORT) {
481 normalSocket->
setPort(SMTP_OLD_PORT);
483 normalSocketTimer->
start(10000);
487 normalSocketTimer->
stop();
488 normalPossible =
false;
489 normalSocketFinished =
true;
494void ServerTestPrivate::slotSslPossible()
496 secureSocketTimer->
stop();
497 connectionResults << Transport::EnumEncryption::SSL;
500void ServerTestPrivate::slotSslNotPossible()
502 secureSocketTimer->
stop();
503 securePossible =
false;
504 secureSocketFinished =
true;
508void ServerTestPrivate::slotUpdateProgress()
519 , d(new ServerTestPrivate(this))
521 d->normalSocketTimer =
new QTimer(
this);
522 d->normalSocketTimer->setSingleShot(
true);
523 connect(d->normalSocketTimer, SIGNAL(timeout()), SLOT(slotNormalNotPossible()));
525 d->secureSocketTimer =
new QTimer(
this);
526 d->secureSocketTimer->setSingleShot(
true);
527 connect(d->secureSocketTimer, SIGNAL(timeout()), SLOT(slotSslNotPossible()));
529 d->progressTimer =
new QTimer(
this);
530 connect(d->progressTimer, SIGNAL(timeout()), SLOT(slotUpdateProgress()));
537 qCDebug(MAILTRANSPORT_LOG) << d.get();
539 d->connectionResults.clear();
540 d->authenticationResults.clear();
541 d->capabilityResults.clear();
542 d->popSupportsTLS =
false;
545 d->encryptionMode = Transport::EnumEncryption::None;
546 d->normalPossible =
true;
547 d->securePossible =
true;
549 if (d->testProgress) {
550 d->testProgress->setMaximum(20);
551 d->testProgress->setValue(0);
552 d->testProgress->setFormat(
i18nc(
"Percent value; %p is the value, % is the percent sign",
"%p%"));
553 d->testProgress->setTextVisible(
true);
554 d->testProgress->show();
555 d->progressTimer->start(1000);
561 d->normalSocket->setServer(d->server);
562 d->normalSocket->setProtocol(d->testProtocol);
563 if (d->testProtocol == IMAP_PROTOCOL) {
564 d->normalSocket->setPort(IMAP_PORT);
565 d->secureSocket->setPort(IMAPS_PORT);
566 }
else if (d->testProtocol == SMTP_PROTOCOL) {
567 d->normalSocket->setPort(SMTP_PORT);
568 d->secureSocket->setPort(SMTPS_PORT);
569 }
else if (d->testProtocol == POP_PROTOCOL) {
570 d->normalSocket->setPort(POP_PORT);
571 d->secureSocket->setPort(POPS_PORT);
572 }
else if (d->testProtocol == NNTP_PROTOCOL) {
573 d->normalSocket->setPort(NNTP_PORT);
574 d->secureSocket->setPort(NNTPS_PORT);
577 if (d->customPorts.contains(Transport::EnumEncryption::None)) {
578 d->normalSocket->setPort(d->customPorts.value(Transport::EnumEncryption::None));
580 if (d->customPorts.contains(Transport::EnumEncryption::SSL)) {
581 d->secureSocket->setPort(d->customPorts.value(Transport::EnumEncryption::SSL));
584 connect(d->normalSocket, SIGNAL(connected()), SLOT(slotNormalPossible()));
585 connect(d->normalSocket, SIGNAL(failed()), SLOT(slotNormalNotPossible()));
587 connect(d->normalSocket, SIGNAL(tlsDone()), SLOT(slotTlsDone()));
588 d->normalSocket->reconnect();
589 d->normalSocketTimer->start(10000);
591 if (d->secureSocket->port() > 0) {
593 d->secureSocket->setServer(d->server);
594 d->secureSocket->setProtocol(d->testProtocol +
QLatin1Char(
's'));
595 d->secureSocket->setSecure(
true);
596 connect(d->secureSocket, SIGNAL(connected()), SLOT(slotSslPossible()));
597 connect(d->secureSocket, SIGNAL(failed()), SLOT(slotSslNotPossible()));
599 d->secureSocket->reconnect();
600 d->secureSocketTimer->start(10000);
602 d->slotSslNotPossible();
613 return d->fakeHostname;
623 Q_ASSERT(encryptionMode == Transport::EnumEncryption::None || encryptionMode == Transport::EnumEncryption::SSL);
624 d->customPorts.insert(encryptionMode,
port);
629 d->testProgress = pb;
634 d->testProtocol = protocol;
635 d->customPorts.clear();
640 return d->testProtocol;
650 Q_ASSERT(encryptionMode == Transport::EnumEncryption::None || encryptionMode == Transport::EnumEncryption::SSL);
651 if (d->customPorts.contains(encryptionMode)) {
652 return d->customPorts.value(
static_cast<int>(encryptionMode));
660 return d->testProgress;
665 return d->authenticationResults[TransportBase::EnumEncryption::None];
670 return d->normalPossible;
675 return d->authenticationResults[TransportBase::EnumEncryption::TLS];
680 return d->authenticationResults[Transport::EnumEncryption::SSL];
685 return d->securePossible;
690 return d->capabilityResults.values();
693#include "moc_servertest.cpp"
This class can be used to test certain server to see if they support stuff.
void finished(const QList< int > &)
This will be emitted when the test is done.
ServerTest(QObject *parent=nullptr)
Creates a new server test.
QString fakeHostname() const
void setProgressBar(QProgressBar *pb)
Makes pb the progressbar to use.
bool isSecurePossible() const
tells you if the ssl server is available
void setPort(Transport::EnumEncryption encryptionMode, uint port)
Set a custom port to use.
~ServerTest() override
Destroys the server test.
void setProtocol(const QString &protocol)
Sets protocol the protocol to test, currently supported are "smtp", "pop", "imap",...
QList< int > tlsProtocols() const
Get the protocols for the TLS connections.
@ Pipelining
POP3 only. The server supports pipeplining of commands.
@ UIDL
POP3 only. The server has support for unique identifiers.
@ Top
POP3 only. The server supports fetching only the headers.
QList< Capability > capabilities() const
Get the special capabilities of the server.
void start()
Starts the test.
void setServer(const QString &server)
Sets the server to test.
bool isNormalPossible() const
tells you if the normal server is available
void setFakeHostname(const QString &fakeHostname)
Sets a fake hostname for the test.
QList< int > secureProtocols() const
Get the protocols for the SSL connections.
QList< int > normalProtocols() const
Get the protocols for the normal connections.
int port(Transport::EnumEncryption encryptionMode) const
Responsible for communicating with the server, it's designed to work with the ServerTest class.
void setPort(int port)
set the port to use.
virtual void reconnect()
Existing connection will be closed and a new connection will be made.
int port() const
returns the used port.
virtual void write(const QString &text)
Write text to the socket.
void startTLS()
If you want to start TLS encryption, call this.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
Internal file containing constant definitions etc.
Type type(const QSqlDatabase &db)
NETWORKMANAGERQT_EXPORT QString hostname()
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
bool contains(const AT &value) const const
qsizetype count() const const
QList< T > mid(qsizetype pos, qsizetype length) const const
void reserve(qsizetype size)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
qsizetype size() const const
QString chopped(qsizetype len) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
bool isNull() const const
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QString right(qsizetype n) const const
qsizetype size() const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QString toUpper() const const
QList< QStringView > split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const