File indexing completed on 2024-05-12 05:17:23
0001 /* 0002 Copyright (c) 2006 - 2007 Volker Krause <vkrause@kde.org> 0003 Copyright (c) 2009 Andras Mantia <amantia@kde.org> 0004 Copyright (c) 2017 Christian Mollekopf <mollekopf@kolabsys.com> 0005 0006 Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com> 0007 Author: Kevin Ottens <kevin@kdab.com> 0008 Copyright (c) 2016 Christian Mollekopf <mollekopf@kolabsys.com> 0009 0010 This library is free software; you can redistribute it and/or modify it 0011 under the terms of the GNU Library General Public License as published by 0012 the Free Software Foundation; either version 2 of the License, or (at your 0013 option) any later version. 0014 0015 This library is distributed in the hope that it will be useful, but WITHOUT 0016 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 0017 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 0018 License for more details. 0019 0020 You should have received a copy of the GNU Library General Public License 0021 along with this library; see the file COPYING.LIB. If not, write to the 0022 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 0023 02110-1301, USA. 0024 */ 0025 0026 #include "imapstreamparser.h" 0027 0028 #include <QIODevice> 0029 #include <QDebug> 0030 0031 using namespace KIMAP2; 0032 0033 ImapStreamParser::ImapStreamParser(QIODevice *socket, bool serverModeEnabled) 0034 : m_socket(socket), 0035 m_isServerModeEnabled(serverModeEnabled), 0036 m_processing(false), 0037 m_position(0), 0038 m_readPosition(0), 0039 m_literalSize(0), 0040 m_bufferSize(16000), 0041 m_currentState(InitState), 0042 m_listCounter(0), 0043 m_stringStartPos(0), 0044 m_readingLiteral(false), 0045 m_error(false), 0046 m_list(nullptr) 0047 { 0048 m_data1.resize(m_bufferSize); 0049 m_data2.resize(m_bufferSize); 0050 m_current = &m_data1; 0051 setupCallbacks(); 0052 } 0053 0054 QByteArray &ImapStreamParser::buffer() 0055 { 0056 return *m_current; 0057 } 0058 0059 const QByteArray &ImapStreamParser::buffer() const 0060 { 0061 return *m_current; 0062 } 0063 0064 char ImapStreamParser::at(int pos) const 0065 { 0066 return m_current->constData()[pos]; 0067 } 0068 0069 QByteArray ImapStreamParser::mid(int start, int len) const 0070 { 0071 return buffer().mid(start, len); 0072 } 0073 0074 QByteArray ImapStreamParser::midRef(int start, int len) const 0075 { 0076 return QByteArray::fromRawData(buffer().constData() + start, len); 0077 } 0078 0079 int ImapStreamParser::length() const 0080 { 0081 return m_readPosition; 0082 } 0083 0084 int ImapStreamParser::readFromSocket() 0085 { 0086 if (m_readingLiteral && !m_isServerModeEnabled) { 0087 Q_ASSERT(m_currentState == LiteralStringState); 0088 Q_ASSERT(m_literalSize > 0); 0089 const auto amountToRead = qMin(m_socket->bytesAvailable(), m_literalSize); 0090 Q_ASSERT(amountToRead > 0); 0091 auto pos = m_literalData.size(); 0092 m_literalData.resize(m_literalData.size() + amountToRead); 0093 const auto readBytes = m_socket->read(m_literalData.data() + pos, amountToRead); 0094 if (readBytes < 0) { 0095 qWarning() << "Failed to read data"; 0096 return 0; 0097 } 0098 // qDebug() << "Read literal data: " << readBytes << m_literalSize; 0099 m_literalSize -= readBytes; 0100 Q_ASSERT(m_literalSize >= 0); 0101 return readBytes; 0102 } else { 0103 if (m_readPosition == m_bufferSize) { 0104 // qDebug() << "Buffer is full, trimming"; 0105 trimBuffer(); 0106 } 0107 const auto amountToRead = qMin(m_socket->bytesAvailable(), qint64(m_bufferSize - m_readPosition)); 0108 Q_ASSERT(amountToRead > 0); 0109 const auto readBytes = m_socket->read(buffer().data() + m_readPosition, amountToRead); 0110 if (readBytes < 0) { 0111 qWarning() << "Failed to read data"; 0112 return 0; 0113 } 0114 m_readPosition += readBytes; 0115 // qDebug() << "Buffer: " << buffer().mid(0, m_readPosition); 0116 // qDebug() << "Read data: " << readBytes; 0117 return readBytes; 0118 } 0119 } 0120 0121 void ImapStreamParser::setupCallbacks() 0122 { 0123 onString([&](const char *data, const int size) { 0124 if (!m_message) { 0125 //We just assume that we always get a string first 0126 m_message.reset(new Message); 0127 m_currentPayload = &m_message->content; 0128 } 0129 if (m_list) { 0130 *m_list << QByteArray(data, size); 0131 } else { 0132 *m_currentPayload << Message::Part(QByteArray(data, size)); 0133 } 0134 }); 0135 0136 onListStart([&]() { 0137 m_listCounter++; 0138 if (m_listCounter > 1) { 0139 //Parse sublists as string 0140 setState(SublistString); 0141 m_stringStartPos = m_position; 0142 } else { 0143 if (!m_list) { 0144 m_list = new QList<QByteArray>; 0145 } 0146 } 0147 }); 0148 0149 onListEnd([&]() { 0150 if (m_listCounter <= 0) { 0151 qWarning() << "Brackets are off"; 0152 m_error = true; 0153 return; 0154 } 0155 m_listCounter--; 0156 if (m_listCounter == 0) { 0157 Q_ASSERT(m_currentPayload); 0158 Q_ASSERT(m_list); 0159 *m_currentPayload << Message::Part(*m_list); 0160 delete m_list; 0161 m_list = nullptr; 0162 } 0163 }); 0164 0165 onResponseCodeStart([&]() { 0166 m_currentPayload = &m_message->responseCode; 0167 }); 0168 0169 onResponseCodeEnd([&]() { 0170 m_currentPayload = &m_message->content; 0171 }); 0172 0173 onLiteralStart([&](const int size) { 0174 m_literalData.clear(); 0175 m_literalData.reserve(size); 0176 }); 0177 0178 onLiteralPart([&](const char *data, const int size) { 0179 m_literalData.append(QByteArray::fromRawData(data, size)); 0180 }); 0181 0182 onLiteralEnd([&]() { 0183 string(m_literalData.constData(), m_literalData.size()); 0184 }); 0185 0186 onLineEnd([&]() { 0187 if (m_list || m_listCounter != 0) { 0188 qWarning() << "List parsing in progress: " << m_listCounter; 0189 m_error = true; 0190 } 0191 if (m_literalSize || m_readingLiteral) { 0192 qWarning() << "Literal parsing in progress: " << m_literalSize; 0193 m_error = true; 0194 } 0195 Q_ASSERT(responseReceived); 0196 if (m_message) { 0197 responseReceived(*m_message); 0198 m_message.reset(nullptr); 0199 } 0200 m_currentPayload = nullptr; 0201 }); 0202 } 0203 0204 void ImapStreamParser::setState(States state) 0205 { 0206 m_lastState = m_currentState; 0207 m_currentState = state; 0208 } 0209 0210 void ImapStreamParser::forwardToState(States state) 0211 { 0212 m_currentState = state; 0213 } 0214 0215 void ImapStreamParser::resetState() 0216 { 0217 m_currentState = m_lastState; 0218 } 0219 0220 void ImapStreamParser::processBuffer() 0221 { 0222 if (m_error) { 0223 qWarning() << "An error occurred"; 0224 return; 0225 } 0226 if (m_currentState == LiteralStringState && m_literalSize == 0 && m_readingLiteral) { 0227 literalEnd(); 0228 resetState(); 0229 m_readingLiteral = false; 0230 } 0231 0232 while (m_position < m_readPosition) { 0233 Q_ASSERT(m_position < length()); 0234 const char c = buffer()[m_position]; 0235 // qDebug() << "Checking :" << c << m_position << m_readPosition << m_currentState << m_listCounter; 0236 switch (m_currentState) { 0237 case InitState: 0238 if (c == '(') { 0239 listStart(); 0240 } else if (c == ')') { 0241 listEnd(); 0242 } else if (c == '[') { 0243 if (m_listCounter >= 1) { 0244 //Inside lists angle brackets are parsed as strings 0245 setState(AngleBracketStringState); 0246 m_stringStartPos = m_position; 0247 } else { 0248 responseCodeStart(); 0249 } 0250 } else if (c == ']') { 0251 responseCodeEnd(); 0252 } else if (c == ' ') { 0253 //Skip whitespace 0254 setState(WhitespaceState); 0255 } else if (c == '\r') { 0256 setState(CRLFState); 0257 } else if (c == '{') { 0258 setState(LiteralStringState); 0259 m_stringStartPos = m_position + 1; 0260 } else if (c == '\"') { 0261 setState(QuotedStringState); 0262 m_stringStartPos = m_position + 1; 0263 } else { 0264 setState(StringState); 0265 m_stringStartPos = m_position; 0266 } 0267 break; 0268 case QuotedStringState: 0269 if (c == '\"' && buffer().at(m_position - 1) != '\\') { 0270 //Unescaped quote 0271 resetState(); 0272 const auto endPos = m_position; 0273 string(buffer().constData() + m_stringStartPos, endPos - m_stringStartPos); 0274 m_stringStartPos = 0; 0275 } 0276 break; 0277 case LiteralStringState: 0278 if (c == '}') { 0279 m_literalSize = strtol(buffer().constData() + m_stringStartPos, nullptr, 10); 0280 // qDebug() << "Found literal size: " << m_literalSize; 0281 literalStart(m_literalSize); 0282 m_readingLiteral = false; 0283 m_stringStartPos = 0; 0284 break; 0285 } 0286 if (!m_readingLiteral) { 0287 //Skip CRLF after literal size 0288 if (c == '\n') { 0289 m_readingLiteral = true; 0290 if (m_isServerModeEnabled && m_literalSize > 0) { 0291 sendContinuationResponse(m_literalSize); 0292 } 0293 } 0294 } else { 0295 Q_ASSERT(m_position < length()); 0296 if (m_literalSize) { 0297 int size = m_literalSize; 0298 if (length() < m_position + size) { 0299 //If the literal is not complete we take what is available 0300 size = length() - m_position; 0301 } 0302 literalPart(buffer().constData() + m_position, size); 0303 m_position += size; 0304 m_literalSize -= size; 0305 } 0306 if (m_literalSize <= 0) { 0307 Q_ASSERT(m_literalSize == 0); 0308 literalEnd(); 0309 resetState(); 0310 m_readingLiteral = false; 0311 } 0312 continue; 0313 } 0314 break; 0315 case StringState: 0316 if (c == ' ' || 0317 c == ')' || //End of list 0318 c == '(' || //New list 0319 //FIXME because we want to concat in sublists. 0320 // c == '[' || 0321 c == ']' || 0322 c == '\r' || //CRLF 0323 c == '\"') { 0324 resetState(); 0325 string(buffer().constData() + m_stringStartPos, m_position - m_stringStartPos); 0326 m_stringStartPos = 0; 0327 continue; 0328 } 0329 //Inside lists we want to parse the angle brackets as part of the string. 0330 if (c == '[') { 0331 if (m_listCounter >= 1) { 0332 // qDebug() << "Switching to angle bracket state"; 0333 forwardToState(AngleBracketStringState); 0334 break; 0335 } 0336 } 0337 break; 0338 case AngleBracketStringState: 0339 if (c == ']') { 0340 resetState(); 0341 string(buffer().constData() + m_stringStartPos, m_position - m_stringStartPos + 1); 0342 m_stringStartPos = 0; 0343 } 0344 break; 0345 case SublistString: 0346 if (c == '(') { 0347 m_listCounter++; 0348 } else if (c == ')') { 0349 m_listCounter--; 0350 if (m_listCounter <= 1) { 0351 resetState(); 0352 string(buffer().constData() + m_stringStartPos, m_position - m_stringStartPos + 1); 0353 m_stringStartPos = 0; 0354 } 0355 } 0356 break; 0357 case WhitespaceState: 0358 if (c != ' ') { 0359 //Skip whitespace 0360 resetState(); 0361 continue; 0362 } 0363 break; 0364 case CRLFState: 0365 if (c == '\n') { 0366 lineEnd(); 0367 resetState(); 0368 } else { 0369 //Skip over the \r that isn't part of the CRLF 0370 resetState(); 0371 continue; 0372 } 0373 break; 0374 } 0375 m_position++; 0376 } 0377 } 0378 0379 void ImapStreamParser::parseStream() 0380 { 0381 if (m_processing) { 0382 return; 0383 } 0384 if (m_error) { 0385 qWarning() << "An error occurred"; 0386 return; 0387 } 0388 m_processing = true; 0389 while (m_socket->bytesAvailable()) { 0390 if (readFromSocket() <= 0) { 0391 //If we're not making progress we could loop forever, 0392 //and given that we check beforehand if there is data, 0393 //this should never happen. 0394 qWarning() << "Read nothing from the socket."; 0395 m_error = true; 0396 Q_ASSERT(false); 0397 return; 0398 }; 0399 processBuffer(); 0400 } 0401 m_processing = false; 0402 } 0403 0404 void ImapStreamParser::trimBuffer() 0405 { 0406 int offset = m_position; 0407 if (m_stringStartPos) { 0408 offset = qMin(m_stringStartPos, m_position); 0409 } 0410 0411 auto remainderSize = m_readPosition - offset; 0412 Q_ASSERT( remainderSize >= 0); 0413 QByteArray *otherBuffer; 0414 if (m_current == &m_data1) { 0415 otherBuffer = &m_data2; 0416 } else { 0417 otherBuffer = &m_data1; 0418 } 0419 if (remainderSize) { 0420 otherBuffer->replace(0, remainderSize, buffer().constData() + offset, remainderSize); 0421 } 0422 m_current = otherBuffer; 0423 m_readPosition = remainderSize; 0424 m_position -= offset; 0425 if (m_stringStartPos) { 0426 m_stringStartPos -= offset; 0427 } 0428 // qDebug() << "Buffer after trim: " << mid(0, m_readPosition); 0429 } 0430 0431 int ImapStreamParser::availableDataSize() const 0432 { 0433 return m_socket->bytesAvailable() + length() - m_position; 0434 } 0435 0436 QByteArray ImapStreamParser::readUntilCommandEnd() 0437 { 0438 QByteArray result; 0439 auto startPos = m_position; 0440 onLineEnd([&result, this, startPos]() { 0441 result = mid(startPos, m_position - startPos - 1); 0442 }); 0443 Q_FOREVER { 0444 if (!m_socket->bytesAvailable()) { 0445 if (!m_socket->waitForReadyRead(10000)) { 0446 qWarning() << "No data available"; 0447 return result; 0448 } 0449 } 0450 parseStream(); 0451 if (!result.isEmpty() && m_currentState == InitState) { 0452 // qDebug() << "Got a result: " << m_readingLiteral; 0453 // result.append(m_literalData); 0454 break; 0455 } 0456 } 0457 qDebug() << "Read until command end: " << result; 0458 return result; 0459 } 0460 0461 void ImapStreamParser::sendContinuationResponse(qint64 size) 0462 { 0463 QByteArray block = "+ Ready for literal data (expecting " + 0464 QByteArray::number(size) + " bytes)\r\n"; 0465 m_socket->write(block); 0466 m_socket->waitForBytesWritten(30000); 0467 } 0468 0469 void ImapStreamParser::onResponseReceived(std::function<void(const Message &)> f) 0470 { 0471 responseReceived = f; 0472 } 0473 0474 bool ImapStreamParser::error() const 0475 { 0476 return m_error; 0477 } 0478 0479 QByteArray ImapStreamParser::currentBuffer() const 0480 { 0481 return mid(0, m_readPosition); 0482 }