Line data Source code
1 : #ifndef MAIL_CLIENT_H
2 : #define MAIL_CLIENT_H
3 :
4 : #include "config.h"
5 : #include "imap_client.h"
6 : #include <stddef.h>
7 :
8 : /**
9 : * @file mail_client.h
10 : * @brief Unified dispatch layer for IMAP and Gmail backends.
11 : *
12 : * Sits between email_service.c and the protocol-specific clients.
13 : * cfg->gmail_mode selects the backend at connect time.
14 : */
15 :
16 : typedef struct MailClient MailClient;
17 :
18 : /** Semantic search criteria (protocol-independent). */
19 : typedef enum {
20 : MAIL_SEARCH_ALL,
21 : MAIL_SEARCH_UNREAD, /**< IMAP: UNSEEN; Gmail: has UNREAD label */
22 : MAIL_SEARCH_FLAGGED, /**< IMAP: FLAGGED; Gmail: has STARRED label */
23 : MAIL_SEARCH_DONE, /**< IMAP: KEYWORD $Done; Gmail: not used yet */
24 : } MailSearchCriteria;
25 :
26 : /** RAII cleanup helper. */
27 : static inline void mail_client_free_ptr(MailClient **p);
28 : #define RAII_MAIL __attribute__((cleanup(mail_client_free_ptr)))
29 :
30 : /**
31 : * @brief Connect to the mail backend specified by cfg.
32 : *
33 : * If cfg->gmail_mode: uses Gmail REST API (OAuth2).
34 : * Otherwise: uses IMAP over TLS.
35 : *
36 : * @param cfg Account configuration (borrowed, not copied).
37 : * @return New client handle, or NULL on failure.
38 : */
39 : MailClient *mail_client_connect(Config *cfg);
40 :
41 : /** @brief Disconnect and free the client. Safe to call with NULL. */
42 : void mail_client_free(MailClient *c);
43 :
44 : /** @brief Returns 1 for Gmail (label-based), 0 for IMAP (folder-based). */
45 : int mail_client_uses_labels(const MailClient *c);
46 :
47 : /**
48 : * @brief List folders (IMAP) or labels (Gmail).
49 : *
50 : * @param c Connected client.
51 : * @param names_out Heap-allocated array of name strings. Caller frees each + array.
52 : * @param count_out Number of entries.
53 : * @param sep_out Hierarchy separator (IMAP only; '/' for Gmail). May be NULL.
54 : * @return 0 on success, -1 on error.
55 : */
56 : int mail_client_list(MailClient *c, char ***names_out, int *count_out, char *sep_out);
57 :
58 : /**
59 : * @brief Select a folder (IMAP) or set the active label filter (Gmail).
60 : *
61 : * Must be called before search/fetch operations.
62 : */
63 : int mail_client_select(MailClient *c, const char *name);
64 :
65 : /**
66 : * @brief Search for messages matching criteria within the selected folder/label.
67 : *
68 : * @param c Connected client.
69 : * @param criteria Semantic search criteria.
70 : * @param uids_out Heap-allocated array of 16-char UID strings. Caller must free().
71 : * @param count_out Number of UIDs.
72 : * @return 0 on success, -1 on error.
73 : */
74 : int mail_client_search(MailClient *c, MailSearchCriteria criteria,
75 : char (**uids_out)[17], int *count_out);
76 :
77 : /**
78 : * @brief Fetch raw RFC 2822 headers for a message.
79 : * @return Heap-allocated headers string, or NULL. Caller must free().
80 : */
81 : char *mail_client_fetch_headers(MailClient *c, const char *uid);
82 :
83 : /**
84 : * @brief Fetch the complete raw RFC 2822 message.
85 : * @return Heap-allocated message string, or NULL. Caller must free().
86 : */
87 : char *mail_client_fetch_body(MailClient *c, const char *uid);
88 :
89 : /**
90 : * @brief Fetch IMAP flags / Gmail label-derived flags as bitmask.
91 : * @return MSG_FLAG_* bitmask, or -1 on error.
92 : */
93 : int mail_client_fetch_flags(MailClient *c, const char *uid);
94 :
95 : /**
96 : * @brief Set or clear a flag on a message.
97 : *
98 : * IMAP: sends UID STORE.
99 : * Gmail: translates flag to label modify (e.g. \\Seen → remove UNREAD).
100 : *
101 : * @param flag IMAP-style flag name (e.g. "\\Seen", "\\Flagged", "$Done").
102 : * @param add 1 = add, 0 = remove.
103 : */
104 : int mail_client_set_flag(MailClient *c, const char *uid,
105 : const char *flag, int add);
106 :
107 : /**
108 : * @brief Mark a message as junk/spam.
109 : * IMAP: sets $Junk, clears $NotJunk.
110 : * Gmail: adds SPAM label, removes INBOX.
111 : */
112 : int mail_client_mark_junk(MailClient *c, const char *uid);
113 :
114 : /**
115 : * @brief Mark a message as not-junk (ham).
116 : * IMAP: sets $NotJunk, clears $Junk.
117 : * Gmail: removes SPAM label, adds INBOX.
118 : */
119 : int mail_client_mark_notjunk(MailClient *c, const char *uid);
120 :
121 : /**
122 : * @brief Move a message to Trash.
123 : *
124 : * IMAP: sets \\Deleted flag (caller may need EXPUNGE).
125 : * Gmail: compound trash operation (removes all labels, adds TRASH).
126 : */
127 : int mail_client_trash(MailClient *c, const char *uid);
128 :
129 : /**
130 : * @brief Move a message to a different IMAP folder (UID COPY + STORE \Deleted + EXPUNGE).
131 : * For Gmail accounts this is a no-op (label-based; use mail_client_modify_label instead).
132 : */
133 : int mail_client_move_to_folder(MailClient *c, const char *uid, const char *target_folder);
134 :
135 : /**
136 : * @brief Append/send a message.
137 : *
138 : * IMAP: APPEND to the specified folder.
139 : * Gmail: messages.send() via REST API (folder parameter ignored).
140 : */
141 : int mail_client_append(MailClient *c, const char *folder,
142 : const char *msg, size_t msg_len);
143 :
144 : /**
145 : * @brief List folders (IMAP) or labels (Gmail), returning both display names and IDs.
146 : * For IMAP, names and IDs are the same string.
147 : * @param names_out Heap-alloc'd array of display names. Caller frees each + array.
148 : * @param ids_out Heap-alloc'd array of IDs (parallel). Caller frees each + array.
149 : * @param count_out Number of entries.
150 : * @return 0 on success, -1 on error.
151 : */
152 : int mail_client_list_with_ids(MailClient *c, char ***names_out,
153 : char ***ids_out, int *count_out);
154 :
155 : /**
156 : * @brief Create a Gmail label. Fails if called on an IMAP account.
157 : * @param name Display name for the new label.
158 : * @param id_out Optional: receives new label ID. Caller frees.
159 : * @return 0 on success, -1 on error.
160 : */
161 : int mail_client_create_label(MailClient *c, const char *name, char **id_out);
162 :
163 : /**
164 : * @brief Delete a Gmail label. Fails if called on an IMAP account.
165 : * @param label_id Label ID (from list-labels).
166 : * @return 0 on success, -1 on error.
167 : */
168 : int mail_client_delete_label(MailClient *c, const char *label_id);
169 :
170 : /**
171 : * @brief Create an IMAP folder. Fails if called on a Gmail account.
172 : * @param name Folder path / name.
173 : * @return 0 on success, -1 on error.
174 : */
175 : int mail_client_create_folder(MailClient *c, const char *name);
176 :
177 : /**
178 : * @brief Delete an IMAP folder. Fails if called on a Gmail account.
179 : * @param name Folder path / name.
180 : * @return 0 on success, -1 on error.
181 : */
182 : int mail_client_delete_folder(MailClient *c, const char *name);
183 :
184 : /**
185 : * @brief Add or remove a label on a Gmail message (no-op for IMAP).
186 : *
187 : * @param label_id Gmail label ID (e.g. "INBOX", "STARRED", "Work").
188 : * @param add 1 = add label, 0 = remove label.
189 : * @return 0 on success, -1 on error. Always returns 0 for IMAP (no-op).
190 : */
191 : int mail_client_modify_label(MailClient *c, const char *uid,
192 : const char *label_id, int add);
193 :
194 : /**
195 : * @brief Install a progress callback for large downloads.
196 : */
197 : void mail_client_set_progress(MailClient *c, ImapProgressFn fn, void *ctx);
198 :
199 : /* ── Incremental sync (CONDSTORE / QRESYNC) ─────────────────────────────── */
200 :
201 : /**
202 : * @brief Extended SELECT with CONDSTORE/QRESYNC support.
203 : *
204 : * Selects the folder. For IMAP with CONDSTORE/QRESYNC the server is asked
205 : * for incremental sync information; for Gmail this is a no-op SELECT.
206 : *
207 : * Pass known_uidval=0 / known_modseq=0 on first run or after a forced
208 : * reconcile; the server will respond with full state.
209 : *
210 : * @param known_uidval Last saved UIDVALIDITY (0 = no saved state).
211 : * @param known_modseq Last saved HIGHESTMODSEQ (0 = no saved state).
212 : * @param res_out Filled on return. Caller must free res_out->vanished_uids.
213 : * @return 0 on success, -1 on error.
214 : */
215 : int mail_client_select_ext(MailClient *c, const char *folder,
216 : uint32_t known_uidval, uint64_t known_modseq,
217 : ImapSelectResult *res_out);
218 :
219 : /**
220 : * @brief Fetch flag updates for messages changed since @p modseq.
221 : *
222 : * IMAP: sends UID FETCH 1:* (UID FLAGS) (CHANGEDSINCE modseq).
223 : * Gmail: not supported — returns empty result (success).
224 : *
225 : * @param modseq Messages with modseq > this value are returned.
226 : * @param out Set to heap-alloc'd ImapFlagUpdate array. Caller frees.
227 : * @param count_out Number of entries.
228 : * @return 0 on success, -1 on error.
229 : */
230 : int mail_client_fetch_flags_changedsince(MailClient *c, uint64_t modseq,
231 : ImapFlagUpdate **out, int *count_out);
232 :
233 : /**
234 : * @brief Synchronise the account (Gmail: full/incremental sync).
235 : *
236 : * For IMAP this is a no-op (sync is handled by email_service_sync).
237 : * For Gmail this delegates to gmail_sync().
238 : */
239 : int mail_client_sync(MailClient *c);
240 :
241 : /* ── Inline RAII cleanup ─────────────────────────────────────��────── */
242 :
243 205 : static inline void mail_client_free_ptr(MailClient **p) {
244 205 : if (p && *p) {
245 180 : mail_client_free(*p);
246 180 : *p = NULL;
247 : }
248 205 : }
249 :
250 : #endif /* MAIL_CLIENT_H */
|