fix: IMAP FETCH timeout and tagged response detection

- sendCommandWaitBytes(): check for tagged response (OK/NO/BAD) BEFORE parsing literals
- Fixed detection of 'A0001 OK ...' responses that were being missed
- Added debug logging on failure for easier troubleshooting
- Increased FETCH timeout from 30s to 120s for large emails with attachments
- Applied same 120s timeout to single UID fallback fetches
This commit is contained in:
2026-08-19 00:40:35 +02:00
parent 43ccd7bd26
commit 2a7142be1e
2 changed files with 26 additions and 9 deletions
+21 -5
View File
@@ -168,6 +168,18 @@ bool ImapConnection::sendCommandWaitBytes(const QString &command, QByteArray &re
const int lineEnd = buffer.indexOf("\r\n", pos);
if (lineEnd < 0) break;
const QByteArray line = buffer.mid(pos, lineEnd - pos);
// Check for tagged response FIRST (before literal parsing)
// Tagged responses: "A0001 OK ...", "A0001 NO ...", "A0001 BAD ..."
if (line.startsWith(tagBytes + QByteArrayLiteral(" OK ")) ||
line.startsWith(tagBytes + QByteArrayLiteral(" NO ")) ||
line.startsWith(tagBytes + QByteArrayLiteral(" BAD ")) ||
line.startsWith(tagBytes + QByteArrayLiteral(" "))) {
complete = true;
loop.quit();
break;
}
const int open = line.lastIndexOf('{');
QByteArray literalSize = open >= 0 && line.endsWith('}')
? line.mid(open + 1, line.size() - open - 2) : QByteArray();
@@ -183,11 +195,6 @@ bool ImapConnection::sendCommandWaitBytes(const QString &command, QByteArray &re
if (buffer.mid(pos, 2) == QByteArrayLiteral("\r\n")) pos += 2;
continue;
}
if (line.startsWith(tagBytes + QByteArrayLiteral(" "))) {
complete = true;
loop.quit();
break;
}
pos = lineEnd + 2;
}
};
@@ -212,6 +219,15 @@ bool ImapConnection::sendCommandWaitBytes(const QString &command, QByteArray &re
m_readyReadConnection = connect(m_socket, &QSslSocket::readyRead, this, &ImapConnection::onReadyRead);
response = buffer;
// Debug: log if failed
if (timedOut || !complete) {
qWarning() << "IMAP command failed:" << command.left(100)
<< "- timedOut:" << timedOut
<< "- complete:" << complete
<< "- response size:" << buffer.size()
<< "- last 200 chars:" << QString::fromUtf8(buffer.right(200));
}
if (timedOut || !complete) return false;
return true;
}
+5 -4
View File
@@ -369,22 +369,23 @@ QVector<MailItem> ImapSynchronizer::fetchMailItems(const QString& folderId, qint
QString fetchCommand = QString("UID FETCH %1 (BODY.PEEK[] FLAGS INTERNALDATE)")
.arg(batchList);
QByteArray fetchResponse;
if (!conn.sendCommandWaitBytes(fetchCommand, fetchResponse, 30000)) {
qWarning() << "FETCH failed for batch" << i << "(" << batch.size() << "UIDs)"
// Increase timeout to 120s for large emails with attachments
if (!conn.sendCommandWaitBytes(fetchCommand, fetchResponse, 120000)) {
qWarning() << "FETCH failed for batch" << i << "(" << batch.size() << "UIDs)"
<< "- first UID:" << batch.first() << "last UID:" << batch.last()
<< "- response:" << QString::fromUtf8(fetchResponse).left(200);
// Fallback: try fetching UIDs one by one
for (qint64 uid : batch) {
QString singleFetchCmd = QString("UID FETCH %1 (BODY.PEEK[] FLAGS INTERNALDATE)").arg(uid);
QByteArray singleResponse;
if (conn.sendCommandWaitBytes(singleFetchCmd, singleResponse, 30000)) {
if (conn.sendCommandWaitBytes(singleFetchCmd, singleResponse, 120000)) {
QVector<MailItem> singleItems = parseFetchResponseBytes(singleResponse);
for (MailItem& item : singleItems) {
item.setFolderId(fid);
items.append(item);
}
} else {
qWarning() << " Single UID fetch failed for UID" << uid
qWarning() << " Single UID fetch failed for UID" << uid
<< "- response:" << QString::fromUtf8(singleResponse).left(200);
}
}