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 #ifndef IMAP_PARSER_H 0023 #define IMAP_PARSER_H 0024 #include <QLinkedList> 0025 #include <QSharedPointer> 0026 #include "Command.h" 0027 #include "Response.h" 0028 #include "Sequence.h" 0029 #include "../ConnectionState.h" 0030 #include "../Exceptions.h" 0031 #include "Imap/Model/CatenateData.h" 0032 #include "Imap/Model/UidSubmitData.h" 0033 0034 /** 0035 * @file 0036 * A header file defining Parser class and various helpers. 0037 * 0038 * @author Jan Kundrát <jkt@flaska.net> 0039 */ 0040 0041 class ImapParserParseTest; 0042 0043 namespace Streams { 0044 class Socket; 0045 } 0046 0047 /** @short Namespace for IMAP interaction */ 0048 namespace Imap 0049 { 0050 0051 /** @short A handle identifying a command sent to the server */ 0052 typedef QByteArray CommandHandle; 0053 0054 // this is required for clang 3.0 0055 typedef QMap<QByteArray, quint64> MapByteArrayUint64; 0056 0057 /** @short Class that does all IMAP parsing */ 0058 class Parser : public QObject 0059 { 0060 Q_OBJECT 0061 0062 friend class ::ImapParserParseTest; 0063 0064 public: 0065 /** @short Constructor. 0066 * 0067 * Takes an QIODevice instance as a parameter. */ 0068 Parser(QObject *parent, Streams::Socket *socket, const uint myId); 0069 0070 ~Parser(); 0071 0072 /** @short Checks for waiting responses */ 0073 bool hasResponse() const; 0074 0075 /** @short De-queue and return parsed response */ 0076 QSharedPointer<Responses::AbstractResponse> getResponse(); 0077 0078 /** @short Support of the LITERAL+ and LITERAL- extensions, RFC 7888 and RFC 2088 */ 0079 enum class LiteralPlus { 0080 Unsupported, /**< @short No joy, use synchronizing literals */ 0081 Plus, /**< @short Unlimited LITERAL+ regardless of the size */ 0082 Minus, /**< @short Can use non-synchronizing literals if the size is <= 4kB */ 0083 }; 0084 0085 /** @short Enable/Disable sending literals using the LITERAL+ extension */ 0086 void enableLiteralPlus(const LiteralPlus mode); 0087 0088 uint parserId() const; 0089 0090 public slots: 0091 0092 /** @short CAPABILITY, RFC 3501 section 6.1.1 */ 0093 CommandHandle capability(); 0094 0095 /** @short NOOP, RFC 3501 section 6.1.2 */ 0096 CommandHandle noop(); 0097 0098 /** @short LOGOUT, RFC3501 section 6.1.3 */ 0099 CommandHandle logout(); 0100 0101 0102 /** @short STARTTLS, RFC3051 section 6.2.1 */ 0103 CommandHandle startTls(); 0104 0105 #if 0 0106 /** @short AUTHENTICATE, RFC3501 section 6.2.2 */ 0107 CommandHandle authenticate(/* FIXME: parameter */); 0108 #endif 0109 0110 /** @short LOGIN, RFC3501 section 6.2.3 */ 0111 CommandHandle login(const QString &user, const QString &pass); 0112 0113 0114 /** @short SELECT, RFC3501 section 6.3.1 */ 0115 CommandHandle select(const QString &mailbox, const QList<QByteArray> ¶ms = QList<QByteArray>()); 0116 0117 /** @short SELECT extended according to RFC 5162 section 3.1 */ 0118 CommandHandle selectQresync(const QString &mailbox, const uint uidValidity, 0119 const quint64 highestModSeq, const Sequence &knownUids = Sequence(), 0120 const Sequence &sequenceSnapshot = Sequence(), const Sequence &uidSnapshot = Sequence()); 0121 0122 /** @short EXAMINE, RFC3501 section 6.3.2 */ 0123 CommandHandle examine(const QString &mailbox, const QList<QByteArray> ¶ms = QList<QByteArray>()); 0124 0125 /** @short CREATE, RFC3501 section 6.3.3 */ 0126 CommandHandle create(const QString &mailbox); 0127 0128 /** @short DELETE, RFC3501 section 6.3.4 */ 0129 CommandHandle deleteMailbox(const QString &mailbox); 0130 0131 /** @short RENAME, RFC3501 section 6.3.5 */ 0132 CommandHandle rename(const QString &oldName, const QString &newName); 0133 0134 /** @short SUBSCRIBE, RFC3501 section 6.3.6 */ 0135 CommandHandle subscribe(const QString &mailbox); 0136 0137 /** @short UNSUBSCRIBE, RFC3501 section 6.3.7 */ 0138 CommandHandle unSubscribe(const QString &mailbox); 0139 0140 /** @short LIST, RFC3501 section 6.3.8, as extended by RFC5258 */ 0141 CommandHandle list(const QString &reference, const QString &mailbox, const QStringList &returnOptions = QStringList()); 0142 0143 /** @short LSUB, RFC3501 section 6.3.9 */ 0144 CommandHandle lSub(const QString &reference, const QString &mailbox); 0145 0146 /** @short STATUS, RFC3501 section 6.3.10 */ 0147 CommandHandle status(const QString &mailbox, const QStringList &fields); 0148 0149 /** @short APPEND, RFC3501 section 6.3.11 */ 0150 CommandHandle append(const QString &mailbox, const QByteArray &message, 0151 const QStringList &flags = QStringList(), const QDateTime ×tamp = QDateTime()); 0152 0153 /** @short APPEND CATENATE, RFC 4469 */ 0154 CommandHandle appendCatenate(const QString &mailbox, const QList<Imap::Mailbox::CatenatePair> &data, 0155 const QStringList &flags = QStringList(), const QDateTime ×tamp = QDateTime()); 0156 0157 0158 /** @short CHECK, RFC3501 sect 6.4.1 */ 0159 CommandHandle check(); 0160 0161 /** @short CLOSE, RFC3501 sect 6.4.2 */ 0162 CommandHandle close(); 0163 0164 /** @short EXPUNGE, RFC3501 sect 6.4.3 */ 0165 CommandHandle expunge(); 0166 0167 /** @short SEARCH, RFC3501 sect 6.4.4 */ 0168 CommandHandle search(const QStringList &criteria, const QByteArray &charset = QByteArray()) { 0169 return searchHelper("SEARCH", criteria, charset); 0170 }; 0171 0172 /** @short FETCH, RFC3501 sect 6.4.5 */ 0173 CommandHandle fetch(const Sequence &seq, const QStringList &items, 0174 const QMap<QByteArray, quint64> &uint64Modifiers = MapByteArrayUint64()); 0175 0176 /** @short STORE, RFC3501 sect 6.4.6 */ 0177 CommandHandle store(const Sequence &seq, const QString &item, const QString &value); 0178 0179 /** @short COPY, RFC3501 sect 6.4.7 */ 0180 CommandHandle copy(const Sequence &seq, const QString &mailbox); 0181 0182 /** @short UID command (FETCH), RFC3501 sect 6.4.8 */ 0183 CommandHandle uidFetch(const Sequence &seq, const QList<QByteArray> &items); 0184 0185 /** @short UID command (STORE), RFC3501 sect 6.4.8 */ 0186 CommandHandle uidStore(const Sequence &seq, const QString &item, const QString &value); 0187 0188 /** @short UID command (COPY), RFC3501 sect 6.4.8 */ 0189 CommandHandle uidCopy(const Sequence &seq, const QString &mailbox); 0190 0191 /** @short UID XMOVE, draft-gulbrandsen-imap-move-01 as implemented by fastmail.fm */ 0192 CommandHandle uidMove(const Sequence &seq, const QString &mailbox); 0193 0194 /** @short UID EXPUNGE from the UIDPLUS extension, RFC 2359 section 4.1 */ 0195 CommandHandle uidExpunge(const Sequence &seq); 0196 0197 /** @short UID command (SEARCH), RFC3501 sect 6.4.8 */ 0198 CommandHandle uidSearch(const QStringList &criteria, const QByteArray &charset = QByteArray()) { 0199 return searchHelper("UID SEARCH", criteria, charset); 0200 } 0201 0202 /** @short A special case of the "UID SEARCH UID" command */ 0203 CommandHandle uidSearchUid(const QByteArray &sequence); 0204 0205 /** @short Perform the UID ESEARCH command with the specified UID set */ 0206 CommandHandle uidESearchUid(const QByteArray &sequence); 0207 0208 0209 /** @short X<atom>, RFC3501 sect 6.5.1 */ 0210 CommandHandle xAtom(const Commands::Command &commands); 0211 0212 0213 /** @short UNSELECT, RFC3691 */ 0214 CommandHandle unSelect(); 0215 0216 /** @short IDLE, RFC2177 0217 0218 The IDLE command has to be explicitly terminated by calling idleDone(). 0219 */ 0220 CommandHandle idle(); 0221 0222 /** @short The DONE for terminating the IDLE state, RFC2177 */ 0223 void idleDone(); 0224 0225 /** @short Don't wait for the initial continuation prompt, it won't come */ 0226 void idleContinuationWontCome(); 0227 0228 /** @short The IDLE command got terminated by the server after it sent the continuation request but before we got a chance to break it */ 0229 void idleMagicallyTerminatedByServer(); 0230 0231 0232 /** @short NAMESPACE, RFC 2342 */ 0233 CommandHandle namespaceCommand(); 0234 0235 /** SORT, RFC5256 */ 0236 CommandHandle sort(const QStringList &sortCriteria, const QByteArray &charset, const QStringList &searchCriteria); 0237 /** UID SORT, RFC5256 */ 0238 CommandHandle uidSort(const QStringList &sortCriteria, const QByteArray &charset, const QStringList &searchCriteria); 0239 /** THREAD, RFC5256 */ 0240 CommandHandle thread(const QByteArray &algo, const QByteArray &charset, const QStringList &searchCriteria); 0241 /** UID THREAD, RFC5256 */ 0242 CommandHandle uidThread(const QByteArray &algo, const QByteArray &charset, const QStringList &searchCriteria); 0243 0244 /** @short ESORT, the extended UID SORT from RFC 5267, section 3.1 */ 0245 CommandHandle uidESort(const QStringList &sortCriteria, const QByteArray &charset, const QStringList &searchCriteria, 0246 const QStringList &returnOptions); 0247 0248 /** @short ESEARCH, the extended UID SEARCH with support for ESEARCH return options from RFC 5267 */ 0249 CommandHandle uidESearch(const QByteArray &charset, const QStringList &searchCriteria, const QStringList &returnOptions); 0250 0251 0252 CommandHandle uidEThread(const QByteArray &algo, const QByteArray &charset, const QStringList &searchCriteria, 0253 const QStringList &returnOptions); 0254 0255 /** @short CANCELUPDATE, tell the server that it shall stop sending any ESEARCH responses associated with the given tag */ 0256 CommandHandle cancelUpdate(const CommandHandle &tag); 0257 0258 /** @short ID, RFC 2971 section 3.1 0259 0260 This variant will send the ID NIL command. 0261 */ 0262 CommandHandle idCommand(); 0263 0264 /** @short ID, RFC 2971 section 3.1 0265 0266 This variant of the idCommand() sends an arbitrary list of arguments to the server. 0267 */ 0268 CommandHandle idCommand(const QMap<QByteArray,QByteArray> &args); 0269 0270 /** @short ENABLE command, RFC 6151 */ 0271 CommandHandle enable(const QList<QByteArray> &extensions); 0272 0273 /** @short COMPRESS DEFLATE, RFC 4978 */ 0274 CommandHandle compressDeflate(); 0275 0276 /** @short GENURLAUTH, RFC 4467 */ 0277 CommandHandle genUrlAuth(const QByteArray &url, const QByteArray mechanism); 0278 0279 /** @short UID SENDMAIL, jkt's draft-imap-sendmail */ 0280 CommandHandle uidSendmail(const uint uid, const Mailbox::UidSubmitOptionsList &submissionOptions); 0281 0282 void slotSocketStateChanged(const Imap::ConnectionState connState, const QString &message); 0283 0284 void unfreezeAfterEncryption(); 0285 0286 void closeConnection(); 0287 0288 0289 signals: 0290 /** @short New response received */ 0291 void responseReceived(Imap::Parser *parser); 0292 0293 /** @short A full line was received from the remote IMAP server 0294 0295 This signal is emitted when a full line, including all embedded literals, have 0296 been received from the remote IMAP server, but before it was attempted to parse 0297 it. However, 0298 */ 0299 void lineReceived(Imap::Parser *parser, const QByteArray &line); 0300 0301 /** @short A full line has been sent to the remote IMAP server */ 0302 void lineSent(Imap::Parser *parser, const QByteArray &line); 0303 0304 /** @short There's been a non-fatal error when parsing given line 0305 0306 Detailed information is available in the @arg message with @arg line and @arg position 0307 containing the original line and indicating the troublesome position, or -1 if not applciable. 0308 */ 0309 void parserWarning(Imap::Parser *parser, const QString &message, const QByteArray *line, uint position); 0310 0311 void commandQueued(); 0312 0313 /** @short The socket's state has changed */ 0314 void connectionStateChanged(Imap::Parser *parser, Imap::ConnectionState); 0315 0316 private slots: 0317 void handleReadyRead(); 0318 void handleDisconnected(const QString &reason); 0319 void executeACommand(); 0320 void executeCommands(); 0321 void finishStartTls(); 0322 void handleSocketEncrypted(); 0323 void handleCompressionPossibleActivated(); 0324 0325 private: 0326 /** @short Private copy constructor */ 0327 Parser(const Parser &); 0328 /** @short Private assignment operator */ 0329 Parser &operator=(const Parser &); 0330 0331 /** @short Queue command for execution.*/ 0332 CommandHandle queueCommand(Commands::Command command); 0333 0334 /** @short Shortcut function; works exactly same as above mentioned queueCommand() */ 0335 CommandHandle queueCommand(const Commands::TokenType kind, const QByteArray &text) { 0336 return queueCommand(Commands::Command() << Commands::PartOfCommand(kind, text)); 0337 }; 0338 0339 /** @short Helper for handleReadyRead() -- actually read & parse the data */ 0340 void reallyReadLine(); 0341 0342 /** @short Helper for search() and uidSearch() */ 0343 CommandHandle searchHelper(const QByteArray &command, const QStringList &criteria, 0344 const QByteArray &charset = QByteArray()); 0345 0346 CommandHandle sortHelper(const QByteArray &command, const QStringList &sortCriteria, const QByteArray &charset, const QStringList &searchCriteria); 0347 CommandHandle threadHelper(const QByteArray &command, const QByteArray &algo, const QByteArray &charset, const QStringList &searchCriteria); 0348 0349 /** @short Generate tag for next command */ 0350 QByteArray generateTag(); 0351 0352 void processLine(QByteArray line); 0353 0354 /** @short Parse line for untagged reply */ 0355 QSharedPointer<Responses::AbstractResponse> parseUntagged(const QByteArray &line); 0356 0357 /** @short Parse line for tagged reply */ 0358 QSharedPointer<Responses::AbstractResponse> parseTagged(const QByteArray &line); 0359 0360 /** @short helper for parseUntagged() */ 0361 QSharedPointer<Responses::AbstractResponse> parseUntaggedNumber( 0362 const QByteArray &line, int &start, const uint number); 0363 0364 /** @short helper for parseUntagged() */ 0365 QSharedPointer<Responses::AbstractResponse> parseUntaggedText( 0366 const QByteArray &line, int &start); 0367 0368 /** @short Add parsed response to the internal queue, emit notification signal */ 0369 void queueResponse(const QSharedPointer<Responses::AbstractResponse> &resp); 0370 0371 /** @short Connection to the IMAP server */ 0372 Streams::Socket *socket; 0373 0374 /** @short Keeps track of the last-used command tag */ 0375 unsigned int m_lastTagUsed; 0376 0377 /** @short Queue storing commands that are about to be executed */ 0378 std::list<Commands::Command> cmdQueue; 0379 0380 /** @short Queue storing parsed replies from the IMAP server */ 0381 std::list<QSharedPointer<Responses::AbstractResponse> > respQueue; 0382 0383 bool idling; 0384 bool waitForInitialIdle; 0385 0386 LiteralPlus m_literalPlus; 0387 bool waitingForContinuation; 0388 bool startTlsInProgress; 0389 bool compressDeflateInProgress; 0390 bool waitingForConnection; 0391 bool waitingForEncryption; 0392 bool waitingForSslPolicy; 0393 bool m_expectsInitialGreeting; 0394 0395 enum { ReadingLine, ReadingNumberOfBytes } readingMode; 0396 QByteArray currentLine; 0397 int oldLiteralPosition; 0398 uint readingBytes; 0399 QByteArray startTlsCommand; 0400 QByteArray startTlsReply; 0401 QByteArray compressDeflateCommand; 0402 QByteArray literalCommandTag; 0403 0404 /** @short Unique-id for debugging purposes */ 0405 uint m_parserId; 0406 }; 0407 0408 QTextStream &operator<<(QTextStream &stream, const Sequence &s); 0409 0410 } 0411 #endif /* IMAP_PARSER_H */