/* mailbox.h -- Mailbox format definitions
 *
 * Copyright (c) 1994-2008 Carnegie Mellon University.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The name "Carnegie Mellon University" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission. For permission or any legal
 *    details, please contact
 *      Carnegie Mellon University
 *      Center for Technology Transfer and Enterprise Creation
 *      4615 Forbes Avenue
 *      Suite 302
 *      Pittsburgh, PA  15213
 *      (412) 268-7393, fax: (412) 268-7395
 *      innovation@andrew.cmu.edu
 *
 * 4. Redistributions of any form whatsoever must retain the following
 *    acknowledgment:
 *    "This product includes software developed by Computing Services
 *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
 *
 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * $Id: mailbox.h,v 1.98 2010/06/28 12:04:20 brong Exp $
 */

#ifndef INCLUDED_MAILBOX_H
#define INCLUDED_MAILBOX_H

#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <config.h>

#include "auth.h"
#include "byteorder64.h"
#include "message_guid.h"
#include "prot.h"
#include "quota.h"
#include "sequence.h"

#define MAX_MAILBOX_NAME 490
/* enough space for all possible rewrites and DELETED.* and stuff */
#define MAX_MAILBOX_BUFFER 1024
#define MAX_MAILBOX_PATH 4096

#define MAX_USER_FLAGS (16*8)

#define MAILBOX_HEADER_MAGIC ("\241\002\213\015Cyrus mailbox header\n" \
     "\"The best thing about this system was that it had lots of goals.\"\n" \
     "\t--Jim Morris on Andrew\n")

#define MAILBOX_MINOR_VERSION	12
#define MAILBOX_CACHE_MINOR_VERSION 3

#define FNAME_HEADER "/cyrus.header"
#define FNAME_INDEX "/cyrus.index"
#define FNAME_CACHE "/cyrus.cache"
#define FNAME_SQUAT "/cyrus.squat"
#define FNAME_EXPUNGE "/cyrus.expunge"

enum meta_filename {
  META_HEADER = 1,
  META_INDEX,
  META_CACHE,
  META_SQUAT,
  META_EXPUNGE
};

#define MAILBOX_FNAME_LEN 256

#define LOCK_NONE 0
#define LOCK_SHARED 1
#define LOCK_EXCLUSIVE 2
#define LOCK_NONBLOCKING 3

#define NUM_CACHE_FIELDS 10

struct cacheitem {
    unsigned offset;
    unsigned len;
};

struct cacherecord {
    struct buf *base;
    unsigned offset;
    unsigned len;
    struct cacheitem item[NUM_CACHE_FIELDS];
};

struct statusdata {
    const char *userid;
    unsigned statusitems;

    unsigned messages;
    unsigned recent;
    unsigned uidnext;
    unsigned uidvalidity;
    unsigned unseen;
    modseq_t highestmodseq;
};

struct index_record {
    uint32_t uid;
    time_t internaldate;
    time_t sentdate;
    uint32_t size;
    uint32_t header_size;
    time_t gmtime;
    uint32_t cache_offset;
    time_t last_updated;
    bit32 system_flags;
    bit32 user_flags[MAX_USER_FLAGS/32];
    uint32_t content_lines;
    uint32_t cache_version;
    struct message_guid guid;
    modseq_t modseq;
    bit32 cache_crc;
    bit32 record_crc;

    /* metadata */
    uint32_t recno;
    int silent;
    struct cacherecord crec;
};

struct index_header {
    /* track if it's been changed */
    int dirty;

    /* header fields */
    bit32 generation_no;
    int format;
    int minor_version;
    uint32_t start_offset;
    uint32_t record_size;
    uint32_t num_records;
    time_t last_appenddate;
    uint32_t last_uid;
    uquota_t quota_mailbox_used;
    time_t pop3_last_login;
    uint32_t uidvalidity;

    uint32_t deleted;
    uint32_t answered;
    uint32_t flagged;

