summaryrefslogtreecommitdiffstats
path: root/src/lib-storage/mail-storage.h
blob: 1abf5d4cbdf5138a4bb2670dc04156d8359ecdba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
#ifndef MAIL_STORAGE_H
#define MAIL_STORAGE_H

struct message_size;

#include "seq-range-array.h"
#include "file-lock.h"
#include "guid.h"
#include "mail-types.h"
#include "mail-error.h"
#include "mail-index.h"
#include "mail-namespace.h"
#include "mailbox-list.h"
#include "mailbox-attribute.h"

/* If some operation is taking long, call notify_ok every n seconds. */
#define MAIL_STORAGE_STAYALIVE_SECS 15

#define MAIL_KEYWORD_HAS_ATTACHMENT "$HasAttachment"
#define MAIL_KEYWORD_HAS_NO_ATTACHMENT "$HasNoAttachment"

enum mail_storage_flags {
	/* Remember message headers' MD5 sum */
	MAIL_STORAGE_FLAG_KEEP_HEADER_MD5	= 0x01,
	/* Don't try to autodetect anything, require that the given data 
	   contains all the necessary information. */
	MAIL_STORAGE_FLAG_NO_AUTODETECTION	= 0x02,
	/* Don't autocreate any directories. If they don't exist,
	   fail to create the storage. */
	MAIL_STORAGE_FLAG_NO_AUTOCREATE		= 0x04,
	/* Don't verify existence or accessibility of any directories.
	   Create the storage in any case. */
	MAIL_STORAGE_FLAG_NO_AUTOVERIFY		= 0x08
};

enum mailbox_flags {
	/* Mailbox must not be modified even if asked */
	MAILBOX_FLAG_READONLY		= 0x01,
	/* Only saving/copying mails to mailbox works. */
	MAILBOX_FLAG_SAVEONLY		= 0x02,
	/* Remove MAIL_RECENT flags when syncing */
	MAILBOX_FLAG_DROP_RECENT	= 0x04,
	/* Don't create index files for the mailbox */
	MAILBOX_FLAG_NO_INDEX_FILES	= 0x10,
	/* Keep mailbox exclusively locked all the time while it's open */
	MAILBOX_FLAG_KEEP_LOCKED	= 0x20,
	/* Enable if mailbox is used for serving POP3. This allows making
	   better caching decisions. */
	MAILBOX_FLAG_POP3_SESSION	= 0x40,
	/* Enable if mailbox is used for saving a mail delivery using MDA.
	   This causes ACL plugin to use POST right rather than INSERT. */
	MAILBOX_FLAG_POST_SESSION	= 0x80,
	/* Force opening mailbox and ignoring any ACLs */
	MAILBOX_FLAG_IGNORE_ACLS	= 0x100,
	/* Open mailbox even if it's already marked as deleted */
	MAILBOX_FLAG_OPEN_DELETED	= 0x200,
	/* Mailbox is opened for deletion, which should be performed as
	   efficiently as possible, even allowing the mailbox state to become
	   inconsistent. For example this disables lazy_expunge plugin and
	   quota updates (possibly resulting in broken quota). and This is
	   useful for example when deleting entire user accounts. */
	MAILBOX_FLAG_DELETE_UNSAFE	= 0x400,
	/* Mailbox is created implicitly if it does not exist. */
	MAILBOX_FLAG_AUTO_CREATE	= 0x1000,
	/* Mailbox is subscribed to implicitly when it is created automatically */
	MAILBOX_FLAG_AUTO_SUBSCRIBE	= 0x2000,
	/* Run fsck for mailbox index before doing anything else. This may be
	   useful in fixing index corruption errors that aren't otherwise
	   detected and that are causing the full mailbox opening to fail. */
	MAILBOX_FLAG_FSCK		= 0x4000,
	/* Interpret name argument for mailbox_alloc_for_user() as a SPECIAL-USE
	   flag. */
	MAILBOX_FLAG_SPECIAL_USE	= 0x8000,
	/* Mailbox is opened for reading/writing attributes. This allows ACL
	   plugin to determine correctly whether the mailbox should be allowed
	   to be opened. */
	MAILBOX_FLAG_ATTRIBUTE_SESSION	= 0x10000,
};

enum mailbox_feature {
	/* Enable tracking modsequences */
	MAILBOX_FEATURE_CONDSTORE	= 0x01,
};

enum mailbox_existence {
	MAILBOX_EXISTENCE_NONE,
	MAILBOX_EXISTENCE_NOSELECT,
	MAILBOX_EXISTENCE_SELECT
};

enum mailbox_status_items {
	STATUS_MESSAGES		= 0x01,
	STATUS_RECENT		= 0x02,
	STATUS_UIDNEXT		= 0x04,
	STATUS_UIDVALIDITY	= 0x08,
	STATUS_UNSEEN		= 0x10,
	STATUS_FIRST_UNSEEN_SEQ	= 0x20,
	STATUS_KEYWORDS		= 0x40,
	STATUS_HIGHESTMODSEQ	= 0x80,
	STATUS_PERMANENT_FLAGS	= 0x200,
	STATUS_FIRST_RECENT_UID	= 0x400,
	STATUS_LAST_CACHED_SEQ	= 0x800,
	STATUS_CHECK_OVER_QUOTA	= 0x1000, /* return error if over quota */
	STATUS_HIGHESTPVTMODSEQ	= 0x2000,
	/* status items that must not be looked up with
	   mailbox_get_open_status(), because they can return failure. */
#define MAILBOX_STATUS_FAILING_ITEMS \
	(STATUS_LAST_CACHED_SEQ | STATUS_CHECK_OVER_QUOTA)
};

enum mailbox_metadata_items {
	MAILBOX_METADATA_GUID			= 0x01,
	MAILBOX_METADATA_VIRTUAL_SIZE		= 0x02,
	MAILBOX_METADATA_CACHE_FIELDS		= 0x04,
	MAILBOX_METADATA_PRECACHE_FIELDS	= 0x08,
	MAILBOX_METADATA_BACKEND_NAMESPACE	= 0x10,
	MAILBOX_METADATA_PHYSICAL_SIZE		= 0x20,
	MAILBOX_METADATA_FIRST_SAVE_DATE	= 0x40
	/* metadata items that require mailbox to be synced at least once. */
#define MAILBOX_METADATA_SYNC_ITEMS \
	(MAILBOX_METADATA_VIRTUAL_SIZE | MAILBOX_METADATA_PHYSICAL_SIZE | \
	 MAILBOX_METADATA_FIRST_SAVE_DATE)
};

