Line data Source code
1 : #pragma once
2 :
3 : #include <QObject>
4 : #include <QString>
5 : #include <QTimer>
6 :
7 : #include "data/AccountConfig.h"
8 : #include "data/Models.h"
9 : #include "data/SettingsSyncModels.h"
10 : #include "service/ImapService.h"
11 :
12 : class ConnectionHealthMonitor;
13 :
14 : // T-312/T-314: Manages settings synchronisation via a dedicated IMAP connection.
15 : // Stores settings as a virtual mail (JSON body) in a configurable IMAP folder.
16 : // Uses IMAP IDLE for real-time change detection from other clients.
17 : //
18 : // Architecture: owns its own ImapService instance (separate from the main
19 : // connection) to avoid interference with regular mail operations.
20 : //
21 : // T-720: auto-reconnect (and dead-socket detection) for the dedicated IMAP
22 : // connection is delegated to ConnectionHealthMonitor (Sprint 72). The
23 : // m_reconnectTimer/m_reconnectAttempts members and handleDisconnect()/
24 : // onReconnectTimer() methods have been removed.
25 : class SettingsSyncService : public QObject {
26 2 : Q_OBJECT
27 : #ifdef MAILJD_UNIT_TEST
28 : friend class TestSettingsSyncService;
29 : #endif
30 :
31 : public:
32 : explicit SettingsSyncService(QObject *parent = nullptr);
33 : ~SettingsSyncService() override;
34 :
35 : // Configure and connect
36 : void configure(const ImapConfig &config, const QString &syncFolder);
37 : void setEnabled(bool enabled);
38 11 : bool isEnabled() const { return m_enabled; }
39 4 : QString syncFolder() const { return m_syncFolder; }
40 :
41 : // Upload current settings to IMAP
42 : void uploadSettings(const SyncPayload &payload);
43 :
44 : // Fetch latest settings from IMAP
45 : void fetchLatestSettings();
46 :
47 : // Start/stop IDLE-watching on the sync folder
48 : void startWatching();
49 : void stopWatching();
50 :
51 : // Disconnect and clean up
52 : void shutdown();
53 :
54 : signals:
55 : void settingsReceived(const SyncPayload &payload);
56 : void uploadComplete();
57 : void syncError(const QString &error);
58 : void syncFolderReady();
59 : void connected();
60 :
61 : private slots:
62 : void onStateChanged(ImapService::State newState);
63 : void onFolderListReceived(const QList<FolderInfo> &folders);
64 : void onFolderSelected(const QString &path, int messageCount,
65 : quint32 uidValidity, quint64 highestModseq);
66 : void onFolderCreated(const QString &folderPath);
67 : void onSearchResultReceived(const QList<qint64> &uids);
68 : void onRawBodyReceived(qint64 uid, const QByteArray &rawBody);
69 : void onMessageAppended(const QString &folder, qint64 uid);
70 : void onIdleNewMessages(int newCount);
71 : void onAppendError(const QString &error);
72 : void onFolderOperationError(const QString &op, const QString &error);
73 : void onExpungeComplete();
74 :
75 : private:
76 : enum class Phase {
77 : Idle,
78 : CheckingFolder, // LIST to check if sync folder exists
79 : CreatingFolder, // CREATE sync folder
80 : SelectingFolder, // SELECT sync folder
81 : Fetching, // SEARCH + FETCH latest settings
82 : Uploading, // APPEND new settings
83 : CleaningUp, // STORE \Deleted + EXPUNGE old settings
84 : WatchingIdle, // IDLE on sync folder
85 : };
86 :
87 : void checkOrCreateFolder();
88 : void doFetch();
89 : void doUpload();
90 : void cleanupOldSettings(qint64 keepUid);
91 : void startIdleWatch();
92 :
93 : ImapService *m_imap = nullptr;
94 : // T-720: Health monitor for the dedicated IMAP connection. Periodic
95 : // liveness probes + exponential-backoff reconnect + suspend/network
96 : // reactivity — same code path as the main + body + search connections.
97 : ConnectionHealthMonitor *m_health = nullptr;
98 : ImapConfig m_config;
99 : QString m_syncFolder;
100 : bool m_enabled = false;
101 : Phase m_phase = Phase::Idle;
102 :
103 : // State tracking
104 : qint64 m_currentSettingsUid = -1; // UID of the current settings mail
105 : int m_messageCount = 0; // Messages in sync folder
106 : QByteArray m_pendingUpload; // RFC-2822 message to upload
107 : qint64 m_newAppendedUid = -1; // UID of just-appended message
108 : };
|