    uint32_t options;
    uint32_t leaked_cache_records;
    modseq_t highestmodseq;
    modseq_t deletedmodseq;
    uint32_t exists;
    time_t first_expunged;
    time_t last_repack_time;

    bit32 header_file_crc;
    bit32 sync_crc;

    uint32_t recentuid;
    time_t recenttime;

    uint32_t header_crc;
};

struct mailbox {
    int index_fd;
    int cache_fd;
    int lock_fd;
    int header_fd;

    const char *index_base;
    unsigned long index_len;	/* mapped size */
    struct buf cache_buf;
    unsigned long cache_len;	/* mapped size */

    int index_locktype; /* 0 = none, 1 = shared, 2 = exclusive */

    ino_t header_file_ino;
    bit32 header_file_crc;

    time_t index_mtime;
    ino_t index_ino;
    size_t index_size;
    int need_cache_refresh;

    /* Information in mailbox list */
    char *name;
    int mbtype;
    char *part;
    char *acl;

    struct index_header i;

    /* Information in header */
    char *uniqueid;
    char *quotaroot;
    char *flagname[MAX_USER_FLAGS];

    /* change management */
    int modseq_dirty;
    int header_dirty;
    int cache_dirty;
    int quota_dirty;
    int has_changed;
    time_t last_updated; /* for appends*/
    quota_t quota_previously_used; /* for quota change */
};

/* Offsets of index/expunge header fields
 *
 * NOTE: Since we might be using a 64-bit MODSEQ in the index record,
 *       the size of the index header MUST be a multiple of 8 bytes.
 */
#define OFFSET_GENERATION_NO 0
#define OFFSET_FORMAT 4
#define OFFSET_MINOR_VERSION 8
#define OFFSET_START_OFFSET 12
#define OFFSET_RECORD_SIZE 16
#define OFFSET_NUM_RECORDS 20
#define OFFSET_LAST_APPENDDATE 24
#define OFFSET_LAST_UID 28
#define OFFSET_QUOTA_MAILBOX_USED64 32  /* offset for 64bit quotas */
#define OFFSET_QUOTA_MAILBOX_USED 36    /* offset for 32bit quotas */
#define OFFSET_POP3_LAST_LOGIN 40
#define OFFSET_UIDVALIDITY 44
#define OFFSET_DELETED 48      /* added for ACAP */
#define OFFSET_ANSWERED 52
#define OFFSET_FLAGGED 56
#define OFFSET_MAILBOX_OPTIONS 60
#define OFFSET_LEAKED_CACHE 64     /* Number of leaked records in cache file */
#define OFFSET_HIGHESTMODSEQ_64 68 /* CONDSTORE (64-bit modseq) */
#define OFFSET_HIGHESTMODSEQ 72    /* CONDSTORE (32-bit modseq) */
#define OFFSET_DELETEDMODSEQ_64 76 /* CONDSTORE (64-bit modseq) */
#define OFFSET_DELETEDMODSEQ 80    /* CONDSTORE (32-bit modseq) */
#define OFFSET_EXISTS 84           /* Non-expunged records */
#define OFFSET_FIRST_EXPUNGED 88   /* last_updated of oldest expunged message */
#define OFFSET_LAST_REPACK_TIME 92 /* time of last expunged cleanup  */
#define OFFSET_HEADER_FILE_CRC 96  /* CRC32 of the index header file */
#define OFFSET_SYNC_CRC 100        /* XOR of SYNC CRCs of unexpunged records */
#define OFFSET_RECENTUID 104       /* last UID the owner was told about */
#define OFFSET_RECENTTIME 108      /* last timestamp for seen data */
#define OFFSET_SPARE0 112 /* Spares - only use these if the index */
#define OFFSET_SPARE1 116 /*  record size remains the same */
#define OFFSET_SPARE2 120 /*  (see note above about spares) */
#define OFFSET_HEADER_CRC 124 /* includes all zero for the spares! */

/* Offsets of index_record fields in index/expunge file
 *
 * NOTE: Since we might be using a 64-bit MODSEQ in the index record,
 *       OFFSET_MODSEQ_64 and the size of the index record MUST be
 *       multiples of 8 bytes.
 */