enum mailbox_search_result_flags {
	/* Update search results whenever the mailbox view is synced.
	   Expunged messages are removed even without this flag. */
	MAILBOX_SEARCH_RESULT_FLAG_UPDATE	= 0x01,
	/* Queue changes so _sync() can be used. */
	MAILBOX_SEARCH_RESULT_FLAG_QUEUE_SYNC	= 0x02
};

enum mail_sort_type {
	MAIL_SORT_ARRIVAL	= 0x0001,
	MAIL_SORT_CC		= 0x0002,
	MAIL_SORT_DATE		= 0x0004,
	MAIL_SORT_FROM		= 0x0008,
	MAIL_SORT_SIZE		= 0x0010,
	MAIL_SORT_SUBJECT	= 0x0020,
	MAIL_SORT_TO		= 0x0040,
	MAIL_SORT_RELEVANCY	= 0x0080,
	MAIL_SORT_DISPLAYFROM	= 0x0100,
	MAIL_SORT_DISPLAYTO	= 0x0200,
	MAIL_SORT_POP3_ORDER	= 0x0400,
/* Maximum size for sort program (each one separately + END) */
#define MAX_SORT_PROGRAM_SIZE (11 + 1)

	MAIL_SORT_MASK		= 0x0fff,
	MAIL_SORT_FLAG_REVERSE	= 0x1000, /* reverse this mask type */

	MAIL_SORT_END		= 0x0000 /* ends sort program */
};

enum mail_fetch_field {
	MAIL_FETCH_FLAGS		= 0x00000001,
	MAIL_FETCH_MESSAGE_PARTS	= 0x00000002,

	MAIL_FETCH_STREAM_HEADER	= 0x00000004,
	MAIL_FETCH_STREAM_BODY		= 0x00000008,

	MAIL_FETCH_DATE			= 0x00000010,
	MAIL_FETCH_RECEIVED_DATE	= 0x00000020,
	MAIL_FETCH_SAVE_DATE		= 0x00000040,
	MAIL_FETCH_PHYSICAL_SIZE	= 0x00000080,
	MAIL_FETCH_VIRTUAL_SIZE		= 0x00000100,

	/* Set has_nuls / has_no_nuls fields */
	MAIL_FETCH_NUL_STATE		= 0x00000200,

	MAIL_FETCH_STREAM_BINARY	= 0x00000400,

	/* specials: */
	MAIL_FETCH_IMAP_BODY		= 0x00001000,
	MAIL_FETCH_IMAP_BODYSTRUCTURE	= 0x00002000,
	MAIL_FETCH_IMAP_ENVELOPE	= 0x00004000,
	MAIL_FETCH_FROM_ENVELOPE	= 0x00008000,
	MAIL_FETCH_HEADER_MD5		= 0x00010000,
	MAIL_FETCH_STORAGE_ID		= 0x00020000,
	MAIL_FETCH_UIDL_BACKEND		= 0x00040000,
	MAIL_FETCH_MAILBOX_NAME		= 0x00080000,
	MAIL_FETCH_SEARCH_RELEVANCY	= 0x00100000,
	MAIL_FETCH_GUID			= 0x00200000,
	MAIL_FETCH_POP3_ORDER		= 0x00400000,
	MAIL_FETCH_REFCOUNT		= 0x00800000,
	MAIL_FETCH_BODY_SNIPPET		= 0x01000000,
	MAIL_FETCH_REFCOUNT_ID		= 0x02000000,
};

enum mailbox_transaction_flags {
	/* Hide changes done in this transaction from next view sync */
	MAILBOX_TRANSACTION_FLAG_HIDE		= 0x01,
	/* External transaction. Should be used for copying and appends,
	   but nothing else. */
	MAILBOX_TRANSACTION_FLAG_EXTERNAL	= 0x02,
	/* Always assign UIDs to messages when saving/copying. Normally this
	   is done only if it can be done easily. */
	MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS	= 0x04,
	/* Refresh the index so lookups return latest flags/modseqs */
	MAILBOX_TRANSACTION_FLAG_REFRESH	= 0x08,
	/* Don't update caching decisions no matter what we do in this
	   transaction (useful for e.g. precaching) */
	MAILBOX_TRANSACTION_FLAG_NO_CACHE_DEC	= 0x10,
	/* Sync transaction describes changes to mailbox that already happened
	   to another mailbox with whom we're syncing with (dsync) */
	MAILBOX_TRANSACTION_FLAG_SYNC		= 0x20,
	/* Don't trigger any notifications for this transaction. This
	   especially means the notify plugin. This would normally be used only
	   with _FLAG_SYNC. */
	MAILBOX_TRANSACTION_FLAG_NO_NOTIFY	= 0x40,
};

enum mailbox_sync_flags {
	/* Make sure we sync all external changes done to mailbox */
	MAILBOX_SYNC_FLAG_FULL_READ		= 0x01,
	/* Make sure we write all our internal changes into the mailbox */
	MAILBOX_SYNC_FLAG_FULL_WRITE		= 0x02,
	/* If it's not too much trouble, check if there are some changes */
	MAILBOX_SYNC_FLAG_FAST			= 0x04,

	/* Don't sync expunges from our view */
	MAILBOX_SYNC_FLAG_NO_EXPUNGES		= 0x08,
	/* If mailbox is currently inconsistent, fix it instead of failing. */
	MAILBOX_SYNC_FLAG_FIX_INCONSISTENT	= 0x40,
	/* Syncing after an EXPUNGE command. This is just an informational
	   flag for plugins. */
	MAILBOX_SYNC_FLAG_EXPUNGE		= 0x80,
	/* Force doing a full resync of indexes. */
	MAILBOX_SYNC_FLAG_FORCE_RESYNC		= 0x100,
	/* FIXME: kludge until something better comes along:
	   Request full text search index optimization */
	MAILBOX_SYNC_FLAG_OPTIMIZE		= 0x400
};

enum mailbox_sync_type {
	MAILBOX_SYNC_TYPE_EXPUNGE	= 0x01,
	MAILBOX_SYNC_TYPE_FLAGS		= 0x02,
	MAILBOX_SYNC_TYPE_MODSEQ	= 0x04
};

