11 #include "session_p.h"
16 #include "kimap_debug.h"
21 #include "response_p.h"
23 #include "sessionlogger_p.h"
24 #include "sessionthread_p.h"
28 static const int _kimap_sslVersionId = qRegisterMetaType<QSsl::SslProtocol>();
30 using namespace KIMAP;
32 Session::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);
54 connect(&d->socketTimer, &
QTimer::timeout, d, &SessionPrivate::onSocketTimeout);
56 d->startSocketTimer();
62 d->socketDisconnected();
77 QString Session::hostName()
const
79 return d->thread->hostName();
82 quint16 Session::port()
const
84 return d->thread->port();
87 void Session::setUseNetworkProxy(
bool useProxy)
89 d->thread->setUseNetworkProxy(useProxy);
92 Session::State Session::state()
const
97 QString Session::userName()
const
107 int Session::jobQueueSize()
const
109 return d->queue.
size() + (d->jobRunning ? 1 : 0);
112 void KIMAP::Session::close()
114 d->thread->closeSocket();
121 const bool ignoreSslError = uiProxy && uiProxy->ignoreSslError(errorData);
123 _t->sslErrorHandlerResponse(ignoreSslError);
127 SessionPrivate::SessionPrivate(Session *session)
130 , isSocketConnected(false)
135 , currentJob(nullptr)
138 , socketTimerInterval(30000)
142 SessionPrivate::~SessionPrivate()
147 void SessionPrivate::addJob(Job *job)
150 Q_EMIT q->jobQueueSizeChanged(q->jobQueueSize());
155 if (state != Session::Disconnected) {
160 void SessionPrivate::startNext()
165 void SessionPrivate::doStartNext()
167 if (queue.isEmpty() || jobRunning || !isSocketConnected) {
171 restartSocketTimer();
174 currentJob = queue.dequeue();
175 currentJob->doStart();
178 void SessionPrivate::jobDone(
KJob *job)
181 Q_ASSERT(job == currentJob);
186 currentJob =
nullptr;
187 Q_EMIT q->jobQueueSizeChanged(q->jobQueueSize());
191 void SessionPrivate::jobDestroyed(
QObject *job)
193 queue.removeAll(
static_cast<KIMAP::Job *
>(job));
194 if (currentJob == job) {
195 currentJob =
nullptr;
199 void 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() +
')';
295 void 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") {
329 void SessionPrivate::sendData(
const QByteArray &data)
331 restartSocketTimer();
333 if (logger && isConnected()) {
337 thread->sendData(data +
"\r\n");
340 void 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) {
362 bool SessionPrivate::isConnected()
const
364 return state == Session::Authenticated || state == Session::Selected;
367 void 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;
389 void 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();
415 void 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);
435 QString Session::selectedMailBox()
const
440 void SessionPrivate::onEncryptionNegotiationResult(
bool isEncrypted,
QSsl::SslProtocol protocol)
443 sslVersion = protocol;
447 Q_EMIT encryptionNegotiationResult(isEncrypted);
455 void SessionPrivate::setSocketTimeout(
int ms)
457 bool timerActive = socketTimer.isActive();
463 socketTimerInterval = ms;
470 int SessionPrivate::socketTimeout()
const
472 return socketTimerInterval;
475 void SessionPrivate::startSocketTimer()
477 if (socketTimerInterval < 0) {
480 Q_ASSERT(!socketTimer.isActive());
482 socketTimer.start(socketTimerInterval);
485 void SessionPrivate::stopSocketTimer()
487 if (socketTimerInterval < 0) {
494 void SessionPrivate::restartSocketTimer()
496 if (socketTimer.isActive()) {
502 void SessionPrivate::onSocketTimeout()
504 qCDebug(KIMAP_LOG) <<
"Socket timeout!";
505 thread->closeSocket();
508 void Session::setTimeout(
int timeout)
510 d->setSocketTimeout(timeout * 1000);
513 int Session::timeout()
const
515 return d->socketTimeout() / 1000;
518 #include "moc_session.cpp"
519 #include "moc_session_p.cpp"