#define OFFSET_UID 0
#define OFFSET_INTERNALDATE 4
#define OFFSET_SENTDATE 8
#define OFFSET_SIZE 12
#define OFFSET_HEADER_SIZE 16
#define OFFSET_GMTIME 20
#define OFFSET_CACHE_OFFSET 24
#define OFFSET_LAST_UPDATED 28
#define OFFSET_SYSTEM_FLAGS 32
#define OFFSET_USER_FLAGS 36
#define OFFSET_CONTENT_LINES 52 /* added for nntpd */
#define OFFSET_CACHE_VERSION 56
#define OFFSET_MESSAGE_GUID 60
#define OFFSET_MODSEQ_64 80 /* CONDSTORE (64-bit modseq) */
#define OFFSET_MODSEQ 84 /* CONDSTORE (32-bit modseq) */
#define OFFSET_CACHE_CRC 88 /* CRC32 of cache record */
#define OFFSET_RECORD_CRC 92


#define INDEX_HEADER_SIZE (OFFSET_HEADER_CRC+4)
#define INDEX_RECORD_SIZE (OFFSET_RECORD_CRC+4)

#define FLAG_ANSWERED (1<<0)
#define FLAG_FLAGGED (1<<1)
#define FLAG_DELETED (1<<2)
#define FLAG_DRAFT (1<<3)
#define FLAG_SEEN (1<<4)
#define FLAG_UNLINKED (1<<30)
#define FLAG_EXPUNGED (1U<<31)

#define OPT_POP3_NEW_UIDL (1<<0)	/* added for Outlook stupidity */
/* NOTE: not used anymore - but don't reuse it */
#define OPT_IMAP_CONDSTORE (1<<1)	/* added for CONDSTORE extension */

/* these two are annotations, if you add more, update annotate.c
 * struct annotate_mailbox_flags */
#define OPT_IMAP_SHAREDSEEN (1<<2)	/* added for shared \Seen flag */
#define OPT_IMAP_DUPDELIVER (1<<3)	/* added to allow duplicate delivery */
#define OPT_MAILBOX_NEEDS_UNLINK (1<<29)	/* files to be unlinked */
#define OPT_MAILBOX_NEEDS_REPACK (1<<30)	/* repacking to do */
#define OPT_MAILBOX_DELETED (1U<<31)	/* mailbox is deleted an awaiting cleanup */

#define MAILBOX_OPTIONS_MASK (OPT_POP3_NEW_UIDL | \
			      OPT_IMAP_SHAREDSEEN | \
			      OPT_IMAP_DUPDELIVER)
#define MAILBOX_CLEANUP_MASK (OPT_MAILBOX_NEEDS_UNLINK | \
			      OPT_MAILBOX_NEEDS_REPACK | \
			      OPT_MAILBOX_DELETED)
#define MAILBOX_OPT_VALID (MAILBOX_OPTIONS_MASK | \
			   MAILBOX_CLEANUP_MASK)

/* reconstruct flags */
#define RECONSTRUCT_QUIET           (1<<1)
#define RECONSTRUCT_MAKE_CHANGES    (1<<2)
#define RECONSTRUCT_DO_STAT         (1<<3)
#define RECONSTRUCT_ALWAYS_PARSE    (1<<4)
#define RECONSTRUCT_GUID_REWRITE    (1<<5)
#define RECONSTRUCT_GUID_UNLINK     (1<<6)
#define RECONSTRUCT_REMOVE_ODDFILES (1<<7)
#define RECONSTRUCT_IGNORE_ODDFILES (1<<8)

struct mailbox_header_cache {
    const char *name; /* Name of header */
    bit32 min_cache_version; /* Cache version it appeared in */
};

#define MAX_CACHED_HEADER_SIZE 32 /* Max size of a cached header name */
extern const struct mailbox_header_cache mailbox_cache_headers[];
extern const int MAILBOX_NUM_CACHE_HEADERS;