struct message_part;
struct mail_namespace;
struct mail_storage;
struct mail_search_args;
struct mail_search_result;
struct mail_keywords;
struct mail_save_context;
struct mailbox;
struct mailbox_transaction_context;

struct mailbox_status {
	uint32_t messages; /* STATUS_MESSAGES */
	uint32_t recent; /* STATUS_RECENT */
	uint32_t unseen; /* STATUS_UNSEEN */

	uint32_t uidvalidity; /* STATUS_UIDVALIDITY */
	uint32_t uidnext; /* STATUS_UIDNEXT */

	uint32_t first_unseen_seq; /* STATUS_FIRST_UNSEEN_SEQ */
	uint32_t first_recent_uid; /* STATUS_FIRST_RECENT_UID */
	uint32_t last_cached_seq; /* STATUS_LAST_CACHED_SEQ */
	uint64_t highest_modseq; /* STATUS_HIGHESTMODSEQ */
	/* 0 if no private index (STATUS_HIGHESTPVTMODSEQ) */
	uint64_t highest_pvt_modseq;

	/* NULL-terminated array of keywords (STATUS_KEYWORDS) */
	const ARRAY_TYPE(keywords) *keywords;

	/* These flags can be permanently modified (STATUS_PERMANENT_FLAGS) */
	enum mail_flags permanent_flags;
	/* These flags can be modified (STATUS_PERMANENT_FLAGS) */
	enum mail_flags flags;

	/* All keywords can be permanently modified (STATUS_PERMANENT_FLAGS) */
	bool permanent_keywords:1;
	/* More keywords can be created (STATUS_PERMANENT_FLAGS) */
	bool allow_new_keywords:1;
	/* Modseqs aren't permanent (index is in memory) (STATUS_HIGHESTMODSEQ) */
	bool nonpermanent_modseqs:1;
	/* Modseq tracking has never been enabled for this mailbox
	   yet. (STATUS_HIGHESTMODSEQ) */
	bool no_modseq_tracking:1;

	/* Messages have GUIDs (always set) */
	bool have_guids:1;
	/* mailbox_save_set_guid() works (always set) */
	bool have_save_guids:1;
	/* GUIDs are always 128bit (always set) */
	bool have_only_guid128:1;
};

struct mailbox_cache_field {
	const char *name;
	int decision; /* enum mail_cache_decision_type */
	/* last_used is unchanged, if it's (time_t)-1 */
	time_t last_used;
};
ARRAY_DEFINE_TYPE(mailbox_cache_field, struct mailbox_cache_field);

struct mailbox_metadata {
	guid_128_t guid;
	/* sum of virtual size of all messages in mailbox */
	uint64_t virtual_size;
	/* sum of physical size of all messages in mailbox */
	uint64_t physical_size;
	/* timestamp of when the first message was saved.
	   (time_t)-1 if there are no mails in the mailbox. */
	time_t first_save_date;

	/* Fields that have "temp" or "yes" caching decision. */
	const ARRAY_TYPE(mailbox_cache_field) *cache_fields;
	/* Fields that should be precached */
	enum mail_fetch_field precache_fields;

	/* imapc backend returns this based on the remote NAMESPACE reply,
	   while currently other backends return "" and type the same as the
	   mailbox's real namespace type */
	const char *backend_ns_prefix;
	enum mail_namespace_type backend_ns_type;
};

struct mailbox_update {
	/* All non-zero fields are changed. */
	guid_128_t mailbox_guid;
	uint32_t uid_validity;
	uint32_t min_next_uid;
	uint32_t min_first_recent_uid;
	uint64_t min_highest_modseq;
	uint64_t min_highest_pvt_modseq;
	/* Modify caching decisions, terminated by name=NULL */
	const struct mailbox_cache_field *cache_updates;
};

struct mail_transaction_commit_changes {
	/* Unreference the pool to free memory used by these changes. */
	pool_t pool;

	/* UIDVALIDITY for assigned UIDs. */
	uint32_t uid_validity;
	/* UIDs assigned to saved messages. Not necessarily ascending.
	   If UID assignment wasn't required (e.g. LDA), this array may also be
	   empty. Otherwise all of the saved mails got an UID. */
	ARRAY_TYPE(seq_range) saved_uids;

	/* number of modseq changes that couldn't be changed as requested */
	unsigned int ignored_modseq_changes;

	/* Changes that occurred within this transaction */
	enum mail_index_transaction_change changes_mask;
	/* User doesn't have read ACL for the mailbox, so don't show the
	   uid_validity / saved_uids. */
	bool no_read_perm;
};

struct mailbox_sync_rec {
	uint32_t seq1, seq2;
	enum mailbox_sync_type type;
};
struct mailbox_sync_status {
	/* There are expunges that haven't been synced yet */
	bool sync_delayed_expunges:1;
};

struct mailbox_expunge_rec {
	/* IMAP UID */
	uint32_t uid;
	/* 128 bit GUID. If the actual GUID has a different size, this
	   contains last bits of its SHA1 sum. */
	guid_128_t guid_128;
};
ARRAY_DEFINE_TYPE(mailbox_expunge_rec, struct mailbox_expunge_rec);

enum mail_lookup_abort {
	/* Perform everything no matter what it takes */
	MAIL_LOOKUP_ABORT_NEVER = 0,
	/* Abort if the operation would require reading message header/body or
	   otherwise opening the mail file (e.g. with dbox metadata is read by
	   opening and reading the file). This still allows somewhat fast
	   operations to be performed, such as stat()ing a file. */
	MAIL_LOOKUP_ABORT_READ_MAIL,
	/* Abort if the operation can't be done fully using cache file */
	MAIL_LOOKUP_ABORT_NOT_IN_CACHE,
	/* Abort if the operation can't be done fully using cache file.
	 * During this lookup all cache lookups that have "no" decision
	 * will be changed to "tmp". This way the field will start to be
	 * cached in the future. */
	MAIL_LOOKUP_ABORT_NOT_IN_CACHE_START_CACHING,
};

enum mail_access_type {
	MAIL_ACCESS_TYPE_DEFAULT = 0,
	/* Mail is being used for searching */
	MAIL_ACCESS_TYPE_SEARCH,
	/* Mail is being used for sorting results */
	MAIL_ACCESS_TYPE_SORT,
};

struct mail {
	/* always set */
	struct mailbox *box;
	struct mailbox_transaction_context *transaction;
	uint32_t seq, uid;

