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
69QByteArray ImapStreamParser::mid(
int start,
int len)
const
71 return buffer().mid(
start, len);
74QByteArray ImapStreamParser::midRef(
int start,
int len)
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;
130 *m_list << QByteArray(data, size);
132 *m_currentPayload << Message::Part(QByteArray(data, size));
138 if (m_listCounter > 1) {
140 setState(SublistString);
141 m_stringStartPos = m_position;
144 m_list =
new QList<QByteArray>;
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();
175 m_literalData.reserve(size);
178 onLiteralPart([&](
const char *data,
const int size) {
183 string(m_literalData.constData(), m_literalData.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";
389 while (m_socket->bytesAvailable()) {
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);
413 QByteArray *otherBuffer;
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
433 return m_socket->bytesAvailable() + length() - m_position;
439 auto startPos = m_position;
440 onLineEnd([&result,
this, startPos]() {
441 result = mid(startPos, m_position - startPos - 1);
444 if (!m_socket->bytesAvailable()) {
445 if (!m_socket->waitForReadyRead(10000)) {
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()
const char * constData() const const
QByteArray fromRawData(const char *data, qsizetype size)
bool isEmpty() const const
QByteArray number(double n, char format, int precision)
QByteArray & replace(QByteArrayView before, QByteArrayView after)
virtual bool waitForBytesWritten(int msecs)
qint64 write(const QByteArray &data)