/* Aligned buffer for manipulating index header/record fields */
typedef union {
    unsigned char buf[INDEX_HEADER_SIZE > INDEX_RECORD_SIZE ?
		      INDEX_HEADER_SIZE : INDEX_RECORD_SIZE];
#ifdef HAVE_LONG_LONG_INT
    bit64 align8; /* align on 8-byte boundary */
#else
    bit32 align4; /* align on 4-byte boundary */
#endif
} indexbuffer_t;

/* Access assistance macros for memory-mapped cache file data */
/* CACHE_ITEM_BIT32: Convert to host byte order */
/* CACHE_ITEM_LEN: Get the length out */
/* CACHE_ITEM_NEXT: Return a pointer to the next entry.  Sizes are
 * 4-byte aligned, so round up to the next 4 byte boundry */
#define CACHE_ITEM_BIT32(ptr) (ntohl(*((bit32 *)(ptr))))
#define CACHE_ITEM_LEN(ptr) CACHE_ITEM_BIT32(ptr)
#define CACHE_ITEM_NEXT(ptr) ((ptr)+4+((3+CACHE_ITEM_LEN(ptr))&~3))

/* Size of a bit32 to skip when jumping over cache item sizes */
#define CACHE_ITEM_SIZE_SKIP sizeof(bit32)

/* Cache item positions */
enum {
    CACHE_ENVELOPE = 0,
    CACHE_BODYSTRUCTURE,
    CACHE_BODY,
    CACHE_SECTION,
    CACHE_HEADERS,
    CACHE_FROM,
    CACHE_TO,
    CACHE_CC,
    CACHE_BCC,
    CACHE_SUBJECT
};

/* Cached envelope token positions */
enum {
    ENV_DATE = 0,
    ENV_SUBJECT,
    ENV_FROM,
    ENV_SENDER,
    ENV_REPLYTO,
    ENV_TO,
    ENV_CC,
    ENV_BCC,
    ENV_INREPLYTO,
    ENV_MSGID
};
#define NUMENVTOKENS (10)

unsigned mailbox_cached_header(const char *s);
unsigned mailbox_cached_header_inline(const char *text);

typedef unsigned mailbox_decideproc_t(struct mailbox *mailbox,
				      struct index_record *index,
				      void *rock);

typedef void mailbox_notifyproc_t(const char *mboxname);

extern void mailbox_set_updatenotifier(mailbox_notifyproc_t *notifyproc);
extern mailbox_notifyproc_t *mailbox_get_updatenotifier(void);

/* file names on disk */
#define META_FNAME_NEW 1
extern char *mailbox_meta_fname(struct mailbox *mailbox, int metafile);
extern char *mailbox_meta_newfname(struct mailbox *mailbox, int metafile);
extern int mailbox_meta_rename(struct mailbox *mailbox, int metafile);

extern char *mailbox_message_fname(struct mailbox *mailbox, 
				   unsigned long uid);
extern char *mailbox_datapath(struct mailbox *mailbox);

/* map individual messages in */
extern int mailbox_map_message(struct mailbox *mailbox, unsigned long uid,
				  const char **basep, unsigned long *lenp);
extern void mailbox_unmap_message(struct mailbox *mailbox,
				  unsigned long uid,
				  const char **basep, unsigned long *lenp);

/* cache record API */
int mailbox_open_cache(struct mailbox *mailbox);
int cache_parserecord(struct buf *cachebase,
		      unsigned cache_offset, struct cacherecord *crec);
int mailbox_cacherecord(struct mailbox *mailbox,
			struct index_record *record);
int cache_append_record(int fd, struct index_record *record);
int mailbox_append_cache(struct mailbox *mailbox,
			 struct index_record *record);
const char *cacheitem_base(struct index_record *record, int field);
unsigned cacheitem_size(struct index_record *record, int field);
struct buf *cacheitem_buf(struct index_record *record, int field);
const char *cache_base(struct index_record *record);
unsigned cache_size(struct index_record *record);
struct buf *cache_buf(struct index_record *record);
/* opening and closing */
extern int mailbox_open_iwl(const char *name,
			    struct mailbox **mailboxptr);