	bool expunged:1;
	bool saving:1; /* This mail is still being saved */
	bool has_nuls:1; /* message data is known to contain NULs */
	bool has_no_nuls:1; /* -''- known to not contain NULs */

	/* Mail's header/body stream was opened (or attempted to be opened)
	   within this request. If lookup_abort!=MAIL_LOOKUP_ABORT_NEVER, this
	   can't become TRUE. */
	bool mail_stream_accessed:1;
	/* Mail's fast metadata was accessed within this request, e.g. the mail
	   file was stat()ed. If mail_stream_opened==TRUE, this value isn't
	   accurate anymore, because some backends may always set this when
	   stream is opened and some don't. If lookup_abort is
	   MAIL_LOOKUP_ABORT_NOT_IN_CACHE, this can't become TRUE. */
	bool mail_metadata_accessed:1;

	enum mail_access_type access_type;

	/* If the lookup is aborted, error is set to MAIL_ERROR_NOTPOSSIBLE */
	enum mail_lookup_abort lookup_abort;
};

struct mail_storage_callbacks {
	/* "* OK <text>" */
	void (*notify_ok)(struct mailbox *mailbox, const char *text,
			  void *context);
	/* "* NO <text>" */
	void (*notify_no)(struct mailbox *mailbox, const char *text,
			  void *context);

};

struct mailbox_virtual_pattern {
	struct mail_namespace *ns;
	const char *pattern;
};
ARRAY_DEFINE_TYPE(mailbox_virtual_patterns, struct mailbox_virtual_pattern);
ARRAY_DEFINE_TYPE(mail_storage, struct mail_storage *);
ARRAY_DEFINE_TYPE(mailboxes, struct mailbox *);

extern ARRAY_TYPE(mail_storage) mail_storage_classes;

typedef void mailbox_notify_callback_t(struct mailbox *box, void *context);

void mail_storage_init(void);
void mail_storage_deinit(void);

/* register all mail storages */
void mail_storage_register_all(void);

/* Register mail storage class with given name - all methods that are NULL
   are set to default methods */
void mail_storage_class_register(struct mail_storage *storage_class);
void mail_storage_class_unregister(struct mail_storage *storage_class);
/* Find mail storage class by name */
struct mail_storage *mail_storage_find_class(const char *name);

/* Create a new instance of registered mail storage class with given
   storage-specific data. If driver is NULL, it's tried to be autodetected
   from ns location. If ns location is NULL, it uses the first storage that
   exists. The storage is put into ns->storage. */
int mail_storage_create(struct mail_namespace *ns, const char *driver,
			enum mail_storage_flags flags, const char **error_r)
	ATTR_NULL(2);
int mail_storage_create_full(struct mail_namespace *ns, const char *driver,
			     const char *data, enum mail_storage_flags flags,
			     struct mail_storage **storage_r,
			     const char **error_r) ATTR_NULL(2);
void mail_storage_unref(struct mail_storage **storage);

/* Returns the mail storage settings. */
const struct mail_storage_settings *
mail_storage_get_settings(struct mail_storage *storage) ATTR_PURE;
struct mail_user *mail_storage_get_user(struct mail_storage *storage) ATTR_PURE;

/* Set storage callback functions to use. */
void mail_storage_set_callbacks(struct mail_storage *storage,
				struct mail_storage_callbacks *callbacks,
				void *context) ATTR_NULL(3);

/* Purge storage's mailboxes (freeing disk space from expunged mails),
   if supported by the storage. Otherwise just a no-op. */
int mail_storage_purge(struct mail_storage *storage);

/* Returns the error message of last occurred error. */
const char * ATTR_NOWARN_UNUSED_RESULT
mail_storage_get_last_error(struct mail_storage *storage,
			    enum mail_error *error_r) ATTR_NULL(2);
/* Wrapper for mail_storage_get_last_error(); */
const char * ATTR_NOWARN_UNUSED_RESULT
mailbox_get_last_error(struct mailbox *box, enum mail_error *error_r)
	ATTR_NULL(2);
/* Wrapper for mail_storage_get_last_error(); */
enum mail_error mailbox_get_last_mail_error(struct mailbox *box);

const char * ATTR_NOWARN_UNUSED_RESULT
mail_storage_get_last_internal_error(struct mail_storage *storage,
				     enum mail_error *error_r) ATTR_NULL(2);
/* Wrapper for mail_storage_get_last_internal_error(); */
const char * ATTR_NOWARN_UNUSED_RESULT
mailbox_get_last_internal_error(struct mailbox *box,
				enum mail_error *error_r) ATTR_NULL(2);

/* Save the last error until it's popped. This is useful for cases where the
   storage has already failed, but the cleanup code path changes the error to
   something else unwanted. */
void mail_storage_last_error_push(struct mail_storage *storage);
void mail_storage_last_error_pop(struct mail_storage *storage);

/* Returns TRUE if mailboxes are files. */
bool mail_storage_is_mailbox_file(struct mail_storage *storage) ATTR_PURE;

/* Initialize mailbox without actually opening any files or verifying that
   it exists. Note that append and copy may open the selected mailbox again
   with possibly different readonly-state. */
struct mailbox *mailbox_alloc(struct mailbox_list *list, const char *vname,
			      enum mailbox_flags flags);
/* Like mailbox_alloc(), but use mailbox GUID. */
struct mailbox *mailbox_alloc_guid(struct mailbox_list *list,
				   const guid_128_t guid,
				   enum mailbox_flags flags);
/* Initialize mailbox for a particular user without actually opening any files
   or verifying that it exists. The mname parameter is normally equal to the
   mailbox vname, except when the MAILBOX_FLAG_SPECIAL_USE flag is set, in which
   case it is the special-use flag. */
struct mailbox *
mailbox_alloc_for_user(struct mail_user *user, const char *mname,
		       enum mailbox_flags flags);

/* Get mailbox existence state. If auto_boxes=FALSE, return
   MAILBOX_EXISTENCE_NONE for autocreated mailboxes that haven't been
   physically created yet */
int mailbox_exists(struct mailbox *box, bool auto_boxes,
		   enum mailbox_existence *existence_r);
/* Open the mailbox. If this function isn't called explicitly, it's also called
   internally by lib-storage when necessary. */
