mail-index.h revision f6d121ba17e01dbf43a3d9c98a024ed8f1eb9043
970N/A#ifndef __MAIL_INDEX_H
970N/A#define __MAIL_INDEX_H
970N/A
970N/A#include "message-parser.h"
1072N/A#include "imap-util.h"
970N/A
970N/A#define MAIL_INDEX_VERSION 1
970N/A
970N/A#define INDEX_FILE_PREFIX ".imap.index"
970N/A
970N/Aenum {
970N/A MAIL_INDEX_COMPAT_LITTLE_ENDIAN = 0x01
970N/A};
970N/A
970N/Aenum {
970N/A /* Rebuild flag is set while index is being rebuilt or when
970N/A some error is noticed in the index file. If this flag is set,
970N/A the index shouldn't be used before rebuilding it. */
970N/A MAIL_INDEX_FLAG_REBUILD = 0x01,
970N/A MAIL_INDEX_FLAG_FSCK = 0x02,
970N/A MAIL_INDEX_FLAG_CACHE_FIELDS = 0x04,
970N/A MAIL_INDEX_FLAG_COMPRESS = 0x08,
970N/A MAIL_INDEX_FLAG_COMPRESS_DATA = 0x10,
970N/A MAIL_INDEX_FLAG_REBUILD_TREE = 0x20,
970N/A MAIL_INDEX_FLAG_DIRTY_MESSAGES = 0x40,
970N/A MAIL_INDEX_FLAG_DIRTY_CUSTOMFLAGS = 0x80
970N/A};
970N/A
970N/Atypedef enum {
970N/A /* First MUST become a field that ALWAYS exists. This is because some
970N/A code which goes through all fields does it by calling
970N/A lookup_field(.., .., 1) and next() after that. If the first field
970N/A didn't exist, nothing would be found.
970N/A
970N/A Location field is a good first field anyway, it's the one most
970N/A often needed. With maildir format, it's the file name and with
970N/A mbox format it's the file position as a string. */
970N/A FIELD_TYPE_LOCATION = 0x0001,
970N/A FIELD_TYPE_ENVELOPE = 0x0002,
970N/A FIELD_TYPE_BODY = 0x0004,
970N/A FIELD_TYPE_BODYSTRUCTURE = 0x0008,
970N/A FIELD_TYPE_FROM = 0x0010,
970N/A FIELD_TYPE_TO = 0x0020,
1105N/A FIELD_TYPE_CC = 0x0040,
970N/A FIELD_TYPE_BCC = 0x0080,
970N/A FIELD_TYPE_SUBJECT = 0x0100,
970N/A FIELD_TYPE_MD5 = 0x0200,
970N/A FIELD_TYPE_MESSAGEPART = 0x0400,
970N/A
970N/A FIELD_TYPE_LAST = 0x0800,
970N/A FIELD_TYPE_MAX_BITS = 11
970N/A} MailField;
970N/A
970N/Atypedef enum {
970N/A /* If binary flags are set, it's not checked whether mail is
970N/A missing CRs. So this flag may be set as an optimization for
970N/A regular non-binary mails as well if it's known that it contains
970N/A valid CR+LF line breaks. */
970N/A INDEX_MAIL_FLAG_BINARY_HEADER = 0x0001,
970N/A INDEX_MAIL_FLAG_BINARY_BODY = 0x0002,
970N/A
970N/A /* Currently this means with mbox format that message flags have
970N/A been changed in index, but not written into mbox file yet. */
970N/A INDEX_MAIL_FLAG_DIRTY = 0x0004
970N/A} MailIndexMailFlags;
970N/A
970N/A#define IS_HEADER_FIELD(field) \
970N/A (((field) & (FIELD_TYPE_FROM | FIELD_TYPE_TO | FIELD_TYPE_CC | \
970N/A FIELD_TYPE_BCC | FIELD_TYPE_SUBJECT)) != 0)
1130N/A
970N/A#define IS_BODYSTRUCTURE_FIELD(field) \
970N/A (((field) & (FIELD_TYPE_BODY|FIELD_TYPE_BODYSTRUCTURE| \
970N/A FIELD_TYPE_MESSAGEPART)) != 0)
970N/A
970N/Atypedef enum {
970N/A MAIL_LOCK_UNLOCK = 0,
970N/A MAIL_LOCK_SHARED,
970N/A MAIL_LOCK_EXCLUSIVE
970N/A} MailLockType;
970N/A
970N/Atypedef struct _MailIndex MailIndex;
970N/Atypedef struct _MailIndexData MailIndexData;
970N/Atypedef struct _MailTree MailTree;
970N/Atypedef struct _MailModifyLog MailModifyLog;
970N/Atypedef struct _MailCustomFlags MailCustomFlags;
970N/A
970N/Atypedef struct _MailIndexHeader MailIndexHeader;
970N/Atypedef struct _MailIndexDataHeader MailIndexDataHeader;
970N/A
970N/Atypedef struct _MailIndexRecord MailIndexRecord;
970N/Atypedef struct _MailIndexDataRecord MailIndexDataRecord;
970N/A
970N/Atypedef struct _MailIndexUpdate MailIndexUpdate;
970N/A
970N/Astruct _MailIndexHeader {
970N/A unsigned char compat_data[8];
970N/A /* 0 = version
970N/A 1 = flags,
1105N/A 2 = sizeof(unsigned int),
1105N/A 3 = sizeof(time_t),
970N/A 4 = sizeof(uoff_t),
1105N/A 5 = MEM_ALIGN_SIZE */
970N/A
970N/A unsigned int indexid;
970N/A unsigned int sync_id; /* re-mmap() when changed, required only
970N/A if file size is changed */
970N/A
970N/A unsigned int flags;
1154N/A unsigned int cache_fields;
1152N/A
970N/A uoff_t used_file_size;
1172N/A
1172N/A unsigned int first_hole_index;
1172N/A unsigned int first_hole_records;
970N/A
1105N/A unsigned int uid_validity;
970N/A unsigned int next_uid;
970N/A
1105N/A unsigned int messages_count;
970N/A unsigned int seen_messages_count;
970N/A unsigned int deleted_messages_count;
1120N/A unsigned int last_nonrecent_uid;
1120N/A
1120N/A /* these UIDs may not exist and may not even be unseen */
970N/A unsigned int first_unseen_uid_lowwater;
1172N/A unsigned int first_deleted_uid_lowwater;
1172N/A};
970N/A
1172N/Astruct _MailIndexDataHeader {
1172N/A unsigned int indexid;
1172N/A unsigned int reserved; /* for alignment mostly */
1153N/A
970N/A uoff_t used_file_size;
970N/A uoff_t deleted_space;
970N/A};
970N/A
1105N/Astruct _MailIndexRecord {
1105N/A /* remember to keep uoff_t's 8 byte aligned so we don't waste space */
1105N/A unsigned int uid;
970N/A unsigned int msg_flags; /* MailFlags */
970N/A time_t internal_date;
970N/A time_t sent_date;
1105N/A
970N/A uoff_t header_size;
970N/A uoff_t body_size;
970N/A
970N/A unsigned int index_flags; /* MailIndexMailFlags */
970N/A unsigned int cached_fields;
970N/A
970N/A uoff_t data_position;
970N/A unsigned int data_size;
970N/A
970N/A unsigned int alignment;
970N/A};
1189N/A
1189N/Astruct _MailIndexDataRecord {
1189N/A unsigned int field; /* MailField */
1189N/A unsigned int full_field_size;
1189N/A char data[MEM_ALIGN_SIZE]; /* variable size */
1189N/A};
1189N/A
1189N/A#define SIZEOF_MAIL_INDEX_DATA \
970N/A (sizeof(MailIndexDataRecord) - MEM_ALIGN_SIZE)
970N/A
970N/A#define DATA_RECORD_SIZE(rec) \
970N/A (SIZEOF_MAIL_INDEX_DATA + (rec)->full_field_size)
970N/A
1189N/Astruct _MailIndex {
1189N/A /* If fast is TRUE, compressing and cache updates are not performed. */
970N/A int (*open)(MailIndex *index, int update_recent, int fast);
970N/A int (*open_or_create)(MailIndex *index, int update_recent, int fast);
970N/A
1132N/A /* Free index from memory. */
1132N/A void (*free)(MailIndex *index);
1132N/A
970N/A /* Lock/unlock index. May block. Note that unlocking must not
970N/A reset error from get_last_error() as unlocking can be done as
970N/A a cleanup after some other function failed. Index is always
970N/A mmap()ed after set_lock() succeeds.
970N/A
1132N/A Trying to change a shared lock into exclusive lock is a fatal
1132N/A error, since it may create a deadlock. Even though operating
970N/A system should detect it and fail, it's not a good idea to even
970N/A let it happen. Better ways to do this would be to a) mark the
1132N/A data to be updated later, b) use try_lock() if the update is
1132N/A preferred but not required, c) unlock + lock again, but make
1132N/A sure that won't create race conditions */
1132N/A int (*set_lock)(MailIndex *index, MailLockType lock_type);
1132N/A
970N/A /* Try locking the index. Returns TRUE if the lock was got and
1132N/A FALSE if lock isn't possible to get currently or some other error
1132N/A occured. Never blocks. */
1132N/A int (*try_lock)(MailIndex *index, MailLockType lock_type);
1132N/A
1132N/A /* Rebuild the whole index. Note that this changes the indexid
1147N/A so all the other files must also be rebuilt after this call.
1147N/A Index MUST NOT have shared lock, but exclusive lock or no lock at
1147N/A all is fine. Note that this function may leave the index
1147N/A exclusively locked, and always sets index->inconsistent = TRUE. */
1132N/A int (*rebuild)(MailIndex *index);
1132N/A
1132N/A /* Verify that the index is valid. If anything invalid is found,
1132N/A rebuild() is called. Same locking issues as with rebuild(). */
1132N/A int (*fsck)(MailIndex *index);
1132N/A
1153N/A /* Synchronize the index with the mailbox. Same locking issues as
1153N/A with rebuild(). */
1153N/A int (*sync)(MailIndex *index);
1153N/A
1172N/A /* Returns the index header (never fails). The index needs to be
970N/A locked before calling this function, and must be kept locked as
970N/A long as you keep using the returned structure. */
1003N/A MailIndexHeader *(*get_header)(MailIndex *index);
1003N/A
1153N/A /* sequence -> data lookup. The index needs to be locked before calling
970N/A this function, and must be kept locked as long as you keep using
970N/A the returned structure. */
1153N/A MailIndexRecord *(*lookup)(MailIndex *index, unsigned int seq);
1172N/A
1172N/A /* Return the next record after specified record, or NULL if it was
970N/A last record. The index must be locked all the time between
1172N/A lookup() and last next() call. */
1154N/A MailIndexRecord *(*next)(MailIndex *index, MailIndexRecord *rec);
970N/A
970N/A /* Find first existing UID in range. */
970N/A MailIndexRecord *(*lookup_uid_range)(MailIndex *index,
970N/A unsigned int first_uid,
970N/A unsigned int last_uid,
970N/A unsigned int *seq_r);
970N/A
970N/A /* Find field from specified record, or NULL if it's not in index.
1105N/A Makes sure that the field ends with \0. */
1105N/A const char *(*lookup_field)(MailIndex *index, MailIndexRecord *rec,
970N/A MailField field);
1105N/A
1105N/A /* Find field from specified record, or NULL if it's not in index. */
1153N/A const void *(*lookup_field_raw)(MailIndex *index, MailIndexRecord *rec,
1153N/A MailField field, size_t *size);
970N/A
1153N/A /* Open mail file and return it as mmap()ed IOBuffer, or
1153N/A NULL if failed. */
1153N/A IOBuffer *(*open_mail)(MailIndex *index, MailIndexRecord *rec);
1153N/A
970N/A /* Expunge a mail from index. Tree and modifylog is also updated. The
970N/A index must be exclusively locked before calling this function.
970N/A If seq is 0, the modify log isn't updated. This is useful if
1152N/A after append() something goes wrong and you wish to delete the
1152N/A mail immediately. If external_change is TRUE, the modify log is
1152N/A always written.
970N/A
970N/A Note that the sequence numbers also update immediately after this
970N/A call, so if you want to delete messages 1..4 just call this
970N/A function 4 times with seq being 1. */
970N/A int (*expunge)(MailIndex *index, MailIndexRecord *rec,
970N/A unsigned int seq, int external_change);
970N/A
970N/A /* Update mail flags. The index must be exclusively locked before
970N/A calling this function. This shouldn't be called in the middle of
970N/A update_begin() as it may modify location field. */
970N/A int (*update_flags)(MailIndex *index, MailIndexRecord *rec,
970N/A unsigned int seq, MailFlags flags,
970N/A int external_change);
970N/A
970N/A /* Append a new record to index. The index must be exclusively
970N/A locked before calling this function. The record pointer is
970N/A updated to the mmap()ed record. rec->uid is updated in
1153N/A append_end(). */
970N/A int (*append_begin)(MailIndex *index, MailIndexRecord **rec);
970N/A int (*append_end)(MailIndex *index, MailIndexRecord *rec);
970N/A
970N/A /* Updating fields happens by calling update_begin(), one or more
970N/A update_field()s and finally update_end() which does the actual
970N/A updating. The index must be exclusively locked all this time.
1152N/A update_begin() and update_field() functions cannot fail.
970N/A
970N/A The extra_space parameter for update_field() specifies the amount
970N/A of extra empty space we should leave after the value, so that if
970N/A the field grows in future it could be expanded without copying it
970N/A to end of file. When the field already exists, the extra_space
1152N/A is ignored.
1152N/A
1152N/A The files may not actually be updated until after you've unlocked
1152N/A the file. */
970N/A MailIndexUpdate *(*update_begin)(MailIndex *index,
970N/A MailIndexRecord *rec);
970N/A int (*update_end)(MailIndexUpdate *update);
970N/A
970N/A void (*update_field)(MailIndexUpdate *update, MailField field,
1046N/A const char *value, size_t extra_space);
1046N/A /* Just remember that full_field_size will be MEM_ALIGNed, so
1046N/A it may differer from the given size parameter. */
1046N/A void (*update_field_raw)(MailIndexUpdate *update, MailField field,
1152N/A const void *value, size_t size);
1152N/A
1152N/A /* Returns last error message */
1152N/A const char *(*get_last_error)(MailIndex *index);
970N/A
1152N/A /* Returns TRUE if last error was because we ran out of available
1152N/A disk space. */
970N/A int (*is_diskspace_error)(MailIndex *index);
970N/A
970N/A /* Returns TRUE if index is now in inconsistent state with the
1046N/A previous known state, meaning that the message IDs etc. may
1152N/A have changed - only way to recover this would be to fully close
970N/A the mailbox and reopen it. With IMAP connection this would mean
970N/A a forced disconnection since we can't do forced CLOSE. */
970N/A int (*is_inconsistency_error)(MailIndex *index);
970N/A
1152N/A/* private: */
1152N/A MailIndexData *data;
1152N/A MailTree *tree;
1152N/A MailModifyLog *modifylog;
1152N/A MailCustomFlags *custom_flags;
1152N/A
1152N/A char *dir; /* directory where to place the index files */
1153N/A char *filepath; /* index file path */
1153N/A unsigned int indexid;
1153N/A unsigned int sync_id;
1153N/A
1153N/A char *mbox_path; /* mbox-specific path to the actual mbox file */
1153N/A uoff_t mbox_size; /* last synced size of mbox file */
1152N/A int mbox_fd;
1153N/A int mbox_locks;
1152N/A int mbox_lock_type;
1152N/A
1153N/A int fd; /* opened index file */
1152N/A char *error; /* last error message */
1152N/A
1152N/A void *mmap_base;
970N/A size_t mmap_used_length;
1153N/A size_t mmap_full_length;
1153N/A
1153N/A MailLockType lock_type;
1153N/A
1153N/A MailIndexHeader *header;
1153N/A unsigned int first_recent_uid;
1153N/A
1153N/A unsigned int modifylog_id;
1153N/A time_t file_sync_stamp;
1130N/A
1130N/A /* these fields are OR'ed to the fields in index header once we
1130N/A get around grabbing exclusive lock */
1130N/A unsigned int set_flags;
1130N/A unsigned int set_cache_fields;
1130N/A
1130N/A unsigned int anon_mmap:1;
1130N/A unsigned int opened:1;
1130N/A unsigned int inconsistent:1;
1130N/A unsigned int nodiskspace:1;
1130N/A};
1130N/A
1130N/A/* needed to remove annoying warnings about not initializing all struct
1130N/A members.. */
1130N/A#define MAIL_INDEX_PRIVATE_FILL \
1161N/A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
1130N/A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
1130N/A 0, 0, 0, 0, 0, 0, 0, 0, 0 \
1130N/A
1130N/A/* defaults - same as above but prefixed with mail_index_. */
1130N/Aint mail_index_open(MailIndex *index, int update_recent, int fast);
1130N/Aint mail_index_open_or_create(MailIndex *index, int update_recent, int fast);
1130N/Aint mail_index_set_lock(MailIndex *index, MailLockType lock_type);
1172N/Aint mail_index_try_lock(MailIndex *index, MailLockType lock_type);
1130N/Aint mail_index_fsck(MailIndex *index);
1130N/AMailIndexHeader *mail_index_get_header(MailIndex *index);
1130N/AMailIndexRecord *mail_index_lookup(MailIndex *index, unsigned int seq);
1130N/AMailIndexRecord *mail_index_next(MailIndex *index, MailIndexRecord *rec);
1130N/AMailIndexRecord *mail_index_lookup_uid_range(MailIndex *index,
1161N/A unsigned int first_uid,
1130N/A unsigned int last_uid,
970N/A unsigned int *seq_r);
970N/Aconst char *mail_index_lookup_field(MailIndex *index, MailIndexRecord *rec,
970N/A MailField field);
1161N/Aconst void *mail_index_lookup_field_raw(MailIndex *index, MailIndexRecord *rec,
970N/A MailField field, size_t *size);
970N/Aint mail_index_expunge(MailIndex *index, MailIndexRecord *rec,
970N/A unsigned int seq, int external_change);
970N/Aint mail_index_update_flags(MailIndex *index, MailIndexRecord *rec,
970N/A unsigned int seq, MailFlags flags,
1139N/A int external_change);
1139N/Aint mail_index_append_begin(MailIndex *index, MailIndexRecord **rec);
970N/Aint mail_index_append_end(MailIndex *index, MailIndexRecord *rec);
970N/AMailIndexUpdate *mail_index_update_begin(MailIndex *index,
970N/A MailIndexRecord *rec);
970N/Aint mail_index_update_end(MailIndexUpdate *update);
970N/Avoid mail_index_update_field(MailIndexUpdate *update, MailField field,
970N/A const char *value, size_t extra_space);
1130N/Avoid mail_index_update_field_raw(MailIndexUpdate *update, MailField field,
970N/A const void *value, size_t size);
1130N/Aconst char *mail_index_get_last_error(MailIndex *index);
970N/Aint mail_index_is_diskspace_error(MailIndex *index);
970N/Aint mail_index_is_inconsistency_error(MailIndex *index);
970N/A
970N/A/* INTERNAL: */
1139N/Aint mail_index_mmap_update(MailIndex *index);
970N/Avoid mail_index_init_header(MailIndexHeader *hdr);
1132N/Avoid mail_index_close(MailIndex *index);
970N/Aint mail_index_fmsync(MailIndex *index, size_t size);
970N/Aint mail_index_verify_hole_range(MailIndex *index);
970N/Avoid mail_index_mark_flag_changes(MailIndex *index, MailIndexRecord *rec,
970N/A MailFlags old_flags, MailFlags new_flags);
970N/Avoid mail_index_update_headers(MailIndexUpdate *update, IOBuffer *inbuf,
970N/A MailField cache_fields,
970N/A MessageHeaderFunc header_func, void *context);
970N/Aint mail_index_update_cache(MailIndex *index);
970N/Aint mail_index_compress(MailIndex *index);
970N/Aint mail_index_compress_data(MailIndex *index);
970N/Aint mail_index_truncate(MailIndex *index);
1139N/A
970N/A/* Max. mmap()ed size for a message */
1130N/A#define MAIL_MMAP_BLOCK_SIZE (1024*256)
1130N/A
1130N/A/* number of records to always keep allocated in index file,
1130N/A either used or unused */
1130N/A#define INDEX_MIN_RECORDS_COUNT 64
1130N/A/* when empty space in index file gets full, grow the file n% larger */
1130N/A#define INDEX_GROW_PERCENTAGE 10
1130N/A/* ftruncate() the index file when only n% of it is in use */
1130N/A#define INDEX_TRUNCATE_PERCENTAGE 30
1130N/A/* don't truncate whole file anyway, keep n% of the empty space */
1130N/A#define INDEX_TRUNCATE_KEEP_PERCENTAGE 10
1130N/A/* Compress the file when deleted space reaches n% of total size */
1130N/A#define INDEX_COMPRESS_PERCENTAGE 50
1130N/A
1139N/A/* uoff_t to index file for given record */
1139N/A#define INDEX_FILE_POSITION(index, ptr) \
1130N/A ((uoff_t) ((char *) (ptr) - (char *) ((index)->mmap_base)))
1130N/A
1130N/A/* index number for uoff_t position */
970N/A#define INDEX_POSITION_INDEX(pos) \
970N/A (((pos) - sizeof(MailIndexHeader)) / sizeof(MailIndexRecord))
970N/A
970N/A/* index number for given record */
970N/A#define INDEX_RECORD_INDEX(index, ptr) \
970N/A INDEX_POSITION_INDEX(INDEX_FILE_POSITION(index, ptr))
970N/A
970N/A/* mark the index corrupted */
970N/A#define INDEX_MARK_CORRUPTED(index) \
970N/A STMT_START { (index)->set_flags |= MAIL_INDEX_FLAG_REBUILD; } STMT_END
970N/A
970N/A/* get number of records in mmaped index */
970N/A#define MAIL_INDEX_RECORD_COUNT(index) \
970N/A ((index->mmap_used_length - sizeof(MailIndexHeader)) / \
970N/A sizeof(MailIndexRecord))
970N/A
970N/A/* minimum size for index file */
970N/A#define INDEX_FILE_MIN_SIZE \
970N/A (sizeof(MailIndexHeader) + \
970N/A INDEX_MIN_RECORDS_COUNT * sizeof(MailIndexRecord))
970N/A
970N/A#endif
970N/A