From 2a7142be1ec0d97f1c2812eb7d07b196226a972b Mon Sep 17 00:00:00 2001 From: Javier Date: Wed, 19 Aug 2026 00:40:35 +0200 Subject: [PATCH] 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 --- src/services/imap/imapconnection.cpp | 26 +++++++++++++++++++++----- src/services/imap/imapsynchronizer.cpp | 9 +++++---- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/services/imap/imapconnection.cpp b/src/services/imap/imapconnection.cpp index 61cf3f7..893cae2 100644 --- a/src/services/imap/imapconnection.cpp +++ b/src/services/imap/imapconnection.cpp @@ -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; } diff --git a/src/services/imap/imapsynchronizer.cpp b/src/services/imap/imapsynchronizer.cpp index 94c24ab..da6bfba 100644 --- a/src/services/imap/imapsynchronizer.cpp +++ b/src/services/imap/imapsynchronizer.cpp @@ -369,22 +369,23 @@ QVector 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 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); } }