int mailbox_open(struct mailbox *box);
/* Open mailbox as read-only using the given stream as input. */
int mailbox_open_stream(struct mailbox *box, struct istream *input);
/* Close mailbox. Same as if mailbox was freed and re-allocated. */
void mailbox_close(struct mailbox *box);
/* Close and free the mailbox. */
void mailbox_free(struct mailbox **box);

/* Returns TRUE if box1 points to the same mailbox as ns2/vname2. */
bool mailbox_equals(const struct mailbox *box1,
		    const struct mail_namespace *ns2,
		    const char *vname2) ATTR_PURE;
/* Returns TRUE if the mailbox is user's INBOX or another user's shared INBOX */
bool mailbox_is_any_inbox(struct mailbox *box);

/* Returns TRUE if the mailbox has the specified special use flag assigned. */
bool mailbox_has_special_use(struct mailbox *box, const char *special_use);

/* Change mailbox_verify_create_name() to not verify new mailbox name
   restrictions (but still check that it's a valid existing name). This is
   mainly used by dsync to make sure the sync works even though the original
   name isn't valid anymore. */
void mailbox_skip_create_name_restrictions(struct mailbox *box, bool set);
/* Returns -1 if mailbox_create() is guaranteed to fail because the mailbox
   name is invalid, 0 not. The error message contains a reason. */
int mailbox_verify_create_name(struct mailbox *box);
/* Create a mailbox. Returns failure if it already exists. Mailbox name is
   allowed to contain multiple new nonexistent hierarchy levels. If directory
   is TRUE, the mailbox should be created so that it can contain children. The
   mailbox itself doesn't have to be created as long as it shows up in LIST.
   If update is non-NULL, its contents are used to set initial mailbox
   metadata. */
int mailbox_create(struct mailbox *box, const struct mailbox_update *update,
		   bool directory) ATTR_NULL(2);
/* Update existing mailbox's metadata. */
int mailbox_update(struct mailbox *box, const struct mailbox_update *update);
/* Delete mailbox (and its parent directory, if it has no siblings) */
int mailbox_delete(struct mailbox *box);
/* Delete mailbox, but only if it's empty. If it's not, fails with
   MAIL_ERROR_EXISTS. */
int mailbox_delete_empty(struct mailbox *box);
/* Rename mailbox (and its children). Renaming across different mailbox lists
   is possible only between private namespaces and storages of the same type.
   If the rename fails, the error is set to src's storage. */
int mailbox_rename(struct mailbox *src, struct mailbox *dest);
/* Subscribe/unsubscribe mailbox. Subscribing to
   nonexistent mailboxes is optional. */
int mailbox_set_subscribed(struct mailbox *box, bool set);
/* Returns TRUE if mailbox is subscribed, FALSE if not. This function
   doesn't refresh the subscriptions list, but assumes that it's been done by
   e.g. mailbox_list_iter*(). */
bool mailbox_is_subscribed(struct mailbox *box);

/* Enable the given feature for the mailbox. */
int mailbox_enable(struct mailbox *box, enum mailbox_feature features);
/* Returns all enabled features. */
enum mailbox_feature
mailbox_get_enabled_features(struct mailbox *box) ATTR_PURE;

/* Returns storage of given mailbox */
struct mail_storage *mailbox_get_storage(const struct mailbox *box) ATTR_PURE;
/* Return namespace of given mailbox. */
struct mail_namespace *
mailbox_get_namespace(const struct mailbox *box) ATTR_PURE;
/* Returns the storage's settings. */
const struct mail_storage_settings *
mailbox_get_settings(struct mailbox *box) ATTR_PURE;
/* Returns the mailbox's settings, or NULL if there are none. */
const struct mailbox_settings *
mailbox_settings_find(struct mail_namespace *ns, const char *vname);

/* Returns the (virtual) name of the given mailbox. */
const char *mailbox_get_vname(const struct mailbox *box) ATTR_PURE;
/* Returns the backend name of given mailbox. */
const char *mailbox_get_name(const struct mailbox *box) ATTR_PURE;

/* Returns TRUE if mailbox is read-only. */
bool mailbox_is_readonly(struct mailbox *box);
/* Returns TRUE if two mailboxes point to the same physical mailbox. */
bool mailbox_backends_equal(const struct mailbox *box1,
			    const struct mailbox *box2);
/* Returns TRUE if mailbox is now in inconsistent state, meaning that
   the message IDs etc. may have changed - only way to recover this
   would be to fully close the mailbox and reopen it. With IMAP
   connection this would mean a forced disconnection since we can't
   do forced CLOSE. */
bool mailbox_is_inconsistent(struct mailbox *box);

/* Gets the mailbox status information. If mailbox isn't opened yet, try to
   return the results from mailbox list indexes. Otherwise the mailbox is
   opened and synced. If the mailbox is already opened, no syncing is done
   automatically. */
int mailbox_get_status(struct mailbox *box, enum mailbox_status_items items,
		       struct mailbox_status *status_r);
/* Gets the mailbox status, requires that mailbox is already opened. */
void mailbox_get_open_status(struct mailbox *box,
			     enum mailbox_status_items items,
			     struct mailbox_status *status_r);
/* Gets mailbox metadata */
int mailbox_get_metadata(struct mailbox *box, enum mailbox_metadata_items items,
			 struct mailbox_metadata *metadata_r);
/* Returns a mask of flags that are private to user in this mailbox
   (as opposed to flags shared between users). */
enum mail_flags mailbox_get_private_flags_mask(struct mailbox *box);

/* Synchronize the mailbox. */
struct mailbox_sync_context *
mailbox_sync_init(struct mailbox *box, enum mailbox_sync_flags flags);
bool mailbox_sync_next(struct mailbox_sync_context *ctx,
		       struct mailbox_sync_rec *sync_rec_r);
int mailbox_sync_deinit(struct mailbox_sync_context **ctx,
			struct mailbox_sync_status *status_r);
/* One-step mailbox synchronization. Use this if you don't care about
   changes. */
int mailbox_sync(struct mailbox *box, enum mailbox_sync_flags flags);

/* Call given callback function when something changes in the mailbox. */
void mailbox_notify_changes(struct mailbox *box,
			    mailbox_notify_callback_t *callback, void *context)
	ATTR_NULL(3);
#define mailbox_notify_changes(box, callback, context) \
	  mailbox_notify_changes(box, (mailbox_notify_callback_t *)callback, \
		(void *)((char *)context - CALLBACK_TYPECHECK(callback, \
			void (*)(struct mailbox *, typeof(context)))))
