File indexing completed on 2024-09-22 04:52:50
0001 /* Copyright (C) 2006 - 2014 Jan Kundrát <jkt@flaska.net> 0002 0003 This file is part of the Trojita Qt IMAP e-mail client, 0004 http://trojita.flaska.net/ 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU General Public License as 0008 published by the Free Software Foundation; either version 2 of 0009 the License or (at your option) version 3 or any later version 0010 accepted by the membership of KDE e.V. (or its successor approved 0011 by the membership of KDE e.V.), which shall act as a proxy 0012 defined in Section 14 of version 3 of the license. 0013 0014 This program is distributed in the hope that it will be useful, 0015 but WITHOUT ANY WARRANTY; without even the implied warranty of 0016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0017 GNU General Public License for more details. 0018 0019 You should have received a copy of the GNU General Public License 0020 along with this program. If not, see <http://www.gnu.org/licenses/>. 0021 */ 0022 #include <algorithm> 0023 #include <QDebug> 0024 #include <QStringList> 0025 #include <QMutexLocker> 0026 #include <QProcess> 0027 #include <QSslError> 0028 #include <QTime> 0029 #include <QTimer> 0030 #include "Parser.h" 0031 #include "Imap/Encoders.h" 0032 #include "LowLevelParser.h" 0033 #include "../../Streams/IODeviceSocket.h" 0034 #include "../Model/Utils.h" 0035 0036 //#define PRINT_TRAFFIC 100 0037 //#define PRINT_TRAFFIC_TX 500 0038 //#define PRINT_TRAFFIC_RX 25 0039 //#define PRINT_TRAFFIC_SENSITIVE 0040 0041 #ifdef PRINT_TRAFFIC 0042 # ifndef PRINT_TRAFFIC_TX 0043 # define PRINT_TRAFFIC_TX PRINT_TRAFFIC 0044 # endif 0045 # ifndef PRINT_TRAFFIC_RX 0046 # define PRINT_TRAFFIC_RX PRINT_TRAFFIC 0047 # endif 0048 #endif 0049 0050 /* 0051 * Parser interface considerations: 0052 * 0053 * - Parser receives comments and gives back some kind of ID for tracking the 0054 * command state 0055 * - "High-level stuff" like "has this command already finished" should be 0056 * implemented on higher level 0057 * - Due to command pipelining, there's no way to find out that this untagged 0058 * reply we just received was triggered by FOO command 0059 * 0060 * Interface: 0061 * 0062 * - One function per command 0063 * - Each received reply emits a signal (Qt-specific stuff now) 0064 * 0065 * 0066 * Usage example FIXME DRAFT: 0067 * 0068 * Imap::Parser parser; 0069 * Imap::CommandHandle res = parser.deleteFolder( "foo mailbox/bar/baz" ); 0070 * 0071 * 0072 * 0073 * How it works under the hood: 0074 * 0075 * - When there are any data available on the net, process them ASAP 0076 * - When user queues a command, process it ASAP 0077 * - You can't block the caller of the queueCommand() 0078 * 0079 * So, how to implement this? 0080 * 0081 * - Whenever something interesting happens (data/command/exit 0082 * requested/available), we ask the worker thread to do something 0083 * 0084 * */ 0085 0086 namespace Imap 0087 { 0088 0089 Parser::Parser(QObject *parent, Streams::Socket *socket, const uint myId): 0090 QObject(parent), socket(socket), m_lastTagUsed(0), idling(false), waitForInitialIdle(false), 0091 m_literalPlus(LiteralPlus::Unsupported), waitingForContinuation(false), startTlsInProgress(false), compressDeflateInProgress(false), 0092 waitingForConnection(true), waitingForEncryption(socket->isConnectingEncryptedSinceStart()), waitingForSslPolicy(false), 0093 m_expectsInitialGreeting(true), readingMode(ReadingLine), oldLiteralPosition(0), m_parserId(myId) 0094 { 0095 socket->setParent(this); 0096 connect(socket, &Streams::Socket::disconnected, this, &Parser::handleDisconnected); 0097 connect(socket, &Streams::Socket::readyRead, this, &Parser::handleReadyRead); 0098 connect(socket, &Streams::Socket::stateChanged, this, &Parser::slotSocketStateChanged); 0099 connect(socket, &Streams::Socket::encrypted, this, &Parser::handleSocketEncrypted); 0100 } 0101 0102 CommandHandle Parser::noop() 0103 { 0104 return queueCommand(Commands::ATOM, "NOOP"); 0105 } 0106 0107 CommandHandle Parser::logout() 0108 { 0109 return queueCommand(Commands::Command("LOGOUT")); 0110 0111 // Queue a request for closing the socket. It'll get closed after a short while. 0112 QTimer::singleShot(1000, this, SLOT(closeConnection())); 0113 } 0114 0115 /** @short Close the underlying conneciton */ 0116 void Parser::closeConnection() 0117 { 0118 socket->close(); 0119 } 0120 0121 CommandHandle Parser::capability() 0122 { 0123 // CAPABILITY should take precedence over LOGIN, because we have to check for LOGINDISABLED 0124 return queueCommand(Commands::Command() << 0125 Commands::PartOfCommand(Commands::ATOM, "CAPABILITY")); 0126 } 0127 0128 CommandHandle Parser::startTls() 0129 { 0130 return queueCommand(Commands::Command() << 0131 Commands::PartOfCommand(Commands::STARTTLS, "STARTTLS")); 0132 } 0133 0134 CommandHandle Parser::compressDeflate() 0135 { 0136 return queueCommand(Commands::Command() << 0137 Commands::PartOfCommand(Commands::COMPRESS_DEFLATE, "COMPRESS DEFLATE")); 0138 } 0139 0140 #if 0 0141 CommandHandle Parser::authenticate(/*Authenticator FIXME*/) 0142 { 0143 // FIXME: needs higher priority 0144 return queueCommand(Commands::ATOM, "AUTHENTICATE"); 0145 } 0146 #endif 0147 0148 CommandHandle Parser::login(const QString &username, const QString &password) 0149 { 0150 return queueCommand(Commands::Command("LOGIN") << 0151 Commands::PartOfCommand(username.toUtf8()) << Commands::PartOfCommand(password.toUtf8())); 0152 } 0153 0154 CommandHandle Parser::select(const QString &mailbox, const QList<QByteArray> ¶ms) 0155 { 0156 Commands::Command cmd = Commands::Command("SELECT") << encodeImapFolderName(mailbox); 0157 if (!params.isEmpty()) { 0158 cmd << Commands::PartOfCommand(Commands::ATOM_NO_SPACE_AROUND, " ("); 0159 Q_FOREACH(const QByteArray ¶m, params) { 0160 cmd << Commands::PartOfCommand(Commands::ATOM, param); 0161 } 0162 cmd << Commands::PartOfCommand(Commands::ATOM_NO_SPACE_AROUND, ")"); 0163 } 0164 return queueCommand(cmd); 0165 } 0166 0167 CommandHandle Parser::selectQresync(const QString &mailbox, const uint uidValidity, 0168 const quint64 highestModSeq, const Sequence &knownUids, const Sequence &sequenceSnapshot, 0169 const Sequence &uidSnapshot) 0170 { 0171 Commands::Command cmd = Commands::Command("SELECT") << encodeImapFolderName(mailbox) << 0172 Commands::PartOfCommand(Commands::ATOM_NO_SPACE_AROUND, " (QRESYNC (") << 0173 Commands::PartOfCommand(Commands::ATOM, QByteArray::number(uidValidity)) << 0174 Commands::PartOfCommand(Commands::ATOM, QByteArray::number(highestModSeq)); 0175 if (knownUids.isValid()) { 0176 cmd << Commands::PartOfCommand(Commands::ATOM, knownUids.toByteArray()); 0177 } 0178 Q_ASSERT(uidSnapshot.isValid() == sequenceSnapshot.isValid()); 0179 if (sequenceSnapshot.isValid()) { 0180 cmd << Commands::PartOfCommand(Commands::ATOM_NO_SPACE_AROUND, " (") << 0181 Commands::PartOfCommand(Commands::ATOM, sequenceSnapshot.toByteArray()) << 0182 Commands::PartOfCommand(Commands::ATOM, uidSnapshot.toByteArray()) << 0183 Commands::PartOfCommand(Commands::ATOM_NO_SPACE_AROUND, ")))"); 0184 } else { 0185 cmd << Commands::PartOfCommand(Commands::ATOM_NO_SPACE_AROUND, "))"); 0186 } 0187 return queueCommand(cmd); 0188 } 0189 0190 CommandHandle Parser::examine(const QString &mailbox, const QList<QByteArray> ¶ms) 0191 { 0192 Commands::Command cmd = Commands::Command("EXAMINE") << encodeImapFolderName(mailbox); 0193 if (!params.isEmpty()) { 0194 cmd << Commands::PartOfCommand(Commands::ATOM_NO_SPACE_AROUND, " ("); 0195 Q_FOREACH(const QByteArray ¶m, params) { 0196 cmd << Commands::PartOfCommand(Commands::ATOM, param); 0197 } 0198 cmd << Commands::PartOfCommand(Commands::ATOM_NO_SPACE_AROUND, ")"); 0199 } 0200 return queueCommand(cmd); 0201 } 0202 0203 CommandHandle Parser::deleteMailbox(const QString &mailbox) 0204 { 0205 return queueCommand(Commands::Command("DELETE") << encodeImapFolderName(mailbox)); 0206 } 0207 0208 CommandHandle Parser::create(const QString &mailbox) 0209 { 0210 return queueCommand(Commands::Command("CREATE") << encodeImapFolderName(mailbox)); 0211 } 0212 0213 CommandHandle Parser::rename(const QString &oldName, const QString &newName) 0214 { 0215 return queueCommand(Commands::Command("RENAME") << 0216 encodeImapFolderName(oldName) << 0217 encodeImapFolderName(newName)); 0218 } 0219 0220 CommandHandle Parser::subscribe(const QString &mailbox) 0221 { 0222 return queueCommand(Commands::Command("SUBSCRIBE") << encodeImapFolderName(mailbox)); 0223 } 0224 0225 CommandHandle Parser::unSubscribe(const QString &mailbox) 0226 { 0227 return queueCommand(Commands::Command("UNSUBSCRIBE") << encodeImapFolderName(mailbox)); 0228 } 0229 0230 CommandHandle Parser::list(const QString &reference, const QString &mailbox, const QStringList &returnOptions) 0231 { 0232 Commands::Command cmd("LIST"); 0233 cmd << reference.toUtf8() << encodeImapFolderName(mailbox); 0234 if (!returnOptions.isEmpty()) { 0235 cmd << Commands::PartOfCommand(Commands::ATOM_NO_SPACE_AROUND, " RETURN ("); 0236 Q_FOREACH(const QString &option, returnOptions) { 0237 cmd << Commands::PartOfCommand(Commands::ATOM, option.toUtf8()); 0238 } 0239 cmd << Commands::PartOfCommand(Commands::ATOM_NO_SPACE_AROUND, ")"); 0240 } 0241 return queueCommand(cmd); 0242 } 0243 0244 CommandHandle Parser::lSub(const QString &reference, const QString &mailbox) 0245 { 0246 return queueCommand(Commands::Command("LSUB") << reference.toUtf8() << encodeImapFolderName(mailbox)); 0247 } 0248 0249 CommandHandle Parser::status(const QString &mailbox, const QStringList &fields) 0250 { 0251 return queueCommand(Commands::Command("STATUS") << encodeImapFolderName(mailbox) << 0252 Commands::PartOfCommand(Commands::ATOM, "(" + fields.join(QStringLiteral(" ")).toUtf8() + ")") 0253 ); 0254 } 0255 0256 CommandHandle Parser::append(const QString &mailbox, const QByteArray &message, const QStringList &flags, const QDateTime ×tamp) 0257 { 0258 Commands::Command command("APPEND"); 0259 command << encodeImapFolderName(mailbox); 0260 if (flags.count()) 0261 command << Commands::PartOfCommand(Commands::ATOM, "(" + flags.join(QStringLiteral(" ")).toUtf8() + ")"); 0262 if (timestamp.isValid()) 0263 command << Commands::PartOfCommand(Imap::dateTimeToInternalDate(timestamp).toUtf8()); 0264 command << Commands::PartOfCommand(Commands::LITERAL, message); 0265 0266 return queueCommand(command); 0267 } 0268 0269 CommandHandle Parser::appendCatenate(const QString &mailbox, const QList<Imap::Mailbox::CatenatePair> &data, 0270 const QStringList &flags, const QDateTime ×tamp) 0271 { 0272 Commands::Command command("APPEND"); 0273 command << encodeImapFolderName(mailbox); 0274 if (flags.count()) 0275 command << Commands::PartOfCommand(Commands::ATOM, "(" + flags.join(QStringLiteral(" ")).toUtf8() + ")"); 0276 if (timestamp.isValid()) 0277 command << Commands::PartOfCommand(Imap::dateTimeToInternalDate(timestamp).toUtf8()); 0278 command << Commands::PartOfCommand(Commands::ATOM_NO_SPACE_AROUND, " CATENATE ("); 0279 Q_FOREACH(const Imap::Mailbox::CatenatePair &item, data) { 0280 switch (item.first) { 0281 case Imap::Mailbox::CATENATE_TEXT: 0282 command << Commands::PartOfCommand(Commands::ATOM, "TEXT"); 0283 command << Commands::PartOfCommand(Commands::LITERAL, item.second); 0284 break; 0285 case Imap::Mailbox::CATENATE_URL: 0286 command << Commands::PartOfCommand(Commands::ATOM, "URL"); 0287 command << Commands::PartOfCommand(item.second); 0288 break; 0289 } 0290 } 0291 command << Commands::PartOfCommand(Commands::ATOM_NO_SPACE_AROUND, ")"); 0292 0293 return queueCommand(command); 0294 } 0295 0296 CommandHandle Parser::check() 0297 { 0298 return queueCommand(Commands::ATOM, "CHECK"); 0299 } 0300 0301 CommandHandle Parser::close() 0302 { 0303 return queueCommand(Commands::ATOM, "CLOSE"); 0304 } 0305 0306 CommandHandle Parser::expunge() 0307 { 0308 return queueCommand(Commands::ATOM, "EXPUNGE"); 0309 } 0310 0311 CommandHandle Parser::searchHelper(const QByteArray &command, const QStringList &criteria, const QByteArray &charset) 0312 { 0313 Commands::Command cmd(command); 0314 0315 if (!charset.isEmpty()) 0316 cmd << "CHARSET" << charset; 0317 0318 // FIXME: we don't really support anything else but utf-8 here 0319 0320 if (criteria.size() == 1) { 0321 // Hack: if it's just a single item, let's assume it's already well-formatted by the caller. 0322 // This is required in the current shape of the API if we want to allow the user to type in their queries directly. 0323 cmd << Commands::PartOfCommand(Commands::ATOM, criteria.front().toUtf8()); 0324 } else { 0325 for (QStringList::const_iterator it = criteria.begin(); it != criteria.end(); ++it) 0326 cmd << it->toUtf8(); 0327 } 0328 0329 return queueCommand(cmd); 0330 } 0331 0332 CommandHandle Parser::uidSearchUid(const QByteArray &sequence) 0333 { 0334 Commands::Command command("UID SEARCH"); 0335 command << Commands::PartOfCommand(Commands::ATOM, sequence); 0336 return queueCommand(command); 0337 } 0338 0339 CommandHandle Parser::uidESearchUid(const QByteArray &sequence) 0340 { 0341 Commands::Command command("UID SEARCH RETURN (ALL)"); 0342 command << Commands::PartOfCommand(Commands::ATOM, sequence); 0343 return queueCommand(command); 0344 } 0345 0346 CommandHandle Parser::sortHelper(const QByteArray &command, const QStringList &sortCriteria, const QByteArray &charset, const QStringList &searchCriteria) 0347 { 0348 Q_ASSERT(! sortCriteria.isEmpty()); 0349 Commands::Command cmd; 0350 0351 cmd << Commands::PartOfCommand(Commands::ATOM, command) << 0352 Commands::PartOfCommand(Commands::ATOM, "(" + sortCriteria.join(QStringLiteral(" ")).toUtf8() + ")" ) << 0353 charset; 0354 0355 if (searchCriteria.size() == 1) { 0356 // Hack: if it's just a single item, let's assume it's already well-formatted by the caller. 0357 // This is required in the current shape of the API if we want to allow the user to type in their queries directly. 0358 cmd << Commands::PartOfCommand(Commands::ATOM, searchCriteria.front().toUtf8()); 0359 } else { 0360 for (QStringList::const_iterator it = searchCriteria.begin(); it != searchCriteria.end(); ++it) 0361 cmd << it->toUtf8(); 0362 } 0363 0364 return queueCommand(cmd); 0365 } 0366 0367 CommandHandle Parser::sort(const QStringList &sortCriteria, const QByteArray &charset, const QStringList &searchCriteria) 0368 { 0369 return sortHelper("SORT", sortCriteria, charset, searchCriteria); 0370 } 0371 0372 CommandHandle Parser::uidSort(const QStringList &sortCriteria, const QByteArray &charset, const QStringList &searchCriteria) 0373 { 0374 return sortHelper("UID SORT", sortCriteria, charset, searchCriteria); 0375 } 0376 0377 CommandHandle Parser::uidESort(const QStringList &sortCriteria, const QByteArray &charset, const QStringList &searchCriteria, 0378 const QStringList &returnOptions) 0379 { 0380 return sortHelper("UID SORT RETURN (" + returnOptions.join(QStringLiteral(" ")).toUtf8() + ")", 0381 sortCriteria, charset, searchCriteria); 0382 } 0383 0384 CommandHandle Parser::uidESearch(const QByteArray &charset, const QStringList &searchCriteria, const QStringList &returnOptions) 0385 { 0386 return searchHelper("UID SEARCH RETURN (" + returnOptions.join(QStringLiteral(" ")).toUtf8() + ")", 0387 searchCriteria, charset); 0388 } 0389 0390 CommandHandle Parser::cancelUpdate(const CommandHandle &tag) 0391 { 0392 Commands::Command command("CANCELUPDATE"); 0393 command << Commands::PartOfCommand(Commands::QUOTED_STRING, tag); 0394 return queueCommand(command); 0395 } 0396 0397 CommandHandle Parser::threadHelper(const QByteArray &command, const QByteArray &algo, const QByteArray &charset, const QStringList &searchCriteria) 0398 { 0399 Commands::Command cmd; 0400 0401 cmd << Commands::PartOfCommand(Commands::ATOM, command) << algo << charset; 0402 0403 for (QStringList::const_iterator it = searchCriteria.begin(); it != searchCriteria.end(); ++it) { 0404 // FIXME: this is another place which needs proper structure for this searching stuff... 0405 cmd << Commands::PartOfCommand(Commands::ATOM, it->toUtf8()); 0406 } 0407 0408 return queueCommand(cmd); 0409 } 0410 0411 CommandHandle Parser::thread(const QByteArray &algo, const QByteArray &charset, const QStringList &searchCriteria) 0412 { 0413 return threadHelper("THREAD", algo, charset, searchCriteria); 0414 } 0415 0416 CommandHandle Parser::uidThread(const QByteArray &algo, const QByteArray &charset, const QStringList &searchCriteria) 0417 { 0418 return threadHelper("UID THREAD", algo, charset, searchCriteria); 0419 } 0420 0421 CommandHandle Parser::uidEThread(const QByteArray &algo, const QByteArray &charset, const QStringList &searchCriteria, 0422 const QStringList &returnOptions) 0423 { 0424 return threadHelper("UID THREAD RETURN (" + returnOptions.join(QStringLiteral(" ")).toUtf8() + ")", 0425 algo, charset, searchCriteria); 0426 } 0427 0428 CommandHandle Parser::fetch(const Sequence &seq, const QStringList &items, const QMap<QByteArray, quint64> &uint64Modifiers) 0429 { 0430 Commands::Command cmd = Commands::Command("FETCH") << 0431 Commands::PartOfCommand(Commands::ATOM, seq.toByteArray()) << 0432 Commands::PartOfCommand(Commands::ATOM, '(' + items.join(QStringLiteral(" ")).toUtf8() + ')'); 0433 if (!uint64Modifiers.isEmpty()) { 0434 cmd << Commands::PartOfCommand(Commands::ATOM_NO_SPACE_AROUND, " ("); 0435 for (QMap<QByteArray, quint64>::const_iterator it = uint64Modifiers.constBegin(); it != uint64Modifiers.constEnd(); ++it) { 0436 cmd << Commands::PartOfCommand(Commands::ATOM, it.key()) << 0437 Commands::PartOfCommand(Commands::ATOM, QByteArray::number(it.value())); 0438 } 0439 cmd << Commands::PartOfCommand(Commands::ATOM_NO_SPACE_AROUND, ")"); 0440 } 0441 return queueCommand(cmd); 0442 } 0443 0444 CommandHandle Parser::store(const Sequence &seq, const QString &item, const QString &value) 0445 { 0446 return queueCommand(Commands::Command("STORE") << 0447 Commands::PartOfCommand(Commands::ATOM, seq.toByteArray()) << 0448 Commands::PartOfCommand(Commands::ATOM, item.toUtf8()) << 0449 Commands::PartOfCommand(Commands::ATOM, value.toUtf8()) 0450 ); 0451 } 0452 0453 CommandHandle Parser::copy(const Sequence &seq, const QString &mailbox) 0454 { 0455 return queueCommand(Commands::Command("COPY") << 0456 Commands::PartOfCommand(Commands::ATOM, seq.toByteArray()) << 0457 encodeImapFolderName(mailbox)); 0458 } 0459 0460 CommandHandle Parser::uidFetch(const Sequence &seq, const QList<QByteArray> &items) 0461 { 0462 QByteArray buf; 0463 Q_FOREACH(const QByteArray &item, items) { 0464 buf += ' ' + item; 0465 } 0466 buf += ')'; 0467 buf[0] = '('; 0468 return queueCommand(Commands::Command("UID FETCH") << 0469 Commands::PartOfCommand(Commands::ATOM, seq.toByteArray()) << 0470 Commands::PartOfCommand(Commands::ATOM, buf)); 0471 } 0472 0473 CommandHandle Parser::uidStore(const Sequence &seq, const QString &item, const QString &value) 0474 { 0475 return queueCommand(Commands::Command("UID STORE") << 0476 Commands::PartOfCommand(Commands::ATOM, seq.toByteArray()) << 0477 Commands::PartOfCommand(Commands::ATOM, item.toUtf8()) << 0478 Commands::PartOfCommand(Commands::ATOM, value.toUtf8())); 0479 } 0480 0481 CommandHandle Parser::uidCopy(const Sequence &seq, const QString &mailbox) 0482 { 0483 return queueCommand(Commands::Command("UID COPY") << 0484 Commands::PartOfCommand(Commands::ATOM, seq.toByteArray()) << 0485 encodeImapFolderName(mailbox)); 0486 } 0487 0488 CommandHandle Parser::uidMove(const Sequence &seq, const QString &mailbox) 0489 { 0490 return queueCommand(Commands::Command("UID MOVE") << 0491 Commands::PartOfCommand(Commands::ATOM, seq.toByteArray()) << 0492 encodeImapFolderName(mailbox)); 0493 } 0494 0495 CommandHandle Parser::uidExpunge(const Sequence &seq) 0496 { 0497 return queueCommand(Commands::Command("UID EXPUNGE") << 0498 Commands::PartOfCommand(Commands::ATOM, seq.toByteArray())); 0499 } 0500 0501 CommandHandle Parser::xAtom(const Commands::Command &cmd) 0502 { 0503 return queueCommand(cmd); 0504 } 0505 0506 CommandHandle Parser::unSelect() 0507 { 0508 return queueCommand(Commands::ATOM, "UNSELECT"); 0509 } 0510 0511 CommandHandle Parser::idle() 0512 { 0513 return queueCommand(Commands::IDLE, "IDLE"); 0514 } 0515 0516 void Parser::idleDone() 0517 { 0518 // This is not a new "command", so we don't go via queueCommand() 0519 // which would allocate a new tag for us, but submit directly 0520 Commands::Command cmd; 0521 cmd << Commands::PartOfCommand(Commands::IDLE_DONE, "DONE"); 0522 cmdQueue.push_back(cmd); 0523 QTimer::singleShot(0, this, SLOT(executeCommands())); 0524 } 0525 0526 void Parser::idleContinuationWontCome() 0527 { 0528 Q_ASSERT(waitForInitialIdle); 0529 waitForInitialIdle = false; 0530 idling = false; 0531 QTimer::singleShot(0, this, SLOT(executeCommands())); 0532 } 0533 0534 void Parser::idleMagicallyTerminatedByServer() 0535 { 0536 Q_ASSERT(! waitForInitialIdle); 0537 Q_ASSERT(idling); 0538 idling = false; 0539 } 0540 0541 CommandHandle Parser::namespaceCommand() 0542 { 0543 return queueCommand(Commands::ATOM, "NAMESPACE"); 0544 } 0545 0546 CommandHandle Parser::idCommand() 0547 { 0548 return queueCommand(Commands::Command("ID NIL")); 0549 } 0550 0551 CommandHandle Parser::idCommand(const QMap<QByteArray,QByteArray> &args) 0552 { 0553 Commands::Command cmd("ID "); 0554 cmd << Commands::PartOfCommand(Commands::ATOM_NO_SPACE_AROUND, "("); 0555 for (QMap<QByteArray,QByteArray>::const_iterator it = args.constBegin(); it != args.constEnd(); ++it) { 0556 cmd << Commands::PartOfCommand(Commands::QUOTED_STRING, it.key()) << Commands::PartOfCommand(Commands::QUOTED_STRING, it.value()); 0557 } 0558 cmd << Commands::PartOfCommand(Commands::ATOM_NO_SPACE_AROUND, ")"); 0559 return queueCommand(cmd); 0560 } 0561 0562 CommandHandle Parser::enable(const QList<QByteArray> &extensions) 0563 { 0564 Commands::Command cmd("ENABLE"); 0565 Q_FOREACH(const QByteArray &item, extensions) { 0566 cmd << Commands::PartOfCommand(Commands::ATOM, item); 0567 } 0568 return queueCommand(cmd); 0569 } 0570 0571 CommandHandle Parser::genUrlAuth(const QByteArray &url, const QByteArray mechanism) 0572 { 0573 Commands::Command cmd("GENURLAUTH"); 0574 cmd << Commands::PartOfCommand(Commands::QUOTED_STRING, url); 0575 cmd << Commands::PartOfCommand(Commands::ATOM, mechanism); 0576 return queueCommand(cmd); 0577 } 0578 0579 CommandHandle Parser::uidSendmail(const uint uid, const Mailbox::UidSubmitOptionsList &submissionOptions) 0580 { 0581 Commands::Command cmd("UID SENDMAIL"); 0582 cmd << Commands::PartOfCommand(QByteArray::number(uid)); 0583 if (!submissionOptions.isEmpty()) { 0584 cmd << Commands::PartOfCommand(Commands::ATOM_NO_SPACE_AROUND, " ("); 0585 for (Mailbox::UidSubmitOptionsList::const_iterator it = submissionOptions.begin(); it != submissionOptions.end(); ++it) { 0586 cmd << Commands::PartOfCommand(Commands::ATOM, it->first); 0587 switch (it->second.type()) { 0588 case QVariant::ByteArray: 0589 cmd << Commands::PartOfCommand(it->second.toByteArray()); 0590 break; 0591 case QVariant::List: 0592 cmd << Commands::PartOfCommand(Commands::ATOM_NO_SPACE_AROUND, " ("); 0593 Q_FOREACH(const QVariant &item, it->second.toList()) { 0594 cmd << Commands::PartOfCommand(Commands::ATOM, item.toByteArray()); 0595 } 0596 cmd << Commands::PartOfCommand(Commands::ATOM_NO_SPACE_AROUND, ")"); 0597 break; 0598 case QVariant::Invalid: 0599 cmd << Commands::PartOfCommand(Commands::ATOM, "NIL"); 0600 break; 0601 default: 0602 throw InvalidArgument("Internal error: Malformed data for the UID SEND command."); 0603 } 0604 } 0605 cmd << Commands::PartOfCommand(Commands::ATOM_NO_SPACE_AROUND, ")"); 0606 } 0607 return queueCommand(cmd); 0608 } 0609 0610 CommandHandle Parser::queueCommand(Commands::Command command) 0611 { 0612 CommandHandle tag = generateTag(); 0613 command.addTag(tag); 0614 cmdQueue.push_back(command); 0615 QTimer::singleShot(0, this, SLOT(executeCommands())); 0616 return tag; 0617 } 0618 0619 void Parser::queueResponse(const QSharedPointer<Responses::AbstractResponse> &resp) 0620 { 0621 respQueue.push_back(resp); 0622 // Try to limit the signal rate -- when there are multiple items in the queue, there's no point in sending more signals 0623 if (respQueue.size() == 1) { 0624 emit responseReceived(this); 0625 } 0626 0627 if (waitingForContinuation) { 0628 // Check whether this is the server's way of informing us that the continuation request is not going to arrive 0629 QSharedPointer<Responses::State> stateResponse = resp.dynamicCast<Responses::State>(); 0630 Q_ASSERT(!literalCommandTag.isEmpty()); 0631 if (stateResponse && stateResponse->tag == literalCommandTag) { 0632 literalCommandTag.clear(); 0633 waitingForContinuation = false; 0634 cmdQueue.pop_front(); 0635 QTimer::singleShot(0, this, SLOT(executeCommands())); 0636 if (stateResponse->kind != Responses::NO && stateResponse->kind != Responses::BAD) { 0637 // FIXME: use parserWarning when it's adapted throughout the code 0638 qDebug() << "Synchronized literal rejected but response is neither NO nor BAD"; 0639 } 0640 } 0641 } 0642 } 0643 0644 bool Parser::hasResponse() const 0645 { 0646 return ! respQueue.empty(); 0647 } 0648 0649 QSharedPointer<Responses::AbstractResponse> Parser::getResponse() 0650 { 0651 QSharedPointer<Responses::AbstractResponse> ptr; 0652 if (respQueue.empty()) 0653 return ptr; 0654 ptr = respQueue.front(); 0655 respQueue.pop_front(); 0656 return ptr; 0657 } 0658 0659 QByteArray Parser::generateTag() 0660 { 0661 return QStringLiteral("y%1").arg(m_lastTagUsed++).toUtf8(); 0662 } 0663 0664 void Parser::handleReadyRead() 0665 { 0666 while (!waitingForEncryption && !waitingForSslPolicy) { 0667 switch (readingMode) { 0668 case ReadingLine: 0669 if (socket->canReadLine()) { 0670 reallyReadLine(); 0671 } else { 0672 // Not enough data yet, let's try again later 0673 return; 0674 } 0675 break; 0676 case ReadingNumberOfBytes: 0677 { 0678 QByteArray buf = socket->read(readingBytes); 0679 readingBytes -= buf.size(); 0680 currentLine += buf; 0681 if (readingBytes == 0) { 0682 // we've read the literal 0683 readingMode = ReadingLine; 0684 } else { 0685 return; 0686 } 0687 } 0688 break; 0689 } 0690 } 0691 } 0692 0693 void Parser::reallyReadLine() 0694 { 0695 try { 0696 currentLine += socket->readLine(); 0697 if (currentLine.endsWith("}\r\n")) { 0698 int offset = currentLine.lastIndexOf('{'); 0699 if (offset < oldLiteralPosition) 0700 throw ParseError("Got unmatched '}'", currentLine, currentLine.size() - 3); 0701 bool ok; 0702 int number = currentLine.mid(offset + 1, currentLine.size() - offset - 4).toInt(&ok); 0703 if (!ok) 0704 throw ParseError("Can't parse numeric literal size", currentLine, offset); 0705 if (number < 0) 0706 throw ParseError("Negative literal size", currentLine, offset); 0707 oldLiteralPosition = offset; 0708 readingMode = ReadingNumberOfBytes; 0709 readingBytes = number; 0710 } else if (currentLine.endsWith("\r\n")) { 0711 // it's complete 0712 if (startTlsInProgress && currentLine.startsWith(startTlsCommand)) { 0713 startTlsCommand.clear(); 0714 startTlsReply = currentLine; 0715 currentLine.clear(); 0716 oldLiteralPosition = 0; 0717 QTimer::singleShot(0, this, SLOT(finishStartTls())); 0718 return; 0719 } 0720 processLine(currentLine); 0721 currentLine.clear(); 0722 oldLiteralPosition = 0; 0723 } else { 0724 throw ParseError("Received line doesn't end with any of \"}\\r\\n\" and \"\\r\\n\"", currentLine, 0); 0725 } 0726 } catch (ParserException &e) { 0727 queueResponse(QSharedPointer<Responses::AbstractResponse>(new Responses::ParseErrorResponse(e))); 0728 } 0729 } 0730 0731 void Parser::executeCommands() 0732 { 0733 while (! waitingForContinuation && ! waitForInitialIdle && 0734 ! waitingForConnection && ! waitingForEncryption && ! waitingForSslPolicy && 0735 ! cmdQueue.empty() && ! startTlsInProgress && !compressDeflateInProgress) 0736 executeACommand(); 0737 } 0738 0739 void Parser::finishStartTls() 0740 { 0741 emit lineSent(this, "*** STARTTLS"); 0742 #ifdef PRINT_TRAFFIC_TX 0743 qDebug() << m_parserId << "*** STARTTLS"; 0744 #endif 0745 cmdQueue.pop_front(); 0746 socket->startTls(); // warn: this might invoke event loop 0747 startTlsInProgress = false; 0748 waitingForEncryption = true; 0749 processLine(startTlsReply); 0750 } 0751 0752 void Parser::handleSocketEncrypted() 0753 { 0754 waitingForEncryption = false; 0755 waitingForConnection = false; 0756 waitingForSslPolicy = true; 0757 QSharedPointer<Responses::AbstractResponse> resp( 0758 new Responses::SocketEncryptedResponse(socket->sslChain(), socket->sslErrors())); 0759 QByteArray buf; 0760 QTextStream ss(&buf); 0761 ss << "*** " << *resp; 0762 ss.flush(); 0763 #ifdef PRINT_TRAFFIC_RX 0764 qDebug() << m_parserId << "***" << buf; 0765 #endif 0766 emit lineReceived(this, buf); 0767 handleReadyRead(); 0768 queueResponse(resp); 0769 executeCommands(); 0770 } 0771 0772 /** @short We've previously frozen the command queue, so it's time to kick it a bit and keep the usual sending/receiving again */ 0773 void Parser::handleCompressionPossibleActivated() 0774 { 0775 handleReadyRead(); 0776 executeCommands(); 0777 } 0778 0779 void Parser::unfreezeAfterEncryption() 0780 { 0781 Q_ASSERT(waitingForSslPolicy); 0782 waitingForSslPolicy = false; 0783 handleReadyRead(); 0784 executeCommands(); 0785 } 0786 0787 void Parser::executeACommand() 0788 { 0789 Q_ASSERT(! cmdQueue.empty()); 0790 Commands::Command &cmd = cmdQueue.front(); 0791 0792 QByteArray buf; 0793 0794 bool sensitiveCommand = (cmd.cmds.size() > 2 && cmd.cmds[1].text == "LOGIN"); 0795 QByteArray privateMessage = sensitiveCommand ? QByteArray("[LOGIN command goes here]") : QByteArray(); 0796 0797 #ifdef PRINT_TRAFFIC_TX 0798 #ifdef PRINT_TRAFFIC_SENSITIVE 0799 bool printThisCommand = true; 0800 #else 0801 bool printThisCommand = ! sensitiveCommand; 0802 #endif 0803 #endif 0804 0805 if (cmd.cmds[ cmd.currentPart ].kind == Commands::IDLE_DONE) { 0806 // Handling of the IDLE_DONE is a bit special, as we have to check and update the idling flag... 0807 Q_ASSERT(idling); 0808 buf.append("DONE\r\n"); 0809 #ifdef PRINT_TRAFFIC_TX 0810 qDebug() << m_parserId << ">>>" << buf.left(PRINT_TRAFFIC_TX).trimmed(); 0811 #endif 0812 socket->write(buf); 0813 idling = false; 0814 cmdQueue.pop_front(); 0815 emit lineSent(this, buf); 0816 buf.clear(); 0817 return; 0818 } 0819 0820 Q_ASSERT(! idling); 0821 0822 while (1) { 0823 Commands::PartOfCommand &part = cmd.cmds[ cmd.currentPart ]; 0824 switch (part.kind) { 0825 case Commands::ATOM: 0826 case Commands::ATOM_NO_SPACE_AROUND: 0827 buf.append(part.text); 0828 break; 0829 case Commands::QUOTED_STRING: 0830 { 0831 QByteArray item = part.text; 0832 item.replace('\\', "\\\\"); 0833 buf.append('"'); 0834 buf.append(item); 0835 buf.append('"'); 0836 } 0837 break; 0838 case Commands::LITERAL: 0839 if (m_literalPlus == LiteralPlus::Plus || (m_literalPlus == LiteralPlus::Minus && part.text.size() <= 4096)) { 0840 buf.append('{'); 0841 buf.append(QByteArray::number(part.text.size())); 0842 buf.append("+}\r\n"); 0843 buf.append(part.text); 0844 } else if (part.numberSent) { 0845 buf.append(part.text); 0846 } else { 0847 buf.append('{'); 0848 buf.append(QByteArray::number(part.text.size())); 0849 buf.append("}\r\n"); 0850 #ifdef PRINT_TRAFFIC_TX 0851 if (printThisCommand) 0852 qDebug() << m_parserId << ">>>" << buf.left(PRINT_TRAFFIC_TX).trimmed(); 0853 else 0854 qDebug() << m_parserId << ">>> [sensitive command] -- added literal"; 0855 #endif 0856 socket->write(buf); 0857 part.numberSent = true; 0858 waitingForContinuation = true; 0859 Q_ASSERT(literalCommandTag.isEmpty()); 0860 literalCommandTag = cmd.cmds.first().text; 0861 Q_ASSERT(!literalCommandTag.isEmpty()); 0862 emit lineSent(this, sensitiveCommand ? privateMessage : buf); 0863 return; // and wait for continuation request 0864 } 0865 break; 0866 case Commands::IDLE_DONE: 0867 Q_ASSERT(false); // is handled above 0868 break; 0869 case Commands::IDLE: 0870 buf.append("IDLE\r\n"); 0871 #ifdef PRINT_TRAFFIC_TX 0872 qDebug() << m_parserId << ">>>" << buf.left(PRINT_TRAFFIC_TX).trimmed(); 0873 #endif 0874 socket->write(buf); 0875 idling = true; 0876 waitForInitialIdle = true; 0877 cmdQueue.pop_front(); 0878 emit lineSent(this, buf); 0879 return; 0880 break; 0881 case Commands::STARTTLS: 0882 startTlsCommand = buf; 0883 buf.append("STARTTLS\r\n"); 0884 #ifdef PRINT_TRAFFIC_TX 0885 qDebug() << m_parserId << ">>>" << buf.left(PRINT_TRAFFIC_TX).trimmed(); 0886 #endif 0887 socket->write(buf); 0888 startTlsInProgress = true; 0889 emit lineSent(this, buf); 0890 return; 0891 break; 0892 case Commands::COMPRESS_DEFLATE: 0893 compressDeflateCommand = buf; 0894 buf.append("COMPRESS DEFLATE\r\n"); 0895 #ifdef PRINT_TRAFFIC_TX 0896 qDebug() << m_parserId << ">>>" << buf.left(PRINT_TRAFFIC_TX).trimmed(); 0897 #endif 0898 socket->write(buf); 0899 compressDeflateInProgress = true; 0900 cmdQueue.pop_front(); 0901 emit lineSent(this, buf); 0902 return; 0903 break; 0904 } 0905 if (cmd.currentPart == cmd.cmds.size() - 1) { 0906 // finalize 0907 buf.append("\r\n"); 0908 #ifdef PRINT_TRAFFIC_TX 0909 if (printThisCommand) 0910 qDebug() << m_parserId << ">>>" << buf.left(PRINT_TRAFFIC_TX).trimmed(); 0911 else 0912 qDebug() << m_parserId << ">>> [sensitive command]"; 0913 #endif 0914 socket->write(buf); 0915 cmdQueue.pop_front(); 0916 emit lineSent(this, sensitiveCommand ? privateMessage : buf); 0917 break; 0918 } else { 0919 if (part.kind == Commands::ATOM_NO_SPACE_AROUND || cmd.cmds[cmd.currentPart + 1].kind == Commands::ATOM_NO_SPACE_AROUND) { 0920 // Skip the extra space if asked to do so 0921 } else { 0922 buf.append(' '); 0923 } 0924 ++cmd.currentPart; 0925 } 0926 } 0927 } 0928 0929 /** @short Process a line from IMAP server */ 0930 void Parser::processLine(QByteArray line) 0931 { 0932 #ifdef PRINT_TRAFFIC_RX 0933 QByteArray debugLine = line.trimmed(); 0934 if (debugLine.size() > PRINT_TRAFFIC_RX) 0935 qDebug() << m_parserId << "<<<" << debugLine.left(PRINT_TRAFFIC_RX) << "..."; 0936 else 0937 qDebug() << m_parserId << "<<<" << debugLine; 0938 #endif 0939 emit lineReceived(this, line); 0940 if (m_expectsInitialGreeting && !line.startsWith("* ")) { 0941 throw NotAnImapServerError(std::string(), line, -1); 0942 } else if (line.startsWith("* ")) { 0943 m_expectsInitialGreeting = false; 0944 queueResponse(parseUntagged(line)); 0945 } else if (line.startsWith("+ ")) { 0946 if (waitingForContinuation) { 0947 waitingForContinuation = false; 0948 literalCommandTag.clear(); 0949 QTimer::singleShot(0, this, SLOT(executeCommands())); 0950 } else if (waitForInitialIdle) { 0951 waitForInitialIdle = false; 0952 QTimer::singleShot(0, this, SLOT(executeCommands())); 0953 } else { 0954 throw ContinuationRequest(line.constData()); 0955 } 0956 } else { 0957 queueResponse(parseTagged(line)); 0958 } 0959 } 0960 0961 QSharedPointer<Responses::AbstractResponse> Parser::parseUntagged(const QByteArray &line) 0962 { 0963 int pos = 2; 0964 LowLevelParser::eatSpaces(line, pos); 0965 uint number; 0966 try { 0967 number = LowLevelParser::getUInt(line, pos); 0968 ++pos; 0969 } catch (ParseError &) { 0970 return parseUntaggedText(line, pos); 0971 } 0972 return parseUntaggedNumber(line, pos, number); 0973 } 0974 0975 QSharedPointer<Responses::AbstractResponse> Parser::parseUntaggedNumber( 0976 const QByteArray &line, int &start, const uint number) 0977 { 0978 if (start == line.size()) 0979 // number and nothing else 0980 throw NoData(line, start); 0981 0982 QByteArray kindStr = LowLevelParser::getAtom(line, start); 0983 Responses::Kind kind; 0984 try { 0985 kind = Responses::kindFromString(kindStr); 0986 } catch (UnrecognizedResponseKind &e) { 0987 throw UnrecognizedResponseKind(e.what(), line, start); 0988 } 0989 0990 switch (kind) { 0991 case Responses::EXISTS: 0992 case Responses::RECENT: 0993 case Responses::EXPUNGE: 0994 // no more data should follow 0995 if (start >= line.size()) 0996 throw TooMuchData(line, start); 0997 else if (line.mid(start) != QByteArray("\r\n")) 0998 throw UnexpectedHere(line, start); // expected CRLF 0999 else 1000 try { 1001 return QSharedPointer<Responses::AbstractResponse>( 1002 new Responses::NumberResponse(kind, number)); 1003 } catch (UnexpectedHere &e) { 1004 throw UnexpectedHere(e.what(), line, start); 1005 } 1006 break; 1007 1008 case Responses::FETCH: 1009 return QSharedPointer<Responses::AbstractResponse>( 1010 new Responses::Fetch(number, line, start)); 1011 break; 1012 1013 default: 1014 break; 1015 } 1016 throw UnexpectedHere(line, start); 1017 } 1018 1019 QSharedPointer<Responses::AbstractResponse> Parser::parseUntaggedText( 1020 const QByteArray &line, int &start) 1021 { 1022 Responses::Kind kind; 1023 try { 1024 kind = Responses::kindFromString(LowLevelParser::getAtom(line, start)); 1025 } catch (UnrecognizedResponseKind &e) { 1026 throw UnrecognizedResponseKind(e.what(), line, start); 1027 } 1028 ++start; 1029 if (start == line.size() && kind != Responses::SEARCH && kind != Responses::SORT) 1030 throw NoData(line, start); 1031 switch (kind) { 1032 case Responses::CAPABILITY: 1033 { 1034 QStringList capabilities; 1035 QList<QByteArray> list = line.mid(start).split(' '); 1036 for (QList<QByteArray>::const_iterator it = list.constBegin(); it != list.constEnd(); ++it) { 1037 QByteArray str = *it; 1038 if (str.endsWith("\r\n")) 1039 str.chop(2); 1040 capabilities << QString::fromUtf8(str); 1041 } 1042 if (!capabilities.count()) 1043 throw NoData(line, start); 1044 return QSharedPointer<Responses::AbstractResponse>( 1045 new Responses::Capability(capabilities)); 1046 } 1047 case Responses::OK: 1048 case Responses::NO: 1049 case Responses::BAD: 1050 case Responses::PREAUTH: 1051 case Responses::BYE: 1052 return QSharedPointer<Responses::AbstractResponse>( 1053 new Responses::State(QByteArray(), kind, line, start)); 1054 case Responses::LIST: 1055 case Responses::LSUB: 1056 return QSharedPointer<Responses::AbstractResponse>( 1057 new Responses::List(kind, line, start)); 1058 case Responses::FLAGS: 1059 return QSharedPointer<Responses::AbstractResponse>( 1060 new Responses::Flags(line, start)); 1061 case Responses::SEARCH: 1062 return QSharedPointer<Responses::AbstractResponse>( 1063 new Responses::Search(line, start)); 1064 case Responses::ESEARCH: 1065 return QSharedPointer<Responses::AbstractResponse>( 1066 new Responses::ESearch(line, start)); 1067 case Responses::STATUS: 1068 return QSharedPointer<Responses::AbstractResponse>( 1069 new Responses::Status(line, start)); 1070 case Responses::NAMESPACE: 1071 return QSharedPointer<Responses::AbstractResponse>( 1072 new Responses::Namespace(line, start)); 1073 case Responses::SORT: 1074 return QSharedPointer<Responses::AbstractResponse>( 1075 new Responses::Sort(line, start)); 1076 case Responses::THREAD: 1077 return QSharedPointer<Responses::AbstractResponse>( 1078 new Responses::Thread(line, start)); 1079 case Responses::ID: 1080 return QSharedPointer<Responses::AbstractResponse>( 1081 new Responses::Id(line, start)); 1082 case Responses::ENABLED: 1083 return QSharedPointer<Responses::AbstractResponse>( 1084 new Responses::Enabled(line, start)); 1085 case Responses::VANISHED: 1086 return QSharedPointer<Responses::AbstractResponse>( 1087 new Responses::Vanished(line, start)); 1088 case Responses::GENURLAUTH: 1089 return QSharedPointer<Responses::AbstractResponse>( 1090 new Responses::GenUrlAuth(line, start)); 1091 1092 1093 // Those already handled above follow here 1094 case Responses::EXPUNGE: 1095 case Responses::FETCH: 1096 case Responses::EXISTS: 1097 case Responses::RECENT: 1098 throw UnexpectedHere("Malformed response: the number should go first", line, start); 1099 } 1100 throw UnexpectedHere(line, start); 1101 } 1102 1103 QSharedPointer<Responses::AbstractResponse> Parser::parseTagged(const QByteArray &line) 1104 { 1105 int pos = 0; 1106 const QByteArray tag = LowLevelParser::getAtom(line, pos); 1107 ++pos; 1108 const Responses::Kind kind = Responses::kindFromString(LowLevelParser::getAtom(line, pos)); 1109 ++pos; 1110 1111 if (compressDeflateInProgress && compressDeflateCommand == tag + ' ') { 1112 switch (kind) { 1113 case Responses::OK: 1114 socket->startDeflate(); 1115 compressDeflateInProgress = false; 1116 compressDeflateCommand.clear(); 1117 break; 1118 default: 1119 // do nothing 1120 break; 1121 } 1122 compressDeflateInProgress = false; 1123 compressDeflateCommand.clear(); 1124 QTimer::singleShot(0, this, SLOT(handleCompressionPossibleActivated())); 1125 } 1126 1127 return QSharedPointer<Responses::AbstractResponse>( 1128 new Responses::State(tag, kind, line, pos)); 1129 } 1130 1131 void Parser::enableLiteralPlus(const LiteralPlus mode) 1132 { 1133 m_literalPlus = mode; 1134 } 1135 1136 void Parser::handleDisconnected(const QString &reason) 1137 { 1138 emit lineReceived(this, "*** Socket disconnected: " + reason.toUtf8()); 1139 #ifdef PRINT_TRAFFIC_TX 1140 qDebug() << m_parserId << "*** Socket disconnected"; 1141 #endif 1142 queueResponse(QSharedPointer<Responses::AbstractResponse>(new Responses::SocketDisconnectedResponse(reason))); 1143 } 1144 1145 Parser::~Parser() 1146 { 1147 // We want to prevent nasty signals from the underlying socket from 1148 // interfering with this object -- some of our local data might have 1149 // been already destroyed! 1150 socket->disconnect(this); 1151 socket->close(); 1152 } 1153 1154 uint Parser::parserId() const 1155 { 1156 return m_parserId; 1157 } 1158 1159 void Parser::slotSocketStateChanged(const Imap::ConnectionState connState, const QString &message) 1160 { 1161 if (connState == CONN_STATE_CONNECTED_PRETLS_PRECAPS) { 1162 #ifdef PRINT_TRAFFIC_TX 1163 qDebug() << m_parserId << "*** Connection established"; 1164 #endif 1165 emit lineReceived(this, "*** Connection established"); 1166 waitingForConnection = false; 1167 QTimer::singleShot(0, this, SLOT(executeCommands())); 1168 } else if (connState == CONN_STATE_AUTHENTICATED) { 1169 // unit tests: don't wait for the initial untagged response greetings 1170 m_expectsInitialGreeting = false; 1171 } 1172 emit lineReceived(this, "*** " + message.toUtf8()); 1173 emit connectionStateChanged(this, connState); 1174 } 1175 1176 }