1 #ifndef MAIL_INDEX_H
2 #define MAIL_INDEX_H
3 
4 #include "file-lock.h"
5 #include "fsync-mode.h"
6 #include "guid.h"
7 #include "mail-types.h"
8 #include "seq-range-array.h"
9 
10 #define MAIL_INDEX_MAJOR_VERSION 7
11 #define MAIL_INDEX_MINOR_VERSION 3
12 
13 #define MAIL_INDEX_HEADER_MIN_SIZE 120
14 
15 /* Log a warning when transaction log has been locked for this many seconds.
16    This lock is held also between mail_index_sync_begin()..commit(). */
17 #define MAIL_TRANSACTION_LOG_LOCK_WARN_SECS 30
18 
19 enum mail_index_open_flags {
20 	/* Create index if it doesn't exist */
21 	MAIL_INDEX_OPEN_FLAG_CREATE		= 0x01,
22 	/* Don't try to mmap() index files */
23 	MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE	= 0x04,
24 	/* Rely on O_EXCL when creating dotlocks */
25 	MAIL_INDEX_OPEN_FLAG_DOTLOCK_USE_EXCL	= 0x10,
26 	/* Flush NFS attr/data/write cache when necessary */
27 	MAIL_INDEX_OPEN_FLAG_NFS_FLUSH		= 0x40,
28 	/* Open the index read-only */
29 	MAIL_INDEX_OPEN_FLAG_READONLY		= 0x80,
30 	/* Create backups of dovecot.index files once in a while */
31 	MAIL_INDEX_OPEN_FLAG_KEEP_BACKUPS	= 0x100,
32 	/* If we run out of disk space, fail modifications instead of moving
33 	   indexes to memory. */
34 	MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY	= 0x200,
35 	/* We're only going to save new messages to the index.
36 	   Avoid unnecessary reads. */
37 	MAIL_INDEX_OPEN_FLAG_SAVEONLY		= 0x400,
38 	/* Enable debug logging */
39 	MAIL_INDEX_OPEN_FLAG_DEBUG		= 0x800,
40 	/* MAIL_INDEX_MAIL_FLAG_DIRTY can be used as a backend-specific flag.
41 	   All special handling of the flag is disabled by this. */
42 	MAIL_INDEX_OPEN_FLAG_NO_DIRTY		= 0x1000,
43 };
44 
45 enum mail_index_header_compat_flags {
46 	/* All fields in these index files are in little-endian format.
47 	   If the current CPU endianess doesn't match this, the indexes can't
48 	   be used. There is currently no support to translate endianess. */
49 	MAIL_INDEX_COMPAT_LITTLE_ENDIAN		= 0x01
50 };
51 
52 enum mail_index_header_flag {
53 	/* mail_index_mark_corrupted() was just called by this process.
54 	   Reopen or recreate it. This flag is never actually written to
55 	   disk. */
56 	MAIL_INDEX_HDR_FLAG_CORRUPTED		= 0x0001,
57 	/* There are messages with MAIL_INDEX_MAIL_FLAG_DIRTY flag. */
58 	MAIL_INDEX_HDR_FLAG_HAVE_DIRTY		= 0x0002,
59 	/* Index has been fsck'd. The caller may want to resync the index
60 	   to make sure it's valid and drop this flag. */
61 	MAIL_INDEX_HDR_FLAG_FSCKD		= 0x0004,
62 };
63 
64 enum mail_index_mail_flags {
65 	/* This flag used to contain MAIL_RECENT flag, but is always zero
66 	   with the current index file format. */
67 	MAIL_INDEX_MAIL_FLAG_UNUSED		= 0x20,
68 	/* For private use by backend. Replacing flags doesn't change this. */
69 	MAIL_INDEX_MAIL_FLAG_BACKEND		= 0x40,
70 	/* Message flags haven't been written to backend. If
71 	   MAIL_INDEX_OPEN_FLAG_NO_DIRTY is set, this is treated as a
72 	   backend-specific flag with no special internal handling. */
73 	MAIL_INDEX_MAIL_FLAG_DIRTY		= 0x80,
74 
75 	/* Force updating this message's modseq via a flag update record.
76 	   Note that this flag isn't saved to disk. */
77 	MAIL_INDEX_MAIL_FLAG_UPDATE_MODSEQ	= 0x100
78 };
79 
80 #define MAIL_INDEX_FLAGS_MASK \
81 	(MAIL_ANSWERED | MAIL_FLAGGED | MAIL_DELETED | MAIL_SEEN | MAIL_DRAFT)
82 
83 struct mail_index_header {
84 	/* Major version is increased only when you can't have backwards
85 	   compatibility. If the field doesn't match MAIL_INDEX_MAJOR_VERSION,
86 	   don't even try to read it. */
87 	uint8_t major_version;
88 	/* Minor version is increased when the file format changes in a
89 	   backwards compatible way. If the field is smaller than
90 	   MAIL_INDEX_MINOR_VERSION, upgrade the file format and update the
91 	   minor_version field as well. If minor_version is higher than
92 	   MAIL_INDEX_MINOR_VERSION, leave it as it is. It likely means that a
93 	   new Dovecot version is currently being upgraded to, but the file was
94 	   still accessed by an old version. */
95 	uint8_t minor_version;
96 
97 	/* sizeof(struct mail_index_header) when creating a new index. If the
98 	   header is smaller, fill the missing fields with 0. If the header is
99 	   larger, preserve the size and unknown fields. */
100 	uint16_t base_header_size;
101 	uint32_t header_size; /* base + extended header size */
102 	/* sizeof(struct mail_index_record) + extensions */
103 	uint32_t record_size;
104 
105 	uint8_t compat_flags; /* enum mail_index_header_compat_flags */
106 	uint8_t unused[3];
107 
108 	/* Unique index file ID. Initialized with the current UNIX timestamp.
109 	   This is used to make sure that the main index, transaction log and
110 	   cache file are all part of the same index. */
111 	uint32_t indexid;
112 	uint32_t flags; /* enum mail_index_header_flag */
113 
114 	/* IMAP UIDVALIDITY. Initially can be 0, but must be non-0 after the
115 	   first mailbox sync. The UIDVALIDITY shouldn't normally change after
116 	   the mailbox is created. */
117 	uint32_t uid_validity;
118 	/* UID for the next saved message (must not be lower than this). This
119 	   value can only increase. */
120 	uint32_t next_uid;
121 
122 	/* Number of messages in the index */
123 	uint32_t messages_count;
124 	uint32_t unused_old_recent_messages_count;
125 	/* Number of messages with MAIL_SEEN flag */
126 	uint32_t seen_messages_count;
127 	/* Number of messages with MAIL_DELETED flag */
128 	uint32_t deleted_messages_count;
129 
130 	/* The specified UID and all mails after it have MAIL_RECENT flag */
131 	uint32_t first_recent_uid;
132 	/* There are no UIDs lower than this without MAIL_SEEN flag. There are
133 	   no guarantees whether this UID has MAIL_SEEN flag, or whether the it
134 	   even exists. Used to optimize finding the first unseen message. */
135 	uint32_t first_unseen_uid_lowwater;
136 	/* Similarly to above, used to optimize finding the first deleted
137 	   message. */
138 	uint32_t first_deleted_uid_lowwater;
139 
140 	/* The index is synced up to this log_file_seq and
141 	   log_file_head_offset. However, non-external transaction records
142 	   between tail_offset..head_offset haven't been synced to the
143 	   mailbox yet. For example there may be pending expunges or flag
144 	   changes, which will be synced on the next mail_index_sync_*()
145 	   calls. */
146 	uint32_t log_file_seq;
147 	uint32_t log_file_tail_offset;
148 	uint32_t log_file_head_offset;
149 
150 	uint32_t unused_old_sync_size_part1;
151 	/* Timestamp of when .log was rotated into .log.2. This can be used to
152 	   optimize checking when it's time to unlink it without stat()ing it.
153 	   0 = unknown, -1 = .log.2 doesn't exists. */
154 	uint32_t log2_rotate_time;
155 	/* Timestamp when the mailbox backend-specific code last checked
156 	   whether there are old temporary files (left by crashes) that should
157 	   be deleted. 0 = unknown. */
158 	uint32_t last_temp_file_scan;
159 
160 	/* UNIX timestamp to the beginning of the day (in server's local
161 	   timezone) when new messages were last added to the index file. */
162 	uint32_t day_stamp;
163 	/* These fields are updated when day_stamp < today. The [0..6] are
164 	   first moved to [1..7], then [0] is set to the first appended UID. So
165 	   they contain the first UID of the day for last 8 days when messages
166 	   were appended.
167 
168 	   These are used by cache purging to decide when to drop
169 	   MAIL_CACHE_DECISION_TEMP fields. */
170 	uint32_t day_first_uid[8];
171 };
172 
173 #define MAIL_INDEX_RECORD_MIN_SIZE (sizeof(uint32_t) + sizeof(uint8_t))
174 struct mail_index_record {
175 	uint32_t uid;
176 	uint8_t flags; /* enum mail_flags | enum mail_index_mail_flags */
177 };
178 
179 struct mail_keywords {
180 	struct mail_index *index;
181 	unsigned int count;
182 	int refcount;
183 
184         /* variable sized list of keyword indexes */
185 	unsigned int idx[FLEXIBLE_ARRAY_MEMBER];
186 };
187 
188 enum mail_index_transaction_flags {
189 	/* If transaction is marked as hidden, the changes are marked with
190 	   hidden=TRUE when the view is synchronized. */
191 	MAIL_INDEX_TRANSACTION_FLAG_HIDE		= 0x01,
192 	/* External transactions describe changes to mailbox that have already
193 	   happened. */
194 	MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL		= 0x02,
195 	/* Don't add flag updates unless they actually change something.
196 	   This is reliable only when syncing, otherwise someone else might
197 	   have already committed a transaction that had changed the flags. */
198 	MAIL_INDEX_TRANSACTION_FLAG_AVOID_FLAG_UPDATES	= 0x04,
199 	/* fsync() this transaction (unless fsyncs are disabled) */
200 	MAIL_INDEX_TRANSACTION_FLAG_FSYNC		= 0x08,
201 	/* Sync transaction describes changes to mailbox that already happened
202 	   to another mailbox with whom we're syncing with (dsync) */
203 	MAIL_INDEX_TRANSACTION_FLAG_SYNC		= 0x10
204 };
205 
206 enum mail_index_sync_type {
207 	MAIL_INDEX_SYNC_TYPE_EXPUNGE		= 0x02,
208 	MAIL_INDEX_SYNC_TYPE_FLAGS		= 0x04,
209 	MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD	= 0x08,
210 	MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE	= 0x10
211 };
212 
213 enum mail_index_fsync_mask {
214 	MAIL_INDEX_FSYNC_MASK_APPENDS	= 0x01,
215 	MAIL_INDEX_FSYNC_MASK_EXPUNGES	= 0x02,
216 	MAIL_INDEX_FSYNC_MASK_FLAGS	= 0x04,
217 	MAIL_INDEX_FSYNC_MASK_KEYWORDS	= 0x08
218 };
219 
220 enum mail_index_sync_flags {
221 	/* Resync all dirty messages' flags. */
222 	MAIL_INDEX_SYNC_FLAG_FLUSH_DIRTY	= 0x01,
223 	/* Drop recent flags from all messages */
224 	MAIL_INDEX_SYNC_FLAG_DROP_RECENT	= 0x02,
225 	/* Create the transaction with AVOID_FLAG_UPDATES flag */
226 	MAIL_INDEX_SYNC_FLAG_AVOID_FLAG_UPDATES	= 0x04,
227 	/* If there are no new transactions and nothing else to do,
228 	   return 0 in mail_index_sync_begin() */
229 	MAIL_INDEX_SYNC_FLAG_REQUIRE_CHANGES	= 0x08,
230 	/* Create the transaction with FSYNC flag */
231 	MAIL_INDEX_SYNC_FLAG_FSYNC		= 0x10,
232 	/* If we see "delete index" request transaction, finish it.
233 	   This flag also allows committing more changes to a deleted index. */
234 	MAIL_INDEX_SYNC_FLAG_DELETING_INDEX	= 0x20,
235 	/* Same as MAIL_INDEX_SYNC_FLAG_DELETING_INDEX, but finish index
236 	   deletion only once and fail the rest (= avoid race conditions when
237 	   multiple processes try to mark the index deleted) */
238 	MAIL_INDEX_SYNC_FLAG_TRY_DELETING_INDEX	= 0x40,
239 	/* Update header's tail_offset to head_offset, even if it's the only
240 	   thing we do and there's no strict need for it. */
241 	MAIL_INDEX_SYNC_FLAG_UPDATE_TAIL_OFFSET	= 0x80
242 };
243 
244 enum mail_index_view_sync_flags {
245 	/* Don't sync expunges */
246 	MAIL_INDEX_VIEW_SYNC_FLAG_NOEXPUNGES		= 0x01,
247 	/* Make sure view isn't inconsistent after syncing. This also means
248 	   that you don't care about view_sync_next()'s output, so it won't
249 	   return anything. */
250 	MAIL_INDEX_VIEW_SYNC_FLAG_FIX_INCONSISTENT	= 0x02,
251 	/* Indicate that this is a secondary view, this can be used to indicate
252 	   that inconsistencies can be expected and if found should be fixed
253 	   by fully syncing. */
254 	MAIL_INDEX_VIEW_SYNC_FLAG_2ND_INDEX		= 0x04,
255 };
256 
257 struct mail_index_sync_rec {
258 	uint32_t uid1, uid2;
259 	enum mail_index_sync_type type;
260 
261 	/* MAIL_INDEX_SYNC_TYPE_FLAGS: */
262 	uint8_t add_flags;
263 	uint8_t remove_flags;
264 
265 	/* MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD, .._REMOVE: */
266 	unsigned int keyword_idx;
267 
268 	/* MAIL_INDEX_SYNC_TYPE_EXPUNGE: */
269 	guid_128_t guid_128;
270 };
271 
272 enum mail_index_view_sync_type {
273 	/* Flags or keywords changed */
274 	MAIL_INDEX_VIEW_SYNC_TYPE_FLAGS		= 0x01,
275 	MAIL_INDEX_VIEW_SYNC_TYPE_MODSEQ	= 0x02
276 };
277 
278 struct mail_index_view_sync_rec {
279 	uint32_t uid1, uid2;
280 	enum mail_index_view_sync_type type;
281 
282 	/* TRUE if this was a hidden transaction. */
283 	bool hidden:1;
284 };
285 
286 enum mail_index_transaction_change {
287 	MAIL_INDEX_TRANSACTION_CHANGE_APPEND	= BIT(0),
288 	MAIL_INDEX_TRANSACTION_CHANGE_EXPUNGE	= BIT(1),
289 	MAIL_INDEX_TRANSACTION_CHANGE_FLAGS	= BIT(2),
290 	MAIL_INDEX_TRANSACTION_CHANGE_KEYWORDS	= BIT(3),
291 	MAIL_INDEX_TRANSACTION_CHANGE_MODSEQ	= BIT(4),
292 	MAIL_INDEX_TRANSACTION_CHANGE_ATTRIBUTE	= BIT(5),
293 
294 	MAIL_INDEX_TRANSACTION_CHANGE_OTHERS	= BIT(30),
295 };
296 
297 struct mail_index_transaction_commit_result {
298 	/* seq/offset points to end of transaction */
299 	uint32_t log_file_seq;
300 	uoff_t log_file_offset;
301 	/* number of bytes in the written transaction.
302 	   all of it was written to the same file. */
303 	uoff_t commit_size;
304 
305 	enum mail_index_transaction_change changes_mask;
306 	unsigned int ignored_modseq_changes;
307 };
308 
309 struct mail_index_base_optimization_settings {
310 	/* Rewrite the index when the number of bytes that needs to be read
311 	   from the .log on refresh is between these min/max values. */
312 	uoff_t rewrite_min_log_bytes;
313 	uoff_t rewrite_max_log_bytes;
314 };
315 
316 struct mail_index_log_optimization_settings {
317 	/* Rotate transaction log after it's a) min_size or larger and it was
318 	   created at least min_age_secs or b) larger than max_size. */
319 	uoff_t min_size;
320 	uoff_t max_size;
321 	unsigned int min_age_secs;
322 
323 	/* Delete .log.2 when it's older than log2_stale_secs. Don't be too
324 	   eager, because older files are useful for QRESYNC and dsync. */
325 	unsigned int log2_max_age_secs;
326 };
327 
328 struct mail_index_cache_optimization_settings {
329 	/* Drop fields that haven't been accessed for n seconds */
330 	unsigned int unaccessed_field_drop_secs;
331 	/* If cache record becomes larger than this, don't add it. */
332 	unsigned int record_max_size;
333 
334 	/* Maximum size for the cache file. Internally the limit is 1 GB. */
335 	uoff_t max_size;
336 	/* Never purge the file if it's smaller than this */
337 	uoff_t purge_min_size;
338 	/* Purge the file when n% of records are deleted */
339 	unsigned int purge_delete_percentage;
340 	/* Purge the file when n% of rows contain continued rows.
341 	   For example 200% means that the record has 2 continued rows, i.e.
342 	   it exists in 3 separate segments in the cache file. */
343 	unsigned int purge_continued_percentage;
344 	/* Purge the file when we need to follow more than n next_offsets to
345 	   find the latest cache header. */
346 	unsigned int purge_header_continue_count;
347 };
348 
349 struct mail_index_optimization_settings {
350 	struct mail_index_base_optimization_settings index;
351 	struct mail_index_log_optimization_settings log;
352 	struct mail_index_cache_optimization_settings cache;
353 };
354 
355 struct mail_index;
356 struct mail_index_map;
357 struct mail_index_view;
358 struct mail_index_transaction;
359 struct mail_index_sync_ctx;
360 struct mail_index_view_sync_ctx;
361 
362 struct mail_index *mail_index_alloc(struct event *parent_event,
363 				    const char *dir, const char *prefix);
364 void mail_index_free(struct mail_index **index);
365 
366 /* Change .cache file's directory. */
367 void mail_index_set_cache_dir(struct mail_index *index, const char *dir);
368 /* Specify how often to do fsyncs. If mode is FSYNC_MODE_OPTIMIZED, the mask
369    can be used to specify which transaction types to fsync. */
370 void mail_index_set_fsync_mode(struct mail_index *index, enum fsync_mode mode,
371 			       enum mail_index_fsync_mask mask);
372 /* Try to set the index's permissions based on its index directory. Returns
373    TRUE if successful (directory existed), FALSE if mail_index_set_permissions()
374    should be called. */
375 bool mail_index_use_existing_permissions(struct mail_index *index);
376 void mail_index_set_permissions(struct mail_index *index,
377 				mode_t mode, gid_t gid, const char *gid_origin);
378 /* Set locking method and maximum time to wait for a lock
379    (UINT_MAX = default). */
380 void mail_index_set_lock_method(struct mail_index *index,
381 				enum file_lock_method lock_method,
382 				unsigned int max_timeout_secs);
383 /* Override the default optimization-related settings. Anything set to 0 will
384    use the default. */
385 void mail_index_set_optimization_settings(struct mail_index *index,
386 	const struct mail_index_optimization_settings *set);
387 /* When creating a new index file or reseting an existing one, add the given
388    extension header data immediately to it. */
389 void mail_index_set_ext_init_data(struct mail_index *index, uint32_t ext_id,
390 				  const void *data, size_t size);
391 
392 /* Open index. Returns 1 if ok, 0 if index doesn't exist and CREATE flags
393    wasn't given, -1 if error. */
394 int mail_index_open(struct mail_index *index, enum mail_index_open_flags flags);
395 /* Open or create index. Returns 0 if ok, -1 if error. */
396 int mail_index_open_or_create(struct mail_index *index,
397 			      enum mail_index_open_flags flags);
398 void mail_index_close(struct mail_index *index);
399 /* unlink() all the index files. */
400 int mail_index_unlink(struct mail_index *index);
401 
402 /* Returns TRUE if index is currently in memory. */
403 bool mail_index_is_in_memory(struct mail_index *index);
404 /* Move the index into memory. Returns 0 if ok, -1 if error occurred. */
405 int mail_index_move_to_memory(struct mail_index *index);
406 
407 struct mail_cache *mail_index_get_cache(struct mail_index *index);
408 
409 /* Refresh index so mail_index_lookup*() will return latest values. Note that
410    immediately after this call there may already be changes, so if you need to
411    rely on validity of the returned values, use some external locking for it. */
412 int ATTR_NOWARN_UNUSED_RESULT
413 mail_index_refresh(struct mail_index *index);
414 
415 /* View can be used to look into index. Sequence numbers inside view change
416    only when you synchronize it. The view acquires required locks
417    automatically, but you'll have to drop them manually. */
418 struct mail_index_view *
419 mail_index_view_open(struct mail_index *index,
420 		     const char *source_filename, unsigned int source_linenum);
421 #define mail_index_view_open(index) \
422 	mail_index_view_open(index, __FILE__, __LINE__)
423 void mail_index_view_close(struct mail_index_view **view);
424 
425 /* Returns the index for given view. */
426 struct mail_index *mail_index_view_get_index(struct mail_index_view *view);
427 /* Returns number of mails in view. */
428 uint32_t mail_index_view_get_messages_count(struct mail_index_view *view);
429 /* Returns TRUE if we lost track of changes for some reason. */
430 bool mail_index_view_is_inconsistent(struct mail_index_view *view);
431 /* Returns TRUE if there are open transactions open for the view. */
432 bool mail_index_view_have_transactions(struct mail_index_view *view);
433 
434 /* Transaction has to be opened to be able to modify index. You can have
435    multiple transactions open simultaneously. Committed transactions won't
436    show up until you've synchronized the view. Expunges won't show up until
437    you've synchronized the mailbox (mail_index_sync_begin). */
438 struct mail_index_transaction *
439 mail_index_transaction_begin(struct mail_index_view *view,
440 			     enum mail_index_transaction_flags flags);
441 int mail_index_transaction_commit(struct mail_index_transaction **t);
442 int mail_index_transaction_commit_full(struct mail_index_transaction **t,
443 				       struct mail_index_transaction_commit_result *result_r);
444 void mail_index_transaction_rollback(struct mail_index_transaction **t);
445 /* Discard all changes in the transaction. */
446 void mail_index_transaction_reset(struct mail_index_transaction *t);
447 /* When committing transaction, drop flag/keyword updates for messages whose
448    mdoseq is larger than max_modseq. Save those messages' sequences to the
449    given array. */
450 void mail_index_transaction_set_max_modseq(struct mail_index_transaction *t,
451 					   uint64_t max_modseq,
452 					   ARRAY_TYPE(seq_range) *seqs);
453 
454 /* Returns the view transaction was created for. */
455 struct mail_index_view *
456 mail_index_transaction_get_view(struct mail_index_transaction *t);
457 /* Returns TRUE if the given sequence is being expunged in this transaction. */
458 bool mail_index_transaction_is_expunged(struct mail_index_transaction *t,
459 					uint32_t seq);
460 
461 /* Returns a view containing the mailbox state after changes in transaction
462    are applied. The view can still be used after transaction has been
463    committed. */
464 struct mail_index_view *
465 mail_index_transaction_open_updated_view(struct mail_index_transaction *t);
466 
467 /* Begin synchronizing mailbox with index file. Returns 1 if ok,
468    0 if MAIL_INDEX_SYNC_FLAG_REQUIRE_CHANGES is set and there's nothing to
469    sync, -1 if error.
470 
471    mail_index_sync_next() returns all changes from previously committed
472    transactions which haven't yet been committed to the actual mailbox.
473    They're returned in ascending order and they never overlap (if we add more
474    sync types, then they might). You must go through all of them and update
475    the mailbox accordingly.
476 
477    Changes done to the returned transaction are expected to describe the
478    mailbox's current state.
479 
480    The returned view already contains all the changes (except expunge
481    requests). After applying sync records on top of backend flags they should
482    match flags in the view. If they don't, there have been external changes.
483 
484    Returned expunges are treated as expunge requests. They're not really
485    removed from the index until you mark them expunged to the returned
486    transaction. If it's not possible to expunge the message (e.g. permission
487    denied), simply don't mark them expunged.
488 
489    Returned sequence numbers describe the mailbox state at the beginning of
490    synchronization, ie. expunges don't affect them. */
491 int mail_index_sync_begin(struct mail_index *index,
492 			  struct mail_index_sync_ctx **ctx_r,
493 			  struct mail_index_view **view_r,
494 			  struct mail_index_transaction **trans_r,
495 			  enum mail_index_sync_flags flags);
496 /* Like mail_index_sync_begin(), but returns 1 if OK and if index is already
497    synchronized up to the given log_file_seq+offset, the synchronization isn't
498    started and this function returns 0. This should be done when you wish to
499    sync your committed transaction instead of doing a full mailbox
500    synchronization. */
501 int mail_index_sync_begin_to(struct mail_index *index,
502 			     struct mail_index_sync_ctx **ctx_r,
503 			     struct mail_index_view **view_r,
504 			     struct mail_index_transaction **trans_r,
505 			     uint32_t log_file_seq, uoff_t log_file_offset,
506 			     enum mail_index_sync_flags flags);
507 /* Returns TRUE if it currently looks like syncing would return changes. */
508 bool mail_index_sync_have_any(struct mail_index *index,
509 			      enum mail_index_sync_flags flags);
510 /* Returns TRUE if it currently looks like syncing would return expunges. */
511 bool mail_index_sync_have_any_expunges(struct mail_index *index);
512 /* Returns the log file seq+offsets for the area which this sync is handling. */
513 void mail_index_sync_get_offsets(struct mail_index_sync_ctx *ctx,
514 				 uint32_t *seq1_r, uoff_t *offset1_r,
515 				 uint32_t *seq2_r, uoff_t *offset2_r);
516 /* Returns -1 if error, 0 if sync is finished, 1 if record was filled. */
517 bool mail_index_sync_next(struct mail_index_sync_ctx *ctx,
518 			  struct mail_index_sync_rec *sync_rec);
519 /* Returns TRUE if there's more to sync. */
520 bool mail_index_sync_have_more(struct mail_index_sync_ctx *ctx);
521 /* Returns TRUE if sync has any expunges to handle. */
522 bool mail_index_sync_has_expunges(struct mail_index_sync_ctx *ctx);
523 /* Reset syncing to initial state after mail_index_sync_begin(), so you can
524    go through all the sync records again with mail_index_sync_next(). */
525 void mail_index_sync_reset(struct mail_index_sync_ctx *ctx);
526 /* Update result when refreshing index at the end of sync. */
527 void mail_index_sync_set_commit_result(struct mail_index_sync_ctx *ctx,
528 				       struct mail_index_transaction_commit_result *result);
529 /* Don't log a warning even if syncing took over
530    MAIL_TRANSACTION_LOG_LOCK_WARN_SECS seconds. Usually this is called because
531    the caller itself already logged a warning about it. */
532 void mail_index_sync_no_warning(struct mail_index_sync_ctx *ctx);
533 /* If a warning is logged because syncing took over
534    MAIL_TRANSACTION_LOG_LOCK_WARN_SECS seconds, log this as the reason for the
535    syncing. */
536 void mail_index_sync_set_reason(struct mail_index_sync_ctx *ctx,
537 				const char *reason);
538 /* Commit synchronization by writing all changes to mail index file. */
539 int mail_index_sync_commit(struct mail_index_sync_ctx **ctx);
540 /* Rollback synchronization - none of the changes listed by sync_next() are
541    actually written to index file. */
542 void mail_index_sync_rollback(struct mail_index_sync_ctx **ctx);
543 
544 /* Lock the index exclusively. This is the same locking as what happens when
545    syncing the index. It's not necessary to normally call this function, unless
546    doing something special such as rebuilding the index outside syncing.
547    Returns 0 on success, -1 if locking failed for any reason. */
548 int mail_index_lock_sync(struct mail_index *index, const char *lock_reason);
549 /* Unlock the locked index. The index must have been locked previously with
550    mail_index_lock_sync(). If the lock had been kept for excessively long,
551    a warning is logged with the long_lock_reason. */
552 void mail_index_unlock(struct mail_index *index, const char *long_lock_reason);
553 /* Returns TRUE if index is currently exclusively locked. */
554 bool mail_index_is_locked(struct mail_index *index);
555 
556 /* Mark index file corrupted in memory and delete it from disk.
557    Invalidates all views. This should be called only for index files that can
558    safely be recreated without any data loss. */
559 void mail_index_mark_corrupted(struct mail_index *index);
560 /* Check and fix any found problems. Returns -1 if we couldn't lock for sync,
561    0 if everything went ok. */
562 int mail_index_fsck(struct mail_index *index);
563 /* Returns TRUE if mail_index_fsck() has been called since the last
564    mail_index_reset_fscked() call. */
565 bool mail_index_reset_fscked(struct mail_index *index);
566 
567 /* Synchronize changes in view. You have to go through all records, or view
568    will be marked inconsistent. Only sync_mask type records are
569    synchronized. */
570 struct mail_index_view_sync_ctx *
571 mail_index_view_sync_begin(struct mail_index_view *view,
572 			   enum mail_index_view_sync_flags flags);
573 bool mail_index_view_sync_next(struct mail_index_view_sync_ctx *ctx,
574 			       struct mail_index_view_sync_rec *sync_rec);
575 void
576 mail_index_view_sync_get_expunges(struct mail_index_view_sync_ctx *ctx,
577 				  const ARRAY_TYPE(seq_range) **expunges_r);
578 int mail_index_view_sync_commit(struct mail_index_view_sync_ctx **ctx,
579 				bool *delayed_expunges_r);
580 
581 /* Returns the index header. */
582 const struct mail_index_header *
583 mail_index_get_header(struct mail_index_view *view);
584 /* Returns the wanted message record. */
585 const struct mail_index_record *
586 mail_index_lookup(struct mail_index_view *view, uint32_t seq);
587 const struct mail_index_record *
588 mail_index_lookup_full(struct mail_index_view *view, uint32_t seq,
589 		       struct mail_index_map **map_r);
590 /* Returns TRUE if the given message has already been expunged from index. */
591 bool mail_index_is_expunged(struct mail_index_view *view, uint32_t seq);
592 /* Note that returned keyword indexes aren't sorted. */
593 void mail_index_lookup_keywords(struct mail_index_view *view, uint32_t seq,
594 				ARRAY_TYPE(keyword_indexes) *keyword_idx);
595 /* Return keywords from given map. */
596 void mail_index_map_lookup_keywords(struct mail_index_map *map, uint32_t seq,
597 				    ARRAY_TYPE(keyword_indexes) *keyword_idx);
598 /* mail_index_lookup[_keywords]() returns the latest flag changes.
599    This function instead attempts to return the flags and keywords done by the
600    last view sync. */
601 void mail_index_lookup_view_flags(struct mail_index_view *view, uint32_t seq,
602 				  enum mail_flags *flags_r,
603 				  ARRAY_TYPE(keyword_indexes) *keyword_idx);
604 /* Returns the UID for given message. May be slightly faster than
605    mail_index_lookup()->uid. */
606 void mail_index_lookup_uid(struct mail_index_view *view, uint32_t seq,
607 			   uint32_t *uid_r);
608 /* Convert UID range to sequence range. If no UIDs are found, returns FALSE and
609    sequences are set to 0. Note that any of the returned sequences may have
610    been expunged already. */
611 bool mail_index_lookup_seq_range(struct mail_index_view *view,
612 				 uint32_t first_uid, uint32_t last_uid,
613 				 uint32_t *first_seq_r, uint32_t *last_seq_r);
614 bool mail_index_lookup_seq(struct mail_index_view *view,
615 			   uint32_t uid, uint32_t *seq_r);
616 /* Find first mail with (mail->flags & flags_mask) == flags. Useful mostly for
617    taking advantage of lowwater-fields in headers. */
618 void mail_index_lookup_first(struct mail_index_view *view,
619 			     enum mail_flags flags, uint8_t flags_mask,
620 			     uint32_t *seq_r);
621 
622 /* Append a new record to index. */
623 void mail_index_append(struct mail_index_transaction *t, uint32_t uid,
624 		       uint32_t *seq_r);
625 /* Assign new UIDs for mails with uid=0 or uid<min_allowed_uid. All the new
626    UIDs are >= first_new_uid, an also higher than the highest seen uid (i.e. it
627    doesn't try to fill UID gaps). Assumes that mailbox is locked in a way that
628    UIDs can be safely assigned. Returns UIDs for all assigned messages, in
629    their sequence order (so UIDs are not necessary ascending). */
630 void mail_index_append_finish_uids_full(struct mail_index_transaction *t,
631 					uint32_t min_allowed_uid,
632 					uint32_t first_new_uid,
633 					ARRAY_TYPE(seq_range) *uids_r);
634 /* Call mail_index_append_finish_uids_full() with first_uid used for both
635    min_allowed_uid and first_new_uid. */
636 void mail_index_append_finish_uids(struct mail_index_transaction *t,
637 				   uint32_t first_uid,
638 				   ARRAY_TYPE(seq_range) *uids_r);
639 /* Expunge record from index. Note that this doesn't affect sequence numbers
640    until transaction is committed and mailbox is synced. */
641 void mail_index_expunge(struct mail_index_transaction *t, uint32_t seq);
642 /* Like mail_index_expunge(), but also write message GUID to transaction log. */
643 void mail_index_expunge_guid(struct mail_index_transaction *t, uint32_t seq,
644 			     const guid_128_t guid_128);
645 /* Revert all changes done in this transaction to the given existing mail. */
646 void mail_index_revert_changes(struct mail_index_transaction *t, uint32_t seq);
647 /* Update flags in index. */
648 void mail_index_update_flags(struct mail_index_transaction *t, uint32_t seq,
649 			     enum modify_type modify_type,
650 			     enum mail_flags flags);
651 void mail_index_update_flags_range(struct mail_index_transaction *t,
652 				   uint32_t seq1, uint32_t seq2,
653 				   enum modify_type modify_type,
654 				   enum mail_flags flags);
655 /* Specified attribute's value was changed. This is just a notification so the
656    change gets assigned its own modseq and any log readers can find out about
657    this change. */
658 void mail_index_attribute_set(struct mail_index_transaction *t,
659 			      bool pvt, const char *key,
660 			      time_t timestamp, uint32_t value_len);
661 /* Attribute was deleted. */
662 void mail_index_attribute_unset(struct mail_index_transaction *t,
663 				bool pvt, const char *key, time_t timestamp);
664 /* Update message's modseq to be at least min_modseq. */
665 void mail_index_update_modseq(struct mail_index_transaction *t, uint32_t seq,
666 			      uint64_t min_modseq);
667 /* Update highest modseq to be at least min_modseq. */
668 void mail_index_update_highest_modseq(struct mail_index_transaction *t,
669 				      uint64_t min_modseq);
670 /* Reset the index before committing this transaction. This is usually done
671    only when UIDVALIDITY changes. */
672 void mail_index_reset(struct mail_index_transaction *t);
673 /* Remove MAIL_INDEX_HDR_FLAG_FSCKD from header if it exists. This must be
674    called only during syncing so that the mailbox is locked. */
675 void mail_index_unset_fscked(struct mail_index_transaction *t);
676 /* Mark index deleted. No further changes will be possible after the
677    transaction has been committed. */
678 void mail_index_set_deleted(struct mail_index_transaction *t);
679 /* Mark a deleted index as undeleted. Afterwards index can be changed again. */
680 void mail_index_set_undeleted(struct mail_index_transaction *t);
681 /* Returns TRUE if index has been set deleted. This gets set only after
682    index has been opened/refreshed and the transaction has been seen. */
683 bool mail_index_is_deleted(struct mail_index *index);
684 /* Returns the last time the index was modified. This can be called even if the
685    index isn't open. If the index doesn't exist, sets mtime to 0. */
686 int mail_index_get_modification_time(struct mail_index *index, time_t *mtime_r);
687 
688 /* Lookup a keyword, returns TRUE if found, FALSE if not. */
689 bool mail_index_keyword_lookup(struct mail_index *index,
690 			       const char *keyword, unsigned int *idx_r);
691 void mail_index_keyword_lookup_or_create(struct mail_index *index,
692 					 const char *keyword,
693 					 unsigned int *idx_r);
694 /* Return a pointer to array of NULL-terminated list of keywords. Note that
695    the array contents (and thus pointers inside it) may change after calling
696    mail_index_keywords_create() or mail_index_sync_begin(). */
697 const ARRAY_TYPE(keywords) *mail_index_get_keywords(struct mail_index *index);
698 
699 /* Create a keyword list structure. */
700 struct mail_keywords *
701 mail_index_keywords_create(struct mail_index *index,
702 			   const char *const keywords[]) ATTR_NULL(2);
703 struct mail_keywords *
704 mail_index_keywords_create_from_indexes(struct mail_index *index,
705 					const ARRAY_TYPE(keyword_indexes)
706 						*keyword_indexes);
707 void mail_index_keywords_ref(struct mail_keywords *keywords);
708 void mail_index_keywords_unref(struct mail_keywords **keywords);
709 
710 /* Update keywords for given message. */
711 void mail_index_update_keywords(struct mail_index_transaction *t, uint32_t seq,
712 				enum modify_type modify_type,
713 				struct mail_keywords *keywords);
714 
715 /* Update field in header. If prepend is TRUE, the header change is visible
716    before message syncing begins. */
717 void mail_index_update_header(struct mail_index_transaction *t,
718 			      size_t offset, const void *data, size_t size,
719 			      bool prepend);
720 
721 /* Returns the full error message for last error. This message may
722    contain paths etc. so it shouldn't be shown to users. */
723 const char *mail_index_get_error_message(struct mail_index *index);
724 /* Reset the error message. */
725 void mail_index_reset_error(struct mail_index *index);
726 
727 /* Apply changes in MAIL_INDEX_SYNC_TYPE_FLAGS typed sync records to given
728    flags variable. */
729 void mail_index_sync_flags_apply(const struct mail_index_sync_rec *sync_rec,
730 				 uint8_t *flags);
731 /* Apply changes in MAIL_INDEX_SYNC_TYPE_KEYWORD_* typed sync records to given
732    keywords array. Returns TRUE If something was changed. */
733 bool mail_index_sync_keywords_apply(const struct mail_index_sync_rec *sync_rec,
734 				    ARRAY_TYPE(keyword_indexes) *keywords);
735 
736 /* register index extension. name is a unique identifier for the extension.
737    returns unique identifier for the name. */
738 uint32_t mail_index_ext_register(struct mail_index *index, const char *name,
739 				 uint32_t default_hdr_size,
740 				 uint16_t default_record_size,
741 				 uint16_t default_record_align);
742 /* Change an already registered extension's default sizes. */
743 void mail_index_ext_register_resize_defaults(struct mail_index *index,
744 					     uint32_t ext_id,
745 					     uint32_t default_hdr_size,
746 					     uint16_t default_record_size,
747 					     uint16_t default_record_align);
748 /* Returns TRUE and sets ext_id_r if extension with given name is registered. */
749 bool mail_index_ext_lookup(struct mail_index *index, const char *name,
750 			   uint32_t *ext_id_r);
751 /* Resize existing extension data. If size is grown, the new data will be
752    zero-filled. If size is shrinked, the data is simply dropped. */
753 void mail_index_ext_resize(struct mail_index_transaction *t, uint32_t ext_id,
754 			   uint32_t hdr_size, uint16_t record_size,
755 			   uint16_t record_align);
756 /* Resize header, keeping the old record size. */
757 void mail_index_ext_resize_hdr(struct mail_index_transaction *t,
758 			       uint32_t ext_id, uint32_t hdr_size);
759 
760 /* Reset extension. Any updates for this extension which were issued before the
761    writer had seen this reset are discarded. reset_id is used to figure this
762    out, so it must be different every time. If clear_data=TRUE, records and
763    header is zeroed. */
764 void mail_index_ext_reset(struct mail_index_transaction *t, uint32_t ext_id,
765 			  uint32_t reset_id, bool clear_data);
766 /* Like mail_index_ext_reset(), but increase extension's reset_id atomically
767    when the transaction is being committed. If prev_reset_id doesn't match the
768    latest reset_id, the reset_id isn't increased and all extension changes are
769    ignored. */
770 void mail_index_ext_reset_inc(struct mail_index_transaction *t, uint32_t ext_id,
771 			      uint32_t prev_reset_id, bool clear_data);
772 /* Discard existing extension updates in this transaction and write new updates
773    using the given reset_id. The difference to mail_index_ext_reset() is that
774    this doesn't clear any existing record or header data. */
775 void mail_index_ext_set_reset_id(struct mail_index_transaction *t,
776 				 uint32_t ext_id, uint32_t reset_id);
777 /* Get the current reset_id for given extension. Returns TRUE if it exists. */
778 bool mail_index_ext_get_reset_id(struct mail_index_view *view,
779 				 struct mail_index_map *map,
780 				 uint32_t ext_id, uint32_t *reset_id_r);
781 
782 /* Returns extension header. */
783 void mail_index_get_header_ext(struct mail_index_view *view, uint32_t ext_id,
784 			       const void **data_r, size_t *data_size_r);
785 void mail_index_map_get_header_ext(struct mail_index_view *view,
786 				   struct mail_index_map *map, uint32_t ext_id,
787 				   const void **data_r, size_t *data_size_r);
788 /* Returns the wanted extension record for given message. If it doesn't exist,
789    *data_r is set to NULL. expunged_r is TRUE if the message has already been
790    expunged from the index. */
791 void mail_index_lookup_ext(struct mail_index_view *view, uint32_t seq,
792 			   uint32_t ext_id, const void **data_r,
793 			   bool *expunged_r);
794 void mail_index_lookup_ext_full(struct mail_index_view *view, uint32_t seq,
795 				uint32_t ext_id, struct mail_index_map **map_r,
796 				const void **data_r, bool *expunged_r);
797 /* Get current extension sizes. Returns 1 if ok, 0 if extension doesn't exist
798    in view. Any of the _r parameters may be NULL. */
799 void mail_index_ext_get_size(struct mail_index_map *map, uint32_t ext_id,
800 			     uint32_t *hdr_size_r, uint16_t *record_size_r,
801 			     uint16_t *record_align_r);
802 /* Update extension header field. */
803 void mail_index_update_header_ext(struct mail_index_transaction *t,
804 				  uint32_t ext_id, size_t offset,
805 				  const void *data, size_t size);
806 /* Update extension record. If old_data_r is non-NULL and the record extension
807    was already updated in this transaction, it's set to contain the data it's
808    now overwriting. */
809 void mail_index_update_ext(struct mail_index_transaction *t, uint32_t seq,
810 			   uint32_t ext_id, const void *data, void *old_data)
811 	ATTR_NULL(5);
812 /* Increase/decrease number in extension atomically. Returns the sum of the
813    diffs for this seq. */
814 int mail_index_atomic_inc_ext(struct mail_index_transaction *t,
815 			      uint32_t seq, uint32_t ext_id, int diff);
816 
817 #endif
818