void mailbox_notify_changes_stop(struct mailbox *box);

struct mailbox_transaction_context *
mailbox_transaction_begin(struct mailbox *box,
			  enum mailbox_transaction_flags flags,
			  const char *reason);
int mailbox_transaction_commit(struct mailbox_transaction_context **t);
int mailbox_transaction_commit_get_changes(
	struct mailbox_transaction_context **t,
	struct mail_transaction_commit_changes *changes_r);
void mailbox_transaction_rollback(struct mailbox_transaction_context **t);
/* Return the number of active transactions for the mailbox. */
unsigned int mailbox_transaction_get_count(const struct mailbox *box) ATTR_PURE;
/* When committing transaction, drop flag/keyword updates for messages whose
   modseq is larger than max_modseq. Save those messages' sequences to the
   given array. */
void mailbox_transaction_set_max_modseq(struct mailbox_transaction_context *t,
					uint64_t max_modseq,
					ARRAY_TYPE(seq_range) *seqs);

struct mailbox *
mailbox_transaction_get_mailbox(const struct mailbox_transaction_context *t)
	ATTR_PURE;

/* Convert uid range to sequence range. */
void mailbox_get_seq_range(struct mailbox *box, uint32_t uid1, uint32_t uid2,
			   uint32_t *seq1_r, uint32_t *seq2_r);
/* Convert sequence range to uid range. If sequences contain
   (uint32_t)-1 to specify "*", they're preserved. */
void mailbox_get_uid_range(struct mailbox *box,
			   const ARRAY_TYPE(seq_range) *seqs,
			   ARRAY_TYPE(seq_range) *uids);
/* Get list of messages' that have been expunged after prev_modseq and that
   exist in uids_filter range. UIDs that have been expunged after the last
   mailbox sync aren't returned. Returns TRUE if ok, FALSE if modseq is lower
   than we can check for (but expunged_uids is still set as best as it can). */
bool mailbox_get_expunges(struct mailbox *box, uint64_t prev_modseq,
			  const ARRAY_TYPE(seq_range) *uids_filter,
			  ARRAY_TYPE(mailbox_expunge_rec) *expunges);
/* Same as mailbox_get_expunges(), but return only list of UIDs. Not caring
   about GUIDs is slightly faster. */
bool mailbox_get_expunged_uids(struct mailbox *box, uint64_t prev_modseq,
			       const ARRAY_TYPE(seq_range) *uids_filter,
			       ARRAY_TYPE(seq_range) *expunged_uids);

/* Initialize header lookup for given headers. */
struct mailbox_header_lookup_ctx *
mailbox_header_lookup_init(struct mailbox *box, const char *const headers[]);
void mailbox_header_lookup_ref(struct mailbox_header_lookup_ctx *ctx);
void mailbox_header_lookup_unref(struct mailbox_header_lookup_ctx **ctx);
/* Merge two header lookups. */
struct mailbox_header_lookup_ctx *
mailbox_header_lookup_merge(const struct mailbox_header_lookup_ctx *hdr1,
			    const struct mailbox_header_lookup_ctx *hdr2);

/* Initialize new search request. If sort_program is non-NULL, the messages are
   returned in the requested order, otherwise from first to last. */
struct mail_search_context * ATTR_NULL(3, 5)
mailbox_search_init(struct mailbox_transaction_context *t,
		    struct mail_search_args *args,
		    const enum mail_sort_type *sort_program,
		    enum mail_fetch_field wanted_fields,
		    struct mailbox_header_lookup_ctx *wanted_headers);
/* Deinitialize search request. */
int mailbox_search_deinit(struct mail_search_context **ctx);
/* Search the next message. Returns TRUE if found, FALSE if not. */
bool mailbox_search_next(struct mail_search_context *ctx, struct mail **mail_r);
/* Like mailbox_search_next(), but don't spend too much time searching.
   Returns FALSE with tryagain_r=FALSE if finished, and tryagain_r=TRUE if
   more results will be returned by calling the function again. */
bool mailbox_search_next_nonblock(struct mail_search_context *ctx,
				  struct mail **mail_r, bool *tryagain_r);
/* Returns TRUE if some messages were already expunged and we couldn't
   determine correctly if those messages should have been returned in this
   search. */
bool mailbox_search_seen_lost_data(struct mail_search_context *ctx);
/* Detach the given mail from the search context. This allows the mail to live
   even after mail_search_context has been freed. */
void mailbox_search_mail_detach(struct mail_search_context *ctx,
				struct mail *mail);

/* Remember the search result for future use. This must be called before the
   first mailbox_search_next*() call. */
struct mail_search_result *
mailbox_search_result_save(struct mail_search_context *ctx,
			   enum mailbox_search_result_flags flags);
/* Free memory used by search result. */
void mailbox_search_result_free(struct mail_search_result **result);
/* A simplified API for searching and saving the result. */
int mailbox_search_result_build(struct mailbox_transaction_context *t,
				struct mail_search_args *args,
				enum mailbox_search_result_flags flags,
				struct mail_search_result **result_r);
/* Return all messages' UIDs in the search result. */
const ARRAY_TYPE(seq_range) *
mailbox_search_result_get(struct mail_search_result *result);
/* Return messages that have been removed and added since the last sync call.
   This function must not be called if search result wasn't saved with
   _QUEUE_SYNC flag. */
void mailbox_search_result_sync(struct mail_search_result *result,
				ARRAY_TYPE(seq_range) *removed_uids,
				ARRAY_TYPE(seq_range) *added_uids);

/* Build mail_keywords from NULL-terminated keywords list. Any duplicate
   keywords are removed. Returns 0 if successful, -1 if there are invalid
   keywords (error is set). */
int mailbox_keywords_create(struct mailbox *box, const char *const keywords[],
			    struct mail_keywords **keywords_r);
/* Like mailbox_keywords_create(), except ignore invalid keywords. */
struct mail_keywords *
mailbox_keywords_create_valid(struct mailbox *box,
			      const char *const keywords[]);
struct mail_keywords *
mailbox_keywords_create_from_indexes(struct mailbox *box,
				     const ARRAY_TYPE(keyword_indexes) *idx);
/* Return union of two mail_keywords. They must be created in the same
   mailbox. */
struct mail_keywords *mailbox_keywords_merge(struct mail_keywords *keywords1,
					     struct mail_keywords *keywords2);
