service/ImapAppendStream.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <QByteArray> | ||
| 4 | #include <QString> | ||
| 5 | |||
| 6 | #include <functional> | ||
| 7 | |||
| 8 | #include "service/ImapResponseParser.h" | ||
| 9 | #include "service/ImapService.h" | ||
| 10 | |||
| 11 | class ImapCommandTable; | ||
| 12 | class ImapMutationCommands; | ||
| 13 | class ImapPushSession; | ||
| 14 | class QObject; | ||
| 15 | struct ImapPendingCommand; | ||
| 16 | class QSslSocket; | ||
| 17 | |||
| 18 | // APPEND of one IMAP connection (Sprint 92 S92.1.7, extracted from | ||
| 19 | // ImapService): request ids, the APPEND command with validated flags, the | ||
| 20 | // literal upload after the server's "+" continuation (small literals in | ||
| 21 | // one write, large ones pumped in bounded chunks on separate event turns), | ||
| 22 | // stale-payload rejection on an unexpected continuation, and the tagged | ||
| 23 | // completion with APPENDUID evidence. | ||
| 24 | // | ||
| 25 | // Owner: ImapService (std::unique_ptr), connection thread. Plain class; the | ||
| 26 | // socket's bytesWritten stays connected (service constructor) to the | ||
| 27 | // literal pump. The line reader dispatches every "+" line to | ||
| 28 | // handleContinuation. Mutation tokens go through the mutation commands, | ||
| 29 | // commands through the command table, push restart through the push | ||
| 30 | // session; failure, E2E phases and the four APPEND signals through Deps | ||
| 31 | // (the emitter stays ImapService). | ||
| 32 | class ImapAppendStream { | ||
| 33 | public: | ||
| 34 | using State = ImapService::State; | ||
| 35 | using MutationOutcome = ImapService::MutationOutcome; | ||
| 36 | |||
| 37 | struct Deps { | ||
| 38 | std::function<void(const QString &error)> failConnection; | ||
| 39 | // ImapService signals. | ||
| 40 | std::function<void(const QString &folder, qint64 uid)> messageAppended; | ||
| 41 | std::function<void(const QString &error)> appendError; | ||
| 42 | std::function<void(quint64 requestId, const QString &folder, qint64 uid, quint32 uidValidity)> appendCompleted; | ||
| 43 | std::function<void(quint64 requestId, const QString &error)> appendFailed; | ||
| 44 | // MAILJD_E2E_TESTING only (present in every build for a stable layout). | ||
| 45 | std::function<void(const QString &phase, quint64 requestToken)> reportE2eTransportPhase; | ||
| 46 | }; | ||
| 47 | ImapAppendStream(QObject *owner, QSslSocket *socket, ImapCommandTable &commands, | ||
| 48 | ImapPushSession &push, ImapMutationCommands &mutations, | ||
| 49 | const State &state, Deps deps); | ||
| 50 | |||
| 51 | quint64 appendMessage(const QString &folder, const QByteArray &rfcMessage, | ||
| 52 | const QString &flags = {}, qint64 mutationToken = 0); | ||
| 53 | quint64 reserveAppendRequestId(); | ||
| 54 | void appendMessage(quint64 requestId, const QString &folder, | ||
| 55 | const QByteArray &rfcMessage, const QString &flags = {}, | ||
| 56 | qint64 mutationToken = 0); | ||
| 57 | void resetAppendLiteral(); | ||
| 58 | // T-79.A1/H1: drops the in-flight APPEND literal with the session (its | ||
| 59 | // record dies with the command table's records). | ||
| 60 | void resetForSession(); | ||
| 61 | // True while an APPEND's literal is not yet completely queued. | ||
| 62 | bool literalPending(); | ||
| 63 | void scheduleAppendLiteralPump(); | ||
| 64 | // T-176: APPEND literal continuation ("+" response). | ||
| 65 | void handleContinuation(const QString &line); | ||
| 66 | void handleTaggedAppend(const TaggedResponse &response, ImapPendingCommand &command); | ||
| 67 | |||
| 68 | // Request id counter (the payload, folder, request id and token of an | ||
| 69 | // in-flight APPEND live in its pending record, FUNC-08). | ||
| 70 | 1 | quint64 &nextAppendRequestId() { return m_nextAppendRequestId; } | |
| 71 | |||
| 72 | private: | ||
| 73 | void appendMessageWithId(quint64 requestId, const QString &folder, | ||
| 74 | const QByteArray &rfcMessage, | ||
| 75 | const QString &flags, qint64 mutationToken); | ||
| 76 | void pumpAppendLiteral(); | ||
| 77 | ✗ | void failConnection(const QString &error) { m_deps.failConnection(error); } | |
| 78 | 20 | void reportE2eTransportPhase(const QString &phase, quint64 requestToken = 0) { | |
| 79 | 20 | m_deps.reportE2eTransportPhase(phase, requestToken); | |
| 80 | 20 | } | |
| 81 | |||
| 82 | QObject *m_owner; // context of the pump's event-turn callbacks | ||
| 83 | QSslSocket *m_socket; | ||
| 84 | ImapCommandTable &m_commands; | ||
| 85 | ImapPushSession &m_push; | ||
| 86 | ImapMutationCommands &m_mutations; | ||
| 87 | const State &m_state; // ImapService::m_state | ||
| 88 | Deps m_deps; | ||
| 89 | |||
| 90 | quint64 m_nextAppendRequestId = 1; | ||
| 91 | // Large literals are queued to TLS in bounded steps on separate event | ||
| 92 | // turns. The complete immutable MIME bytes remain owned until CRLF queues. | ||
| 93 | qint64 m_appendLiteralOffset = 0; | ||
| 94 | quint64 m_appendLiteralGeneration = 0; | ||
| 95 | bool m_appendLiteralStreaming = false; | ||
| 96 | bool m_appendPumpScheduled = false; | ||
| 97 | }; | ||
| 98 |