7#include "kmanagersieve_debug.h"
10#include "sessionthread_p.h"
16#include <KLocalizedString>
21#include "sasl-common.h"
24using namespace KManageSieve;
26static const sasl_callback_t callbacks[] = {{SASL_CB_ECHOPROMPT,
nullptr,
nullptr},
27 {SASL_CB_NOECHOPROMPT,
nullptr,
nullptr},
28 {SASL_CB_GETREALM,
nullptr,
nullptr},
29 {SASL_CB_USER,
nullptr,
nullptr},
30 {SASL_CB_AUTHNAME,
nullptr,
nullptr},
31 {SASL_CB_PASS,
nullptr,
nullptr},
32 {SASL_CB_CANON_USER,
nullptr,
nullptr},
33 {SASL_CB_LIST_END,
nullptr,
nullptr}};
39 static bool saslInitialized =
false;
40 if (!saslInitialized) {
43 saslInitialized =
true;
52SessionThread::~SessionThread()
55 if (!thread()->wait(10 * 1000)) {
56 thread()->terminate();
64void SessionThread::doInit()
67 m_socket = std::make_unique<QSslSocket>();
68 connect(m_socket.get(), &QSslSocket::readyRead,
this, &SessionThread::slotDataReceived);
70 connect(m_socket.get(), &QSslSocket::disconnected,
this, &SessionThread::socketDisconnected);
71 connect(m_socket.get(), &QSslSocket::connected,
this, &SessionThread::socketConnected);
75void SessionThread::doDestroy()
79 doDisconnectFromHost(
false);
87void SessionThread::connectToHost(
const QUrl &url)
98void SessionThread::doConnectToHost(
const QUrl &url)
107 m_socket->connectToHost(url.
host(), url.
port() ? url.
port() : 4190);
111void SessionThread::disconnectFromHost(
bool sendLogout)
115 [
this, sendLogout]() {
116 doDisconnectFromHost(sendLogout);
122void SessionThread::doDisconnectFromHost(
bool sendLogout)
127 doSendData(
"LOGOUT");
129 m_socket->disconnectFromHost();
133void SessionThread::sendData(
const QByteArray &data)
144void SessionThread::doSendData(
const QByteArray &data)
148 qCDebug(KMANAGERSIEVE_LOG) <<
"C: " << data;
149 m_socket->write(data);
150 m_socket->write(
"\r\n");
154void SessionThread::slotDataReceived()
157 if (m_pendingQuantity > 0) {
158 const QByteArray buffer = m_socket->read(qMin(m_pendingQuantity, m_socket->bytesAvailable()));
160 m_pendingQuantity -= buffer.
size();
161 if (m_pendingQuantity <= 0) {
162 qCDebug(KMANAGERSIEVE_LOG) <<
"S: " << m_data.trimmed();
163 Q_EMIT responseReceived(m_lastResponse, m_data);
169 while (m_socket->canReadLine()) {
177 qCDebug(KMANAGERSIEVE_LOG) <<
"S: " << line;
179 if (!r.parseResponse(line)) {
180 qCDebug(KMANAGERSIEVE_LOG) <<
"protocol violation!";
181 doDisconnectFromHost(
false);
183 qCDebug(KMANAGERSIEVE_LOG) << r.type() << r.key() << r.value() << r.extra() << r.quantity();
186 if (r.quantity() > 0) {
188 m_pendingQuantity = r.quantity();
191 }
else if (r.operationResult() == Response::Bye) {
192 doDisconnectFromHost(
false);
200void SessionThread::slotSocketError()
204 qCWarning(KMANAGERSIEVE_LOG) << Q_FUNC_INFO << m_socket->error() << m_socket->errorString();
206 Q_EMIT
error(m_socket->error(), m_socket->errorString());
207 doDisconnectFromHost(
false);
211void SessionThread::startAuthentication()
217void SessionThread::handleSaslAuthError()
220 doDisconnectFromHost(
true);
224void SessionThread::doStartAuthentication()
229 m_sasl_conn =
nullptr;
230 m_sasl_client_interact =
nullptr;
231 const char *out =
nullptr;
233 const char *mechusing =
nullptr;
235 result = sasl_client_new(
"sieve", m_url.host().toLatin1().constData(),
nullptr,
nullptr, callbacks, 0, &m_sasl_conn);
236 if (result != SASL_OK) {
237 handleSaslAuthError();
242 result = sasl_client_start(m_sasl_conn,
243 m_session->requestedSaslMethod().join(
QLatin1Char(
' ')).toLatin1().constData(),
244 &m_sasl_client_interact,
248 if (result == SASL_INTERACT) {
249 if (!saslInteract(m_sasl_client_interact)) {
250 handleSaslAuthError();
251 sasl_dispose(&m_sasl_conn);
255 }
while (result == SASL_INTERACT);
257 if (result != SASL_CONTINUE && result != SASL_OK) {
258 handleSaslAuthError();
259 sasl_dispose(&m_sasl_conn);
263 qCDebug(KMANAGERSIEVE_LOG) <<
"Preferred authentication method is " << mechusing <<
".";
268 authCommand +=
" \"";
269 authCommand += challenge;
272 doSendData(authCommand);
276void SessionThread::continueAuthentication(
const Response &response,
const QByteArray &data)
282void SessionThread::doContinueAuthentication(
const Response &response,
const QByteArray &data)
286 if (response.operationResult() == Response::Other) {
287 if (!saslClientStep(data)) {
288 handleSaslAuthError();
292 sasl_dispose(&m_sasl_conn);
293 if (response.operationSuccessful()) {
294 qCDebug(KMANAGERSIEVE_LOG) <<
"Authentication complete.";
295 Q_EMIT authenticationDone();
299 i18n(
"Authentication failed.\nMost likely the password is wrong.\nThe server responded:\n%1",
301 doDisconnectFromHost(
true);
307bool SessionThread::saslInteract(
void *in)
311 qCDebug(KMANAGERSIEVE_LOG) <<
"SessionThread::saslInteract";
312 auto *interact = (sasl_interact_t *)in;
316 for (; interact->id != SASL_CB_LIST_END; ++interact) {
317 if (interact->id == SASL_CB_AUTHNAME || interact->id == SASL_CB_PASS) {
318 if (m_url.userName().isEmpty() || m_url.password().isEmpty()) {
319 AuthDetails authData;
321 "requestAuthDetails",
323 Q_RETURN_ARG(KManageSieve::AuthDetails, authData),
326 if (authData.valid) {
327 m_url.setUserName(authData.username);
328 m_url.setPassword(authData.password);
337 interact = (sasl_interact_t *)in;
338 while (interact->id != SASL_CB_LIST_END) {
339 qCDebug(KMANAGERSIEVE_LOG) <<
"SASL_INTERACT id: " << interact->id;
340 switch (interact->id) {
342 case SASL_CB_AUTHNAME:
343 qCDebug(KMANAGERSIEVE_LOG) <<
"SASL_CB_[AUTHNAME|USER]: '" << m_url.userName() <<
"'";
344 interact->result = strdup(m_url.userName().toUtf8().constData());
345 if (interact->result) {
346 interact->len = strlen((
const char *)interact->result);
352 qCDebug(KMANAGERSIEVE_LOG) <<
"SASL_CB_PASS: [hidden] ";
353 interact->result = strdup(m_url.password().toUtf8().constData());
354 if (interact->result) {
355 interact->len = strlen((
const char *)interact->result);
361 interact->result =
nullptr;
371bool SessionThread::saslClientStep(
const QByteArray &challenge)
374 const char *out =
nullptr;
379 result = sasl_client_step(m_sasl_conn,
380 challenge_decoded.
isEmpty() ?
nullptr : challenge_decoded.
data(),
381 challenge_decoded.
size(),
382 &m_sasl_client_interact,
385 if (result == SASL_INTERACT) {
386 if (!saslInteract(m_sasl_client_interact)) {
387 sasl_dispose(&m_sasl_conn);
391 }
while (result == SASL_INTERACT);
393 qCDebug(KMANAGERSIEVE_LOG) <<
"sasl_client_step: " << result;
394 if (result != SASL_CONTINUE && result != SASL_OK) {
395 qCDebug(KMANAGERSIEVE_LOG) <<
"sasl_client_step failed with: " << result <<
QString::fromUtf8(sasl_errdetail(m_sasl_conn));
396 sasl_dispose(&m_sasl_conn);
405void SessionThread::startSsl()
411void SessionThread::doStartSsl()
415 qCDebug(KMANAGERSIEVE_LOG) <<
"SessionThread::doStartSsl()";
417 m_sslCheck =
new QTimer(
this);
418 m_sslCheck->setInterval(60 * 1000);
422 m_socket->ignoreSslErrors();
423 connect(m_socket.get(), &QSslSocket::encrypted,
this, &SessionThread::slotEncryptedDone);
425 m_socket->startClientEncryption();
429void SessionThread::slotSslTimeout()
433 disconnect(m_socket.get(), &QSslSocket::encrypted,
this, &SessionThread::slotEncryptedDone);
438void SessionThread::slotEncryptedDone()
447void SessionThread::sslResult(
bool encrypted)
451 const QSslCipher cipher = m_socket->sessionCipher();
452 const int numberOfSslError = m_socket->sslHandshakeErrors().count();
453 if (!encrypted || numberOfSslError > 0 || !m_socket->isEncrypted() || cipher.isNull() || cipher.usedBits() == 0) {
454 qCDebug(KMANAGERSIEVE_LOG) <<
"Initial SSL handshake failed. cipher.isNull() is" << cipher.isNull() <<
", cipher.usedBits() is" << cipher.usedBits()
455 <<
", the socket says:" << m_socket->errorString() <<
"and the list of SSL errors contains" << numberOfSslError <<
"items.";
463#include "moc_sessionthread_p.cpp"
A response from a managesieve server.
QString i18n(const char *text, const TYPE &arg...)
KIOCORE_EXPORT QString buildErrorString(int errorCode, const QString &errorText)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
void errorOccurred(QAbstractSocket::SocketError socketError)
bool endsWith(QByteArrayView bv) const const
QByteArray fromBase64(const QByteArray &base64, Base64Options options)
QByteArray fromRawData(const char *data, qsizetype size)
bool isEmpty() const const
qsizetype size() const const
QByteArray toBase64(Base64Options options) const const
QString fromLatin1(QByteArrayView str)
QString fromUtf8(QByteArrayView str)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QThread * currentThread()
QString host(ComponentFormattingOptions options) const const
int port(int defaultPort) const const