Line data Source code
1 : #ifndef IMAP_CLIENT_H
2 : #define IMAP_CLIENT_H
3 :
4 : #include <stddef.h>
5 :
6 : /**
7 : * @file imap_client.h
8 : * @brief Minimal IMAP4rev1 client over TLS (OpenSSL) or plain TCP.
9 : *
10 : * Supports the operations needed by email-cli:
11 : * - LIST "" "*" — enumerate folders
12 : * - SELECT <folder> — open a folder
13 : * - UID SEARCH <criteria> — find messages
14 : * - UID FETCH <uid> BODY.PEEK[] — fetch full message (no \Seen flag)
15 : * - UID FETCH <uid> BODY.PEEK[HEADER] — fetch headers only (no \Seen flag)
16 : *
17 : * Connection lifecycle:
18 : * 1. imap_connect() — TCP connect, TLS handshake, read server greeting, LOGIN
19 : * 2. imap_select() — SELECT a folder (required before SEARCH/FETCH)
20 : * 3. imap commands…
21 : * 4. imap_disconnect() — LOGOUT + close
22 : *
23 : * The URL format matches libcurl's IMAP convention:
24 : * imaps://hostname → TLS on port 993
25 : * imaps://hostname:port → TLS on custom port
26 : * imap://hostname → plain TCP on port 143
27 : * imap://hostname:port → plain TCP on custom port
28 : */
29 :
30 : typedef struct ImapClient ImapClient;
31 :
32 : /**
33 : * RAII cleanup helper — store pointer via RAII_IMAP so the client is
34 : * automatically disconnected when the variable goes out of scope.
35 : *
36 : * Usage:
37 : * RAII_IMAP ImapClient *c = imap_connect(...);
38 : */
39 : static inline void imap_disconnect_ptr(ImapClient **p);
40 : #define RAII_IMAP __attribute__((cleanup(imap_disconnect_ptr)))
41 :
42 : /**
43 : * @brief Connect to an IMAP server and authenticate.
44 : *
45 : * @param host_url Server URL, e.g. "imaps://imap.example.com" or
46 : * "imaps://imap.example.com:993".
47 : * @param user IMAP username.
48 : * @param pass IMAP password.
49 : * @param verify_tls 1 = verify TLS certificate (production); 0 = accept
50 : * self-signed (test environments only).
51 : * @return New client handle, or NULL on failure. Caller must call
52 : * imap_disconnect() when done (or use RAII_IMAP).
53 : */
54 : ImapClient *imap_connect(const char *host_url, const char *user,
55 : const char *pass, int verify_tls);
56 :
57 : /**
58 : * @brief Send LOGOUT and close the connection.
59 : * Safe to call with NULL.
60 : */
61 : void imap_disconnect(ImapClient *c);
62 :
63 : /**
64 : * @brief List all folders on the server (LIST "" "*").
65 : *
66 : * Folder names are decoded from IMAP Modified UTF-7 to UTF-8.
67 : *
68 : * @param c Connected client handle.
69 : * @param folders_out On success, set to a heap-allocated array of
70 : * heap-allocated folder name strings. Caller frees each
71 : * element and then the array itself.
72 : * @param count_out Number of folders returned.
73 : * @param sep_out If non-NULL, set to the hierarchy separator character
74 : * (e.g. '.' or '/'); '.' if no separator found.
75 : * @return 0 on success, -1 on error.
76 : */
77 : int imap_list(ImapClient *c, char ***folders_out, int *count_out, char *sep_out);
78 :
79 : /**
80 : * @brief SELECT a folder.
81 : *
82 : * Must be called before imap_uid_search(), imap_uid_fetch_headers(), or
83 : * imap_uid_fetch_body(). Calling again with a different folder re-selects.
84 : *
85 : * @param c Connected client handle.
86 : * @param folder Folder name in UTF-8 (will be encoded to IMAP Modified UTF-7
87 : * internally before sending to the server).
88 : * @return 0 on success, -1 on error.
89 : */
90 : int imap_select(ImapClient *c, const char *folder);
91 :
92 : /**
93 : * @brief UID SEARCH with caller-supplied criteria string.
94 : *
95 : * Typical criteria: "ALL", "UNSEEN".
96 : * Requires a folder to be selected first.
97 : *
98 : * @param c Connected client handle.
99 : * @param criteria IMAP search criteria string (e.g. "ALL", "UNSEEN").
100 : * @param uids_out On success, set to a heap-allocated array of UIDs.
101 : * Caller must free(). Set to NULL if count is 0.
102 : * @param count_out Number of UIDs returned (0 = empty result, not an error).
103 : * @return 0 on success (including empty result), -1 on error.
104 : */
105 : int imap_uid_search(ImapClient *c, const char *criteria,
106 : int **uids_out, int *count_out);
107 :
108 : /**
109 : * @brief Fetch just the RFC 2822 headers for a message (BODY.PEEK[HEADER]).
110 : *
111 : * Does NOT set the \Seen flag.
112 : * Requires a folder to be selected first.
113 : *
114 : * @param c Connected client handle.
115 : * @param uid IMAP UID of the message.
116 : * @return Heap-allocated NUL-terminated string containing the raw headers,
117 : * or NULL on error. Caller must free().
118 : */
119 : char *imap_uid_fetch_headers(ImapClient *c, int uid);
120 :
121 : /**
122 : * @brief Fetch the complete RFC 2822 message (BODY.PEEK[]).
123 : *
124 : * Does NOT set the \Seen flag.
125 : * Requires a folder to be selected first.
126 : *
127 : * @param c Connected client handle.
128 : * @param uid IMAP UID of the message.
129 : * @return Heap-allocated NUL-terminated string containing the full raw message,
130 : * or NULL on error. Caller must free().
131 : */
132 : char *imap_uid_fetch_body(ImapClient *c, int uid);
133 :
134 : /**
135 : * @brief Fetch the IMAP FLAGS for a single message (UID FETCH uid (UID FLAGS)).
136 : *
137 : * Returns a bitmask of MSG_FLAG_* from local_store.h, or -1 on error.
138 : * Does NOT affect \Seen.
139 : */
140 : int imap_uid_fetch_flags(ImapClient *c, int uid);
141 :
142 : /**
143 : * @brief Set or clear a single IMAP flag on a message (UID STORE).
144 : *
145 : * @param flag_name IMAP flag string, e.g. "\\Flagged" or "$Done".
146 : * @param add 1 = add flag (+FLAGS), 0 = remove flag (-FLAGS).
147 : * @return 0 on success, -1 on error.
148 : */
149 : int imap_uid_set_flag(ImapClient *c, int uid, const char *flag_name, int add);
150 :
151 : /**
152 : * @brief Append a message to an IMAP folder (IMAP APPEND command).
153 : *
154 : * Saves @p msg (a complete RFC 2822 message) to @p folder on the server,
155 : * flagged as \\Seen. Used to store a copy of sent messages in the Sent folder.
156 : *
157 : * @param c Connected and authenticated IMAP client.
158 : * @param folder Destination folder name (e.g. "Sent").
159 : * @param msg Raw RFC 2822 message bytes.
160 : * @param msg_len Length of @p msg in bytes.
161 : * @return 0 on success, -1 on error.
162 : */
163 : int imap_append(ImapClient *c, const char *folder,
164 : const char *msg, size_t msg_len);
165 :
166 : /**
167 : * @brief Progress callback type for large literal downloads.
168 : *
169 : * @param received Bytes received so far.
170 : * @param total Total bytes expected (literal size announced by server).
171 : * @param ctx User-supplied context pointer passed to imap_set_progress().
172 : */
173 : typedef void (*ImapProgressFn)(size_t received, size_t total, void *ctx);
174 :
175 : /**
176 : * @brief Install (or clear) a download-progress callback on the client.
177 : *
178 : * The callback is invoked periodically while reading large IMAP literals
179 : * (bodies >= ~100 KB). Pass fn=NULL to disable.
180 : * The callback is NOT cleared automatically after a fetch; call
181 : * imap_set_progress(c, NULL, NULL) when no longer needed.
182 : */
183 : void imap_set_progress(ImapClient *c, ImapProgressFn fn, void *ctx);
184 :
185 : /* ── Inline RAII cleanup ─────────────────────────────────────────────── */
186 :
187 51 : static inline void imap_disconnect_ptr(ImapClient **p) {
188 51 : if (p && *p) {
189 51 : imap_disconnect(*p);
190 51 : *p = NULL;
191 : }
192 51 : }
193 :
194 : #endif /* IMAP_CLIENT_H */
|