void mailbox_keywords_ref(struct mail_keywords *keywords);
void mailbox_keywords_unref(struct mail_keywords **keywords);
/* Returns TRUE if keyword is valid, FALSE and error if not. */
bool mailbox_keyword_is_valid(struct mailbox *box, const char *keyword,
			      const char **error_r);

/* Initialize saving a new mail. You must not try to save more than one mail
   at a time. */
struct mail_save_context *
mailbox_save_alloc(struct mailbox_transaction_context *t);
/* Set the flags and keywords. Nothing is set by default. */
void mailbox_save_set_flags(struct mail_save_context *ctx,
			    enum mail_flags flags,
			    struct mail_keywords *keywords);
/* Copy flags and keywords from given mail. */
void mailbox_save_copy_flags(struct mail_save_context *ctx, struct mail *mail);
/* Set message's modseq to be at least min_modseq. */
void mailbox_save_set_min_modseq(struct mail_save_context *ctx,
				 uint64_t min_modseq);
/* If received date isn't specified the current time is used. timezone_offset
   specifies the preferred timezone in minutes, but it may be ignored if
   backend doesn't support storing it. */
void mailbox_save_set_received_date(struct mail_save_context *ctx,
				    time_t received_date, int timezone_offset);
/* Set the "message saved" date. This should be set only when you're
   replicating/restoring an existing mailbox. */
void mailbox_save_set_save_date(struct mail_save_context *ctx,
				time_t save_date);
/* Set the envelope sender. This is currently used only with mbox files to
   specify the address in From_-line. */
void mailbox_save_set_from_envelope(struct mail_save_context *ctx,
				    const char *envelope);
/* Set message's UID. If UID is smaller than the current next_uid, it's given
   a new UID anyway. */
void mailbox_save_set_uid(struct mail_save_context *ctx, uint32_t uid);
/* Set globally unique ID for the saved mail. A new GUID is generated by
   default. This function should usually be called only when copying an
   existing mail (or restoring a mail from backup). */
void mailbox_save_set_guid(struct mail_save_context *ctx, const char *guid);
/* Set message's POP3 UIDL, if the backend supports it. */
void mailbox_save_set_pop3_uidl(struct mail_save_context *ctx,
				const char *uidl);
/* Specify ordering for POP3 messages. The default is to add them to the end
   of the mailbox. Not all backends support this. */
void mailbox_save_set_pop3_order(struct mail_save_context *ctx,
				 unsigned int order);
/* Returns the destination mail */
struct mail *mailbox_save_get_dest_mail(struct mail_save_context *ctx);
/* Begin saving the message. All mail_save_set_*() calls must have been called
   before this function. If the save initialization fails, the context is freed
   and -1 is returned. After beginning the save you should keep calling
   i_stream_read() and calling mailbox_save_continue() as long as there's
   more input. */
int mailbox_save_begin(struct mail_save_context **ctx, struct istream *input);
int mailbox_save_continue(struct mail_save_context *ctx);
int mailbox_save_finish(struct mail_save_context **ctx);
void mailbox_save_cancel(struct mail_save_context **ctx);

struct mailbox_transaction_context *
mailbox_save_get_transaction(struct mail_save_context *ctx);

/* Copy the given message. You'll need to specify the flags etc. using the
   mailbox_save_*() functions. */
int mailbox_copy(struct mail_save_context **ctx, struct mail *mail);
/* Move the given message. This is usually equivalent to copy+expunge,
   but without enforcing quota. */
int mailbox_move(struct mail_save_context **ctx, struct mail *mail);
/* Same as mailbox_copy(), but treat the message as if it's being saved,
   not copied. (For example: New mail delivered to multiple maildirs, with
   each mails being hard link copies.) */
int mailbox_save_using_mail(struct mail_save_context **ctx, struct mail *mail);

struct mail *mail_alloc(struct mailbox_transaction_context *t,
			enum mail_fetch_field wanted_fields,
			struct mailbox_header_lookup_ctx *wanted_headers)
	ATTR_NULL(3);
void mail_free(struct mail **mail);
void mail_set_seq(struct mail *mail, uint32_t seq);
/* Returns TRUE if successful, FALSE if message doesn't exist.
   mail_*() functions shouldn't be called if FALSE is returned. */
bool mail_set_uid(struct mail *mail, uint32_t uid);

/* Add wanted fields/headers on top of existing ones. These will be forgotten
   after the next mail_set_seq/uid() that closes the existing mail. Note that
   it's valid to call this function while there is no mail assigned
   (mail->seq==0), i.e. this is called before any mail_set_seq/uid() or after
   mail.close(). */
void mail_add_temp_wanted_fields(struct mail *mail,
				 enum mail_fetch_field fields,
				 struct mailbox_header_lookup_ctx *headers)
	ATTR_NULL(3);

/* Returns mail's event. This lazily creates the event when called for the
   first time for the mail. */
struct event *mail_event(struct mail *mail);
/* Returns message's flags */
enum mail_flags mail_get_flags(struct mail *mail);
/* Returns message's keywords */
const char *const *mail_get_keywords(struct mail *mail);
/* Returns message's keywords */
const ARRAY_TYPE(keyword_indexes) *mail_get_keyword_indexes(struct mail *mail);
/* Returns message's modseq */
uint64_t mail_get_modseq(struct mail *mail);
/* Returns message's private modseq, or 0 if message hasn't had any
   private flag changes. This is useful only for shared mailboxes that have
   a private index defined. */
uint64_t mail_get_pvt_modseq(struct mail *mail);

/* Returns message's MIME parts */
int mail_get_parts(struct mail *mail, struct message_part **parts_r);

/* Get the Date-header of the mail. Timezone is in minutes. date=0 if it
   wasn't found or it was invalid. */
int mail_get_date(struct mail *mail, time_t *date_r, int *timezone_r);
/* Get the time when the mail was received (IMAP INTERNALDATE). */
int mail_get_received_date(struct mail *mail, time_t *date_r);
/* Get the time when the mail was saved into this mailbox. This returns -1 on
   error, 0 if a real save date is not supported and a fall-back date is used,
   and 1 when a save date was successfully retrieved. */
int mail_get_save_date(struct mail *mail, time_t *date_r);

/* Get the space used by the mail as seen by the reader. Linefeeds are always
   counted as being CR+LF. */
