File indexing completed on 2024-05-12 05:17:14

0001 /*
0002     SPDX-FileCopyrightText: 2006-2007 Volker Krause <vkrause@kde.org>
0003     SPDX-FileCopyrightText: 2009 Andras Mantia <amantia@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #pragma once
0009 
0010 #include "kimap_export.h"
0011 
0012 #include <exception>
0013 
0014 #include <QByteArray>
0015 #include <QList>
0016 #include <QString>
0017 
0018 class QIODevice;
0019 
0020 namespace KIMAP
0021 {
0022 class ImapParserException : public std::exception
0023 {
0024 public:
0025     explicit ImapParserException(const char *what) throw()
0026         : mWhat(what)
0027     {
0028     }
0029     explicit ImapParserException(const QByteArray &what) throw()
0030         : mWhat(what)
0031     {
0032     }
0033     explicit ImapParserException(const QString &what) throw()
0034         : mWhat(what.toUtf8())
0035     {
0036     }
0037     ImapParserException(const ImapParserException &other) throw()
0038         : std::exception(other)
0039         , mWhat(other.what())
0040     {
0041     }
0042     ~ImapParserException() throw() override
0043     {
0044     }
0045     const char *what() const throw() override
0046     {
0047         return mWhat.constData();
0048     }
0049     virtual const char *type() const throw()
0050     {
0051         return "ImapParserException";
0052     }
0053 
0054 private:
0055     QByteArray mWhat;
0056 };
0057 
0058 /**
0059   Parser for IMAP messages that operates on a local socket stream.
0060 */
0061 class KIMAP_EXPORT ImapStreamParser
0062 {
0063 public:
0064     /**
0065      * Construct the parser.
0066      * @param socket the local socket to work with.
0067      * @param serverModeEnabled true if the parser has to assume we're writing a server (e.g. sends
0068      * continuation message automatically)
0069      */
0070     explicit ImapStreamParser(QIODevice *socket, bool serverModeEnabled = false);
0071 
0072     /**
0073      * Get a string from the message. If the upcoming data is not a quoted string, unquoted string or a literal,
0074      * the behavior is undefined. Use @ref hasString to be sure a string comes. This call might block.
0075      * @return the next string from the message as an utf8 string
0076      */
0077     QString readUtf8String();
0078 
0079     /**
0080      * Same as above, but without decoding it to utf8.
0081      * @return the next string from the message
0082      */
0083     QByteArray readString();
0084 
0085     /**
0086      * Get he next parenthesized list. If the upcoming data is not a parenthesized list,
0087      * the behavior is undefined. Use @ref hasList to be sure a string comes. This call might block.
0088      * @return the next parenthesized list.
0089      */
0090     QList<QByteArray> readParenthesizedList();
0091 
0092     /**
0093      * Get the next data as a number. This call might block.
0094      * @param ok true if the data found was a number
0095      * @return the number
0096      */
0097     qint64 readNumber(bool *ok = nullptr);
0098 
0099     /**
0100      * Check if the next data is a string or not. This call might block.
0101      * @return true if a string follows
0102      */
0103     bool hasString();
0104 
0105     /**
0106      * Check if the next data is a literal data or not. If a literal is found, the
0107      * internal position pointer is set to the beginning of the literal data.
0108      * This call might block.
0109      * @return true if a literal follows
0110      */
0111     bool hasLiteral();
0112 
0113     /**
0114      * Read the next literal sequence. This might or might not be the full data. Example code to read a literal would be:
0115      * @code
0116      * ImapStreamParser parser;
0117      *  ...
0118      * if (parser.hasLiteral())
0119      * {
0120      *   while (!parser.atLiteralEnd())
0121      *   {
0122      *      QByteArray data = parser.readLiteralPart();
0123      *      // do something with the data
0124      *   }
0125      * }
0126      * @endcode
0127      *
0128      * This call might block.
0129      *
0130      * @return part of a literal data
0131      */
0132     QByteArray readLiteralPart();
0133 
0134     /**
0135      * Check if the literal data end was reached. See @ref hasLiteral and @ref readLiteralPart .
0136      * @return true if the literal was completely read.
0137      */
0138     bool atLiteralEnd() const;
0139 
0140     /**
0141      * Check if the next data is a parenthesized list. This call might block.
0142      * @return true if a parenthesized list comes.
0143      */
0144     bool hasList();
0145 
0146     /**
0147      * Check if the next data is a parenthesized list end. This call might block.
0148      * @return true if a parenthesized list end.
0149      */
0150     bool atListEnd();
0151 
0152     /**
0153      * Check if the next data is a response code. This call might block.
0154      * @return true if a response code comes.
0155      */
0156     bool hasResponseCode();
0157 
0158     /**
0159      * Check if the next data is a response code end. This call might block.
0160      * @return true if a response code end.
0161      */
0162     bool atResponseCodeEnd();
0163 
0164     /**
0165      * Check if the command end was reached
0166      * @return true if the end of command is reached
0167      */
0168     bool atCommandEnd();
0169 
0170     /**
0171      * Return everything that remained from the command.
0172      * @return the remaining command data
0173      */
0174     QByteArray readUntilCommandEnd();
0175 
0176     /**
0177      * Return all the data that was read from the socket, but not processed yet.
0178      * @return the remaining unprocessed data
0179      */
0180     QByteArray readRemainingData();
0181 
0182     int availableDataSize() const;
0183 
0184     void setData(const QByteArray &data);
0185 
0186 private:
0187     void stripLeadingSpaces();
0188     QByteArray parseQuotedString();
0189 
0190     /**
0191      * If the condition is true, wait for more data to be available from the socket.
0192      * If no data comes after a timeout (30000ms), it aborts and returns false.
0193      * @param wait the condition
0194      * @return true if more data is available
0195      */
0196     bool waitForMoreData(bool wait);
0197 
0198     /**
0199      * Inform the client to send more literal data.
0200      */
0201     void sendContinuationResponse(qint64 size);
0202 
0203     /**
0204      * Remove already read data from the internal buffer if necessary.
0205      */
0206     void trimBuffer();
0207 
0208     QIODevice *m_socket = nullptr;
0209     bool m_isServerModeEnabled = false;
0210     QByteArray m_data;
0211     int m_position = -1;
0212     qint64 m_literalSize = -1;
0213 };
0214 
0215 }