extern int mailbox_open_irl(const char *name,
			    struct mailbox **mailboxptr);
extern int mailbox_open_exclusive(const char *name,
			          struct mailbox **mailboxptr);
extern void mailbox_close(struct mailbox **mailboxptr);
extern int mailbox_delete(struct mailbox **mailboxptr);

/* reading bits and pieces */
extern int mailbox_read_header(struct mailbox *mailbox, char **aclptr);
extern int mailbox_refresh_index_header(struct mailbox *mailbox);
extern int mailbox_write_header(struct mailbox *mailbox, int force);
extern void mailbox_index_dirty(struct mailbox *mailbox);
extern void mailbox_modseq_dirty(struct mailbox *mailbox);
extern int mailbox_read_index_record(struct mailbox *mailbox,
				     uint32_t recno,
				     struct index_record *record);
extern int mailbox_rewrite_index_record(struct mailbox *mailbox,
				        struct index_record *record);
extern int mailbox_append_index_record(struct mailbox *mailbox,
				       struct index_record *record);

extern int mailbox_set_acl(struct mailbox *mailbox, const char *acl,
			   int dirty_modseq);
extern int mailbox_set_quotaroot(struct mailbox *mailbox, const char *quotaroot);
extern int mailbox_user_flag(struct mailbox *mailbox, const char *flag,
			     int *flagnum);
extern int mailbox_commit(struct mailbox *mailbox);

/* seen state check */
extern int mailbox_internal_seen(struct mailbox *mailbox, const char *userid);

/* index locking operations */
extern int mailbox_lock_index(struct mailbox *mailbox, int locktype);

extern int mailbox_expunge_cleanup(struct mailbox *mailbox, time_t expunge_mark,
				   unsigned *ndeleted);
extern int mailbox_expunge(struct mailbox *mailbox,
			   mailbox_decideproc_t *decideproc, void *deciderock,
			   unsigned *nexpunged);
extern int mailbox_cleanup(struct mailbox *mailbox, int iscurrentdir,
			   mailbox_decideproc_t *decideproc, void *deciderock);
extern void mailbox_unlock_index(struct mailbox *mailbox, struct statusdata *sd);

extern int mailbox_create(const char *name, const char *part, const char *acl,
			  const char *uniqueid, int options, unsigned uidvalidity,
			  struct mailbox **mailboxptr);

extern int mailbox_copy_files(struct mailbox *mailbox, const char *newpart,
			      const char *newname);
extern int mailbox_delete_cleanup(const char *part, const char *name);

extern int mailbox_rename_copy(struct mailbox *oldmailbox, 
			       const char *newname, const char *newpart,
			       const char *userid, int ignorequota,
			       struct mailbox **newmailboxptr);
extern int mailbox_rename_cleanup(struct mailbox **mailboxptr, int isinbox);


extern int mailbox_copyfile(const char *from, const char *to, int nolink);

extern int mailbox_reconstruct(const char *name, int flags);

extern int mailbox_index_recalc(struct mailbox *mailbox);

/* for upgrade index */
extern int mailbox_open_index(struct mailbox *mailbox);
extern int mailbox_buf_to_index_record(const char *buf,
				       struct index_record *record);
extern int mailbox_buf_to_index_header(const char *buf,
				       struct index_header *i);

/* for repack */
struct mailbox_repack {
    struct mailbox *mailbox;
    struct index_header i;
    int newindex_fd;
    int newcache_fd;
};

extern int mailbox_repack_setup(struct mailbox *mailbox,
			        struct mailbox_repack **repackptr);
extern int mailbox_repack_add(struct mailbox_repack *repack,
			      struct index_record *record);
extern void mailbox_repack_abort(struct mailbox_repack **repackptr);
extern int mailbox_repack_commit(struct mailbox_repack **repackptr);

#endif /* INCLUDED_MAILBOX_H */
