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> &params = 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> &params = 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 &timestamp = 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 &timestamp = 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 */