26#include "imapstreamparser.h"
31using namespace KIMAP2;
35 m_isServerModeEnabled(serverModeEnabled),
41 m_currentState(InitState),
44 m_readingLiteral(false),
48 m_data1.
resize(m_bufferSize);
49 m_data2.
resize(m_bufferSize);
59const QByteArray &ImapStreamParser::buffer()
const
64char ImapStreamParser::at(
int pos)
const
79int ImapStreamParser::length()
const
81 return m_readPosition;
84int ImapStreamParser::readFromSocket()
86 if (m_readingLiteral && !m_isServerModeEnabled) {
87 Q_ASSERT(m_currentState == LiteralStringState);
88 Q_ASSERT(m_literalSize > 0);
89 const auto amountToRead = qMin(m_socket->
bytesAvailable(), m_literalSize);
90 Q_ASSERT(amountToRead > 0);
91 auto pos = m_literalData.
size();
92 m_literalData.
resize(m_literalData.
size() + amountToRead);
93 const auto readBytes = m_socket->
read(m_literalData.
data() + pos, amountToRead);
95 qWarning() <<
"Failed to read data";
99 m_literalSize -= readBytes;
100 Q_ASSERT(m_literalSize >= 0);
103 if (m_readPosition == m_bufferSize) {
107 const auto amountToRead = qMin(m_socket->
bytesAvailable(), qint64(m_bufferSize - m_readPosition));
108 Q_ASSERT(amountToRead > 0);
109 const auto readBytes = m_socket->
read(buffer().data() + m_readPosition, amountToRead);
111 qWarning() <<
"Failed to read data";
114 m_readPosition += readBytes;
121void ImapStreamParser::setupCallbacks()
123 onString([&](
const char *data,
const int size) {
126 m_message.
reset(
new Message);
127 m_currentPayload = &m_message->content;
132 *m_currentPayload << Message::Part(
QByteArray(data, size));
138 if (m_listCounter > 1) {
140 setState(SublistString);
141 m_stringStartPos = m_position;
150 if (m_listCounter <= 0) {
151 qWarning() <<
"Brackets are off";
156 if (m_listCounter == 0) {
157 Q_ASSERT(m_currentPayload);
159 *m_currentPayload << Message::Part(*m_list);
165 onResponseCodeStart([&]() {
166 m_currentPayload = &m_message->responseCode;
169 onResponseCodeEnd([&]() {
170 m_currentPayload = &m_message->content;
173 onLiteralStart([&](
const int size) {
174 m_literalData.
clear();
178 onLiteralPart([&](
const char *data,
const int size) {
187 if (m_list || m_listCounter != 0) {
188 qWarning() <<
"List parsing in progress: " << m_listCounter;
191 if (m_literalSize || m_readingLiteral) {
192 qWarning() <<
"Literal parsing in progress: " << m_literalSize;
195 Q_ASSERT(responseReceived);
197 responseReceived(*m_message);
198 m_message.
reset(
nullptr);
200 m_currentPayload =
nullptr;
204void ImapStreamParser::setState(States state)
206 m_lastState = m_currentState;
207 m_currentState = state;
210void ImapStreamParser::forwardToState(States state)
212 m_currentState = state;
215void ImapStreamParser::resetState()
217 m_currentState = m_lastState;
220void ImapStreamParser::processBuffer()
223 qWarning() <<
"An error occurred";
226 if (m_currentState == LiteralStringState && m_literalSize == 0 && m_readingLiteral) {
229 m_readingLiteral =
false;
232 while (m_position < m_readPosition) {
233 Q_ASSERT(m_position < length());
234 const char c = buffer()[m_position];
236 switch (m_currentState) {
240 }
else if (c ==
')') {
242 }
else if (c ==
'[') {
243 if (m_listCounter >= 1) {
245 setState(AngleBracketStringState);
246 m_stringStartPos = m_position;
250 }
else if (c ==
']') {
252 }
else if (c ==
' ') {
254 setState(WhitespaceState);
255 }
else if (c ==
'\r') {
257 }
else if (c ==
'{') {
258 setState(LiteralStringState);
259 m_stringStartPos = m_position + 1;
260 }
else if (c ==
'\"') {
261 setState(QuotedStringState);
262 m_stringStartPos = m_position + 1;
264 setState(StringState);
265 m_stringStartPos = m_position;
268 case QuotedStringState:
269 if (c ==
'\"' && buffer().at(m_position - 1) !=
'\\') {
272 const auto endPos = m_position;
273 string(buffer().constData() + m_stringStartPos, endPos - m_stringStartPos);
274 m_stringStartPos = 0;
277 case LiteralStringState:
279 m_literalSize = strtol(buffer().constData() + m_stringStartPos,
nullptr, 10);
281 literalStart(m_literalSize);
282 m_readingLiteral =
false;
283 m_stringStartPos = 0;
286 if (!m_readingLiteral) {
289 m_readingLiteral =
true;
290 if (m_isServerModeEnabled && m_literalSize > 0) {
291 sendContinuationResponse(m_literalSize);
295 Q_ASSERT(m_position < length());
297 int size = m_literalSize;
298 if (length() < m_position + size) {
300 size = length() - m_position;
302 literalPart(buffer().constData() + m_position, size);
304 m_literalSize -= size;
306 if (m_literalSize <= 0) {
307 Q_ASSERT(m_literalSize == 0);
310 m_readingLiteral =
false;
325 string(buffer().constData() + m_stringStartPos, m_position - m_stringStartPos);
326 m_stringStartPos = 0;
331 if (m_listCounter >= 1) {
333 forwardToState(AngleBracketStringState);
338 case AngleBracketStringState:
341 string(buffer().constData() + m_stringStartPos, m_position - m_stringStartPos + 1);
342 m_stringStartPos = 0;
348 }
else if (c ==
')') {
350 if (m_listCounter <= 1) {
352 string(buffer().constData() + m_stringStartPos, m_position - m_stringStartPos + 1);
353 m_stringStartPos = 0;
357 case WhitespaceState:
379void ImapStreamParser::parseStream()
385 qWarning() <<
"An error occurred";
390 if (readFromSocket() <= 0) {
394 qWarning() <<
"Read nothing from the socket.";
401 m_processing =
false;
404void ImapStreamParser::trimBuffer()
406 int offset = m_position;
407 if (m_stringStartPos) {
408 offset = qMin(m_stringStartPos, m_position);
411 auto remainderSize = m_readPosition - offset;
412 Q_ASSERT( remainderSize >= 0);
414 if (m_current == &m_data1) {
415 otherBuffer = &m_data2;
417 otherBuffer = &m_data1;
420 otherBuffer->
replace(0, remainderSize, buffer().constData() + offset, remainderSize);
422 m_current = otherBuffer;
423 m_readPosition = remainderSize;
424 m_position -= offset;
425 if (m_stringStartPos) {
426 m_stringStartPos -= offset;
431int ImapStreamParser::availableDataSize()
const
439 auto startPos = m_position;
440 onLineEnd([&result,
this, startPos]() {
441 result = mid(startPos, m_position - startPos - 1);
446 qWarning() <<
"No data available";
451 if (!result.
isEmpty() && m_currentState == InitState) {
457 qDebug() <<
"Read until command end: " << result;
461void ImapStreamParser::sendContinuationResponse(qint64 size)
463 QByteArray block =
"+ Ready for literal data (expecting " +
465 m_socket->
write(block);
469void ImapStreamParser::onResponseReceived(std::function<
void(
const Message &)> f)
471 responseReceived = f;
474bool ImapStreamParser::error()
const
479QByteArray ImapStreamParser::currentBuffer()
const
481 return mid(0, m_readPosition);
ImapStreamParser(QIODevice *socket, bool serverModeEnabled=false)
Construct the parser.
QByteArray readUntilCommandEnd()
Return everything that remained from the command.
Q_SCRIPTABLE Q_NOREPLY void start()
QByteArray & append(QByteArrayView data)
const char * constData() const const
QByteArray fromRawData(const char *data, qsizetype size)
bool isEmpty() const const
QByteArray mid(qsizetype pos, qsizetype len) const const
QByteArray number(double n, char format, int precision)
QByteArray & replace(QByteArrayView before, QByteArrayView after)
void reserve(qsizetype size)
void resize(qsizetype newSize, char c)
qsizetype size() const const
virtual qint64 bytesAvailable() const const
QByteArray read(qint64 maxSize)
virtual bool waitForBytesWritten(int msecs)
virtual bool waitForReadyRead(int msecs)
qint64 write(const QByteArray &data)