int mail_get_virtual_size(struct mail *mail, uoff_t *size_r);
/* Get the size of the stream returned by mail_get_stream(). */
int mail_get_physical_size(struct mail *mail, uoff_t *size_r);

/* Get value for single header field, or NULL if header wasn't found.
   Returns 1 if header was found, 0 if not, -1 if error. */
int mail_get_first_header(struct mail *mail, const char *field,
			  const char **value_r);
/* Like mail_get_first_header(), but decode MIME encoded words to UTF-8.
   Also multiline headers are returned unfolded.

   Do not use this function for getting structured fields (e.g. address fields),
   because decoding may break the structuring. Instead parse them first and
   only afterwards decode the encoded words. */
int mail_get_first_header_utf8(struct mail *mail, const char *field,
			       const char **value_r);
/* Return a NULL-terminated list of values for each found field.
   Returns 1 if headers were found, 0 if not (value_r[0]==NULL) or
   -1 if error. */
int mail_get_headers(struct mail *mail, const char *field,
		     const char *const **value_r);
/* Like mail_get_headers(), but decode MIME encoded words to UTF-8.
   Also multiline headers are returned unfolded.
   Do not use for structured fields (see mail_get_first_header_utf8()). */
int mail_get_headers_utf8(struct mail *mail, const char *field,
			  const char *const **value_r);
/* Returns stream containing specified headers. The returned stream will be
   automatically freed when the mail is closed, or when another
   mail_get_header_stream() call is made (so you can't have multiple header
   streams open at the same time). */
int mail_get_header_stream(struct mail *mail,
			   struct mailbox_header_lookup_ctx *headers,
			   struct istream **stream_r);
/* Returns input stream pointing to beginning of message header.
   hdr_size and body_size are updated unless they're NULL. The returned stream
   is destroyed automatically, don't unreference it. */
int mail_get_stream(struct mail *mail, struct message_size *hdr_size,
		    struct message_size *body_size, struct istream **stream_r)
	ATTR_NULL(2, 3);
/* Same as mail_get_stream(), but specify a reason why the mail is being read.
   This can be useful for debugging purposes. */
int mail_get_stream_because(struct mail *mail, struct message_size *hdr_size,
			    struct message_size *body_size,
			    const char *reason, struct istream **stream_r)
	ATTR_NULL(2, 3);
/* Similar to mail_get_stream(), but the stream may or may not contain the
   message body. */
int mail_get_hdr_stream(struct mail *mail, struct message_size *hdr_size,
			struct istream **stream_r) ATTR_NULL(2);
/* Same as mail_get_hdr_stream(), but specify a reason why the header is being
   read. This can be useful for debugging purposes. */
int mail_get_hdr_stream_because(struct mail *mail,
				struct message_size *hdr_size,
				const char *reason, struct istream **stream_r);
/* Returns the message part's body decoded to 8bit binary. If the
   Content-Transfer-Encoding isn't supported, returns -1 and sets error to
   MAIL_ERROR_CONVERSION. If the part refers to a multipart, all of its
   children are returned decoded. Note that the returned stream must be
   unreferenced, unlike mail_get_*stream*() which automatically free it. */
int mail_get_binary_stream(struct mail *mail, const struct message_part *part,
			   bool include_hdr, uoff_t *size_r,
			   bool *binary_r, struct istream **stream_r);
/* Like mail_get_binary_stream(), but only return the size. */
int mail_get_binary_size(struct mail *mail, const struct message_part *part,
			 bool include_hdr, uoff_t *size_r,
			 unsigned int *lines_r);

/* Get any of the "special" fields. Unhandled specials are returned as "". */
int mail_get_special(struct mail *mail, enum mail_fetch_field field,
		     const char **value_r);
/* Returns the mail for the physical message. Normally this is the mail itself,
   but in virtual mailboxes it points to the backend mailbox. */
int mail_get_backend_mail(struct mail *mail, struct mail **real_mail_r);

/* Retrieve and parse the value of the Message-ID header field. Returns 1 if the
   header was found and it contains a valid message ID, 0 if the header was not
   found or no valid message ID was contained in it, and -1 if an error occurred
   while retrieving the header. Returns the message ID value including '<' and
   '>' in the *value_r return parameter or NULL if the header wasn't found or
   its value was invalid. */
int mail_get_message_id(struct mail *mail, const char **value_r);
/* Try to retrieve a properly parsed Message-ID header, if that fails return
   the raw header value as it is. */
int mail_get_message_id_no_validation(struct mail *mail, const char **value_r);

/* Update message flags. */
void mail_update_flags(struct mail *mail, enum modify_type modify_type,
		       enum mail_flags flags);
/* Update message keywords. */
void mail_update_keywords(struct mail *mail, enum modify_type modify_type,
			  struct mail_keywords *keywords);
/* Update message's modseq to be at least min_modseq. */
void mail_update_modseq(struct mail *mail, uint64_t min_modseq);
/* Update message's private modseq to be at least min_pvt_modseq. */
void mail_update_pvt_modseq(struct mail *mail, uint64_t min_pvt_modseq);

/* Update message's POP3 UIDL (if possible). */
void mail_update_pop3_uidl(struct mail *mail, const char *uidl);
/* Expunge this message. Sequence numbers don't change until commit. */
void mail_expunge(struct mail *mail);

/* Add missing fields to cache. */
int mail_precache(struct mail *mail);
/* Mark a cached field corrupted and have it recalculated. */
void mail_set_cache_corrupted(struct mail *mail,
			      enum mail_fetch_field field,
			      const char *reason);

/* Return 128 bit GUID using input string. If guid is already 128 bit hex
   encoded, it's returned as-is. Otherwise SHA1 sum is taken and its last
   128 bits are returned. */
void mail_generate_guid_128_hash(const char *guid, guid_128_t guid_128_r);

/* Parse a human-writable string into a timestamp. utc_r controls whether
   the returned timestamp should be treated as an exact UTC time (TRUE), or
   whether this is a human-given date where the timestamp could be adjusted
   by the matched mails' timezones (see MAIL_SEARCH_ARG_FLAG_USE_TZ).

   Returns 0 and timestamp on success, -1 if the string couldn't be parsed.
   Currently supported string formats: yyyy-mm-dd (utc=FALSE),
   imap date (utc=FALSE), unix timestamp (utc=TRUE), interval (e.g. n days,
   utc=TRUE). */
int mail_parse_human_timestamp(const char *str, time_t *timestamp_r,
			       bool *utc_r);

#endif