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-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com> 0006 SPDX-FileContributor: Kevin Ottens <kevin@kdab.com> 0007 0008 SPDX-License-Identifier: LGPL-2.0-or-later 0009 */ 0010 0011 #include "imapstreamparser.h" 0012 0013 #include <QIODevice> 0014 #include <ctype.h> 0015 0016 using namespace KIMAP; 0017 0018 ImapStreamParser::ImapStreamParser(QIODevice *socket, bool serverModeEnabled) 0019 : m_position(0) 0020 , m_literalSize(0) 0021 { 0022 m_socket = socket; 0023 m_isServerModeEnabled = serverModeEnabled; 0024 } 0025 0026 QString ImapStreamParser::readUtf8String() 0027 { 0028 QByteArray tmp; 0029 tmp = readString(); 0030 QString result = QString::fromUtf8(tmp); 0031 return result; 0032 } 0033 0034 QByteArray ImapStreamParser::readString() 0035 { 0036 QByteArray result; 0037 if (!waitForMoreData(m_data.isEmpty())) { 0038 throw ImapParserException("Unable to read more data"); 0039 } 0040 stripLeadingSpaces(); 0041 if (!waitForMoreData(m_position >= m_data.length())) { 0042 throw ImapParserException("Unable to read more data"); 0043 } 0044 0045 // literal string 0046 // TODO: error handling 0047 if (hasLiteral()) { 0048 while (!atLiteralEnd()) { 0049 result += readLiteralPart(); 0050 } 0051 return result; 0052 } 0053 0054 // quoted string 0055 return parseQuotedString(); 0056 } 0057 0058 bool ImapStreamParser::hasString() 0059 { 0060 if (!waitForMoreData(m_position >= m_data.length())) { 0061 throw ImapParserException("Unable to read more data"); 0062 } 0063 int savedPos = m_position; 0064 stripLeadingSpaces(); 0065 int pos = m_position; 0066 m_position = savedPos; 0067 const char dataChar = m_data.at(pos); 0068 if (dataChar == '{') { 0069 return true; // literal string 0070 } else if (dataChar == '"') { 0071 return true; // quoted string 0072 } else if (dataChar != ' ' && dataChar != '(' && dataChar != ')' && dataChar != '[' && dataChar != ']' && dataChar != '\n' && dataChar != '\r') { 0073 return true; // unquoted string 0074 } 0075 0076 return false; // something else, not a string 0077 } 0078 0079 bool ImapStreamParser::hasLiteral() 0080 { 0081 if (!waitForMoreData(m_position >= m_data.length())) { 0082 throw ImapParserException("Unable to read more data"); 0083 } 0084 int savedPos = m_position; 0085 stripLeadingSpaces(); 0086 if (m_data.at(m_position) == '{') { 0087 int end = -1; 0088 do { 0089 end = m_data.indexOf('}', m_position); 0090 if (!waitForMoreData(end == -1)) { 0091 throw ImapParserException("Unable to read more data"); 0092 } 0093 } while (end == -1); 0094 Q_ASSERT(end > m_position); 0095 m_literalSize = m_data.mid(m_position + 1, end - m_position - 1).toInt(); 0096 // strip CRLF 0097 m_position = end + 1; 0098 // ensure that the CRLF is available 0099 if (!waitForMoreData(m_position + 1 >= m_data.length())) { 0100 throw ImapParserException("Unable to read more data"); 0101 } 0102 if (m_position < m_data.length() && m_data.at(m_position) == '\r') { 0103 ++m_position; 0104 } 0105 if (m_position < m_data.length() && m_data.at(m_position) == '\n') { 0106 ++m_position; 0107 } 0108 0109 // FIXME: Makes sense only on the server side? 0110 if (m_isServerModeEnabled && m_literalSize > 0) { 0111 sendContinuationResponse(m_literalSize); 0112 } 0113 return true; 0114 } else { 0115 m_position = savedPos; 0116 return false; 0117 } 0118 } 0119 0120 bool ImapStreamParser::atLiteralEnd() const 0121 { 0122 return (m_literalSize == 0); 0123 } 0124 0125 QByteArray ImapStreamParser::readLiteralPart() 0126 { 0127 static const qint64 maxLiteralPartSize = 4096; 0128 int size = qMin(maxLiteralPartSize, m_literalSize); 0129 0130 if (!waitForMoreData(m_data.length() < m_position + size)) { 0131 throw ImapParserException("Unable to read more data"); 0132 } 0133 0134 if (m_data.length() < m_position + size) { // Still not enough data 0135 // Take what's already there 0136 size = m_data.length() - m_position; 0137 } 0138 0139 QByteArray result = m_data.mid(m_position, size); 0140 m_position += size; 0141 m_literalSize -= size; 0142 Q_ASSERT(m_literalSize >= 0); 0143 trimBuffer(); 0144 0145 return result; 0146 } 0147 0148 bool ImapStreamParser::hasList() 0149 { 0150 if (!waitForMoreData(m_position >= m_data.length())) { 0151 throw ImapParserException("Unable to read more data"); 0152 } 0153 int savedPos = m_position; 0154 stripLeadingSpaces(); 0155 int pos = m_position; 0156 m_position = savedPos; 0157 if (m_data.at(pos) == '(') { 0158 return true; 0159 } 0160 return false; 0161 } 0162 0163 bool ImapStreamParser::atListEnd() 0164 { 0165 if (!waitForMoreData(m_position >= m_data.length())) { 0166 throw ImapParserException("Unable to read more data"); 0167 } 0168 int savedPos = m_position; 0169 stripLeadingSpaces(); 0170 int pos = m_position; 0171 m_position = savedPos; 0172 if (m_data.at(pos) == ')') { 0173 m_position = pos + 1; 0174 return true; 0175 } 0176 return false; 0177 } 0178 0179 QList<QByteArray> ImapStreamParser::readParenthesizedList() 0180 { 0181 QList<QByteArray> result; 0182 if (!waitForMoreData(m_data.length() <= m_position)) { 0183 throw ImapParserException("Unable to read more data"); 0184 } 0185 0186 stripLeadingSpaces(); 0187 if (m_data.at(m_position) != '(') { 0188 return result; // no list found 0189 } 0190 0191 bool concatToLast = false; 0192 int count = 0; 0193 int sublistbegin = m_position; 0194 int i = m_position + 1; 0195 for (;;) { 0196 if (!waitForMoreData(m_data.length() <= i)) { 0197 m_position = i; 0198 throw ImapParserException("Unable to read more data"); 0199 } 0200 if (m_data.at(i) == '(') { 0201 ++count; 0202 if (count == 1) { 0203 sublistbegin = i; 0204 } 0205 ++i; 0206 continue; 0207 } 0208 if (m_data.at(i) == ')') { 0209 if (count <= 0) { 0210 m_position = i + 1; 0211 return result; 0212 } 0213 if (count == 1) { 0214 result.append(m_data.mid(sublistbegin, i - sublistbegin + 1)); 0215 } 0216 --count; 0217 ++i; 0218 continue; 0219 } 0220 if (m_data.at(i) == ' ') { 0221 ++i; 0222 continue; 0223 } 0224 if (m_data.at(i) == '"') { 0225 if (count > 0) { 0226 m_position = i; 0227 parseQuotedString(); 0228 i = m_position; 0229 continue; 0230 } 0231 } 0232 if (m_data.at(i) == '[') { 0233 concatToLast = true; 0234 if (result.isEmpty()) { 0235 result.append(QByteArray()); 0236 } 0237 result.last() += '['; 0238 ++i; 0239 continue; 0240 } 0241 if (m_data.at(i) == ']') { 0242 concatToLast = false; 0243 result.last() += ']'; 0244 ++i; 0245 continue; 0246 } 0247 if (count == 0) { 0248 m_position = i; 0249 QByteArray ba; 0250 if (hasLiteral()) { 0251 while (!atLiteralEnd()) { 0252 ba += readLiteralPart(); 0253 } 0254 } else { 0255 ba = readString(); 0256 } 0257 0258 // We might sometime get some unwanted CRLF, but we're still not at the end 0259 // of the list, would make further string reads fail so eat the CRLFs. 0260 while ((m_position < m_data.size()) && (m_data.at(m_position) == '\r' || m_data.at(m_position) == '\n')) { 0261 m_position++; 0262 } 0263 0264 i = m_position - 1; 0265 if (concatToLast) { 0266 result.last() += ba; 0267 } else { 0268 result.append(ba); 0269 } 0270 } 0271 ++i; 0272 } 0273 0274 throw ImapParserException("Something went very very wrong!"); 0275 } 0276 0277 bool ImapStreamParser::hasResponseCode() 0278 { 0279 if (!waitForMoreData(m_position >= m_data.length())) { 0280 throw ImapParserException("Unable to read more data"); 0281 } 0282 int savedPos = m_position; 0283 stripLeadingSpaces(); 0284 int pos = m_position; 0285 m_position = savedPos; 0286 if (m_data.at(pos) == '[') { 0287 m_position = pos + 1; 0288 return true; 0289 } 0290 return false; 0291 } 0292 0293 bool ImapStreamParser::atResponseCodeEnd() 0294 { 0295 if (!waitForMoreData(m_position >= m_data.length())) { 0296 throw ImapParserException("Unable to read more data"); 0297 } 0298 int savedPos = m_position; 0299 stripLeadingSpaces(); 0300 int pos = m_position; 0301 m_position = savedPos; 0302 if (m_data.at(pos) == ']') { 0303 m_position = pos + 1; 0304 return true; 0305 } 0306 return false; 0307 } 0308 0309 QByteArray ImapStreamParser::parseQuotedString() 0310 { 0311 QByteArray result; 0312 if (!waitForMoreData(m_data.length() == 0)) { 0313 throw ImapParserException("Unable to read more data"); 0314 } 0315 stripLeadingSpaces(); 0316 int end = m_position; 0317 result.clear(); 0318 if (!waitForMoreData(m_position >= m_data.length())) { 0319 throw ImapParserException("Unable to read more data"); 0320 } 0321 if (!waitForMoreData(m_position >= m_data.length())) { 0322 throw ImapParserException("Unable to read more data"); 0323 } 0324 0325 bool foundSlash = false; 0326 // quoted string 0327 if (m_data.at(m_position) == '"') { 0328 ++m_position; 0329 int i = m_position; 0330 for (;;) { 0331 if (!waitForMoreData(m_data.length() <= i)) { 0332 m_position = i; 0333 throw ImapParserException("Unable to read more data"); 0334 } 0335 if (m_data.at(i) == '\\') { 0336 i += 2; 0337 foundSlash = true; 0338 continue; 0339 } 0340 if (m_data.at(i) == '"') { 0341 result = m_data.mid(m_position, i - m_position); 0342 end = i + 1; // skip the '"' 0343 break; 0344 } 0345 ++i; 0346 } 0347 } 0348 0349 // unquoted string 0350 else { 0351 bool reachedInputEnd = true; 0352 int i = m_position; 0353 for (;;) { 0354 if (!waitForMoreData(m_data.length() <= i)) { 0355 m_position = i; 0356 throw ImapParserException("Unable to read more data"); 0357 } 0358 if (m_data.at(i) == ' ' || m_data.at(i) == '(' || m_data.at(i) == ')' || m_data.at(i) == '[' || m_data.at(i) == ']' || m_data.at(i) == '\n' 0359 || m_data.at(i) == '\r' || m_data.at(i) == '"') { 0360 end = i; 0361 reachedInputEnd = false; 0362 break; 0363 } 0364 if (m_data.at(i) == '\\') { 0365 foundSlash = true; 0366 } 0367 i++; 0368 } 0369 if (reachedInputEnd) { // FIXME: how can it get here? 0370 end = m_data.length(); 0371 } 0372 0373 result = m_data.mid(m_position, end - m_position); 0374 } 0375 0376 // strip quotes 0377 if (foundSlash) { 0378 while (result.contains("\\\"")) { 0379 result.replace("\\\"", "\""); 0380 } 0381 while (result.contains("\\\\")) { 0382 result.replace("\\\\", "\\"); 0383 } 0384 } 0385 m_position = end; 0386 return result; 0387 } 0388 0389 qint64 ImapStreamParser::readNumber(bool *ok) 0390 { 0391 qint64 result; 0392 if (ok) { 0393 *ok = false; 0394 } 0395 if (!waitForMoreData(m_data.length() == 0)) { 0396 throw ImapParserException("Unable to read more data"); 0397 } 0398 stripLeadingSpaces(); 0399 if (!waitForMoreData(m_position >= m_data.length())) { 0400 throw ImapParserException("Unable to read more data"); 0401 } 0402 if (m_position >= m_data.length()) { 0403 throw ImapParserException("Unable to read more data"); 0404 } 0405 int i = m_position; 0406 for (;;) { 0407 if (!waitForMoreData(m_data.length() <= i)) { 0408 m_position = i; 0409 throw ImapParserException("Unable to read more data"); 0410 } 0411 if (!isdigit(m_data.at(i))) { 0412 break; 0413 } 0414 ++i; 0415 } 0416 const QByteArray tmp = m_data.mid(m_position, i - m_position); 0417 result = tmp.toLongLong(ok); 0418 m_position = i; 0419 return result; 0420 } 0421 0422 void ImapStreamParser::stripLeadingSpaces() 0423 { 0424 for (int i = m_position; i < m_data.length(); ++i) { 0425 if (m_data.at(i) != ' ') { 0426 m_position = i; 0427 return; 0428 } 0429 } 0430 m_position = m_data.length(); 0431 } 0432 0433 bool ImapStreamParser::waitForMoreData(bool wait) 0434 { 0435 if (wait) { 0436 if (m_socket->bytesAvailable() > 0 || m_socket->waitForReadyRead(30000)) { 0437 m_data.append(m_socket->readAll()); 0438 } else { 0439 return false; 0440 } 0441 } 0442 return true; 0443 } 0444 0445 void ImapStreamParser::setData(const QByteArray &data) 0446 { 0447 m_data = data; 0448 } 0449 0450 QByteArray ImapStreamParser::readRemainingData() 0451 { 0452 return m_data.mid(m_position); 0453 } 0454 0455 int ImapStreamParser::availableDataSize() const 0456 { 0457 return m_socket->bytesAvailable() + m_data.size() - m_position; 0458 } 0459 0460 bool ImapStreamParser::atCommandEnd() 0461 { 0462 int savedPos = m_position; 0463 do { 0464 if (!waitForMoreData(m_position >= m_data.length())) { 0465 throw ImapParserException("Unable to read more data"); 0466 } 0467 stripLeadingSpaces(); 0468 } while (m_position >= m_data.size()); 0469 0470 if (m_data.at(m_position) == '\n' || m_data.at(m_position) == '\r') { 0471 if (m_data.at(m_position) == '\r') { 0472 ++m_position; 0473 } 0474 if (m_position < m_data.length() && m_data.at(m_position) == '\n') { 0475 ++m_position; 0476 } 0477 0478 // We'd better empty m_data from time to time before it grows out of control 0479 trimBuffer(); 0480 0481 return true; // command end 0482 } 0483 m_position = savedPos; 0484 return false; // something else 0485 } 0486 0487 QByteArray ImapStreamParser::readUntilCommandEnd() 0488 { 0489 QByteArray result; 0490 int i = m_position; 0491 int paranthesisBalance = 0; 0492 for (;;) { 0493 if (!waitForMoreData(m_data.length() <= i)) { 0494 m_position = i; 0495 throw ImapParserException("Unable to read more data"); 0496 } 0497 if (m_data.at(i) == '{') { 0498 m_position = i - 1; 0499 hasLiteral(); // init literal size 0500 result.append(m_data.mid(i, m_position + 1)); 0501 while (!atLiteralEnd()) { 0502 result.append(readLiteralPart()); 0503 } 0504 i = m_position; 0505 } 0506 if (m_data.at(i) == '(') { 0507 paranthesisBalance++; 0508 } 0509 if (m_data.at(i) == ')') { 0510 paranthesisBalance--; 0511 } 0512 if ((i == m_data.length() && paranthesisBalance == 0) || m_data.at(i) == '\n' || m_data.at(i) == '\r') { 0513 break; // command end 0514 } 0515 result.append(m_data.at(i)); 0516 ++i; 0517 } 0518 m_position = i; 0519 atCommandEnd(); 0520 return result; 0521 } 0522 0523 void ImapStreamParser::sendContinuationResponse(qint64 size) 0524 { 0525 QByteArray block = "+ Ready for literal data (expecting " + QByteArray::number(size) + " bytes)\r\n"; 0526 m_socket->write(block); 0527 m_socket->waitForBytesWritten(30000); 0528 } 0529 0530 void ImapStreamParser::trimBuffer() 0531 { 0532 if (m_position < 4096) { // right() is expensive, so don't do it for every line 0533 return; 0534 } 0535 m_data = m_data.right(m_data.size() - m_position); 0536 m_position = 0; 0537 }