16#include "kimap_debug.h"
21#include "response_p.h"
23#include "sessionlogger_p.h"
24#include "sessionthread_p.h"
27Q_DECLARE_METATYPE(QSslSocket::SslMode)
28static const int _kimap_sslVersionId = qRegisterMetaType<QSsl::SslProtocol>();
32Session::Session(
const QString &hostName, quint16 port,
QObject *parent)
34 , d(new SessionPrivate(this))
36 if (!qEnvironmentVariableIsEmpty(
"KIMAP_LOGFILE")) {
37 d->logger =
new SessionLogger;
40 d->isSocketConnected =
false;
42 d->jobRunning =
false;
44 d->thread =
new SessionThread(hostName, port);
45 connect(d->thread, &SessionThread::encryptionNegotiationResult, d, &SessionPrivate::onEncryptionNegotiationResult);
46 connect(d->thread, &SessionThread::sslError, d, &SessionPrivate::handleSslError);
47 connect(d->thread, &SessionThread::socketDisconnected, d, &SessionPrivate::socketDisconnected);
48 connect(d->thread, &SessionThread::responseReceived, d, &SessionPrivate::responseReceived);
49 connect(d->thread, &SessionThread::socketConnected, d, &SessionPrivate::socketConnected);
50 connect(d->thread, &SessionThread::socketActivity, d, &SessionPrivate::socketActivity);
51 connect(d->thread, &SessionThread::socketError, d, &SessionPrivate::socketError);
53 d->socketTimer.setSingleShot(
true);
56 d->startSocketTimer();
62 d->socketDisconnected();
77QString Session::hostName()
const
79 return d->thread->hostName();
82quint16 Session::port()
const
84 return d->thread->port();
87void Session::setUseNetworkProxy(
bool useProxy)
89 d->thread->setUseNetworkProxy(useProxy);
92Session::State Session::state()
const
97QString Session::userName()
const
107int Session::jobQueueSize()
const
109 return d->queue.
size() + (d->jobRunning ? 1 : 0);
112void KIMAP::Session::close()
114 d->thread->closeSocket();
121 const bool ignoreSslError = uiProxy && uiProxy->ignoreSslError(errorData);
123 _t->sslErrorHandlerResponse(ignoreSslError);
127SessionPrivate::SessionPrivate(
Session *session)
130 , isSocketConnected(false)
135 , currentJob(nullptr)
138 , socketTimerInterval(30000)
142SessionPrivate::~SessionPrivate()
147void SessionPrivate::addJob(Job *job)
150 Q_EMIT q->jobQueueSizeChanged(q->jobQueueSize());
155 if (state != Session::Disconnected) {
160void SessionPrivate::startNext()
165void SessionPrivate::doStartNext()
167 if (queue.isEmpty() || jobRunning || !isSocketConnected) {
171 restartSocketTimer();
174 currentJob = queue.dequeue();
175 currentJob->doStart();
178void SessionPrivate::jobDone(
KJob *job)
181 Q_ASSERT(job == currentJob);
186 currentJob =
nullptr;
187 Q_EMIT q->jobQueueSizeChanged(q->jobQueueSize());
191void SessionPrivate::jobDestroyed(
QObject *job)
193 queue.removeAll(
static_cast<KIMAP::Job *
>(job));
194 if (currentJob == job) {
195 currentJob =
nullptr;
199void SessionPrivate::responseReceived(
const Response &response)
201 if (logger && isConnected()) {
202 logger->dataReceived(response.toString());
208 if (response.content.size() >= 1) {
209 tag = response.content[0].toString();
212 if (response.content.size() >= 2) {
213 code = response.content[1].toString();
219 Response simplified = response;
220 if (simplified.content.size() >= 2) {
221 simplified.content.removeFirst();
222 simplified.content.removeFirst();
224 qCDebug(KIMAP_LOG) <<
"Received BYE: " << simplified.toString();
229 case Session::Disconnected:
230 if (socketTimer.isActive()) {
234 setState(Session::NotAuthenticated);
236 Response simplified = response;
237 simplified.content.removeFirst();
238 simplified.content.removeFirst();
239 greeting = simplified.toString().trimmed();
241 }
else if (code ==
"PREAUTH") {
242 setState(Session::Authenticated);
244 Response simplified = response;
245 simplified.content.removeFirst();
246 simplified.content.removeFirst();
247 greeting = simplified.toString().trimmed();
251 thread->closeSocket();
254 case Session::NotAuthenticated:
255 if (code ==
"OK" && tag == authTag) {
256 setState(Session::Authenticated);
259 case Session::Authenticated:
260 if (code ==
"OK" && tag == selectTag) {
261 setState(Session::Selected);
262 currentMailBox = upcomingMailBox;
265 case Session::Selected:
266 if ((code ==
"OK" && tag == closeTag) || (code !=
"OK" && tag == selectTag)) {
267 setState(Session::Authenticated);
269 }
else if (code ==
"OK" && tag == selectTag) {
270 currentMailBox = upcomingMailBox;
275 if (tag == authTag) {
278 if (tag == selectTag) {
281 if (tag == closeTag) {
286 if (currentJob !=
nullptr) {
287 restartSocketTimer();
288 currentJob->handleResponse(response);
290 qCWarning(KIMAP_LOG) <<
"A message was received from the server with no job to handle it:" << response.toString()
291 <<
'(' + response.toString().toHex() +
')';
295void SessionPrivate::setState(Session::State s)
298 Session::State oldState = state;
300 Q_EMIT q->stateChanged(state, oldState);
310 payload +=
' ' + args;
315 if (command ==
"LOGIN" || command ==
"AUTHENTICATE") {
317 }
else if (command ==
"SELECT" || command ==
"EXAMINE") {
319 upcomingMailBox = args;
320 upcomingMailBox.
remove(0, 1);
321 upcomingMailBox = upcomingMailBox.
left(upcomingMailBox.indexOf(
'\"'));
323 }
else if (command ==
"CLOSE") {
329void SessionPrivate::sendData(
const QByteArray &data)
331 restartSocketTimer();
333 if (logger && isConnected()) {
337 thread->sendData(data +
"\r\n");
340void SessionPrivate::socketConnected()
343 isSocketConnected =
true;
345 bool willUseSsl =
false;
346 if (!queue.isEmpty()) {
347 auto login = qobject_cast<KIMAP::LoginJob *>(queue.first());
349 willUseSsl = (login->encryptionMode() == KIMAP::LoginJob::SSLorTLS);
351 userName = login->userName();
355 if (state == Session::Disconnected && willUseSsl) {
362bool SessionPrivate::isConnected()
const
364 return state == Session::Authenticated || state == Session::Selected;
367void SessionPrivate::socketDisconnected()
369 if (socketTimer.isActive()) {
373 if (logger && isConnected()) {
374 logger->disconnectionOccured();
377 if (isSocketConnected) {
378 setState(Session::Disconnected);
379 Q_EMIT q->connectionLost();
381 Q_EMIT q->connectionFailed();
384 isSocketConnected =
false;
389void SessionPrivate::socketActivity()
391 restartSocketTimer();
396 if (socketTimer.isActive()) {
401 currentJob->d_ptr->setSocketError(error);
402 }
else if (!queue.isEmpty()) {
403 currentJob = queue.takeFirst();
404 currentJob->d_ptr->setSocketError(error);
407 if (isSocketConnected) {
408 thread->closeSocket();
410 Q_EMIT q->connectionFailed();
415void SessionPrivate::clearJobQueue()
418 currentJob->connectionLost();
419 }
else if (!queue.isEmpty()) {
420 currentJob = queue.takeFirst();
421 currentJob->connectionLost();
425 qDeleteAll(queueCopy);
427 Q_EMIT q->jobQueueSizeChanged(0);
432 thread->startSsl(protocol);
435QString Session::selectedMailBox()
const
440void SessionPrivate::onEncryptionNegotiationResult(
bool isEncrypted,
QSsl::SslProtocol protocol)
443 sslVersion = protocol;
447 Q_EMIT encryptionNegotiationResult(isEncrypted);
455void SessionPrivate::setSocketTimeout(
int ms)
457 bool timerActive = socketTimer.isActive();
463 socketTimerInterval = ms;
470int SessionPrivate::socketTimeout()
const
472 return socketTimerInterval;
475void SessionPrivate::startSocketTimer()
477 if (socketTimerInterval < 0) {
480 Q_ASSERT(!socketTimer.isActive());
482 socketTimer.start(socketTimerInterval);
485void SessionPrivate::stopSocketTimer()
487 if (socketTimerInterval < 0) {
494void SessionPrivate::restartSocketTimer()
496 if (socketTimer.isActive()) {
502void SessionPrivate::onSocketTimeout()
504 qCDebug(KIMAP_LOG) <<
"Socket timeout!";
505 thread->closeSocket();
508void Session::setTimeout(
int timeout)
510 d->setSocketTimeout(timeout * 1000);
513int Session::timeout()
const
515 return d->socketTimeout() / 1000;
518#include "moc_session.cpp"
519#include "moc_session_p.cpp"
Interface to display communication errors and wait for user feedback.
QCA_EXPORT Logger * logger()
bool isEmpty() const const
QByteArray left(qsizetype len) const const
QByteArray number(double n, char format, int precision)
QByteArray & remove(qsizetype pos, qsizetype len)
QByteArray rightJustified(qsizetype width, char fill, bool truncate) const const
qsizetype size() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void destroyed(QObject *obj)
QString fromUtf8(QByteArrayView str)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the IMAP support library and defines the RfcCodecs class.
KIMAP_EXPORT QByteArray decodeImapFolderName(const QByteArray &inSrc)
Converts an UTF-7 encoded IMAP mailbox to a QByteArray.