EXPORTED int mailbox_create(const char *name,
                   uint32_t mbtype,
                   const char *part,
                   const char *acl,
                   const char *uniqueid,
                   int options,
                   unsigned uidvalidity,
                   modseq_t createdmodseq,
                   modseq_t highestmodseq,
                   struct mailbox **mailboxptr)
{
    int r = 0;
    char quotaroot[MAX_MAILBOX_BUFFER];
    int hasquota;
    const char *fname;
    struct mailbox *mailbox = NULL;
    int n;
    int createfnames[] = { META_INDEX, META_HEADER, 0 };
    struct mailboxlist *listitem;

    assert(uniqueid);

    /* if we already have this name open then that's an error too */
    listitem = find_listitem(name);
    if (listitem) return IMAP_MAILBOX_LOCKED;

    listitem = create_listitem(name);
    mailbox = &listitem->m;

    /* needs to be an exclusive namelock to create a mailbox */
    char *userid = mboxname_to_userid(name);
    if (userid) {
        int haslock = user_isnamespacelocked(userid);
        assert(haslock == LOCK_EXCLUSIVE);
        free(userid);
    }

    uint32_t legacy_dirs = (mbtype & MBTYPE_LEGACY_DIRS);
    r = mboxname_lock(legacy_dirs ? name : uniqueid, &listitem->l, LOCK_EXCLUSIVE);
    if (r) {
        if (mailbox->local_namespacelock)
            mboxname_release(&mailbox->local_namespacelock);
        remove_listitem(listitem);
        return r;
    }

    // fill out the initial mbentry (XXX: pass it in from mboxlist.c)
    mailbox->mbentry = mboxlist_entry_create();
    mailbox->mbentry->name = xstrdup(name);
    mailbox->mbentry->partition = xstrdup(part);
    mailbox->mbentry->acl = xstrdup(acl);
    mailbox->mbentry->mbtype = mbtype;
    mailbox->mbentry->uniqueid = xstrdup(uniqueid);

    // fill out the header too
    mailbox->h.name = xstrdup(name);
    mailbox->h.acl = xstrdup(acl);
    mailbox->h.mbtype = mbtype;
    mailbox->h.uniqueid = xstrdup(uniqueid);

    // if we've been given a highestmodseq, we don't update it
    if (highestmodseq) mailbox->silentchanges = 1;

    // set the quotaroot if any
    hasquota = quota_findroot(quotaroot, sizeof(quotaroot), name);
    if (hasquota) {
        mailbox->h.quotaroot = xstrdup(quotaroot);
        mailbox->quota_dirty = 1;
    }

    /* ensure all paths exist */
    for (n = 0; createfnames[n]; n++) {
        fname = mailbox_meta_fname(mailbox, createfnames[n]);
        if (!fname) {
            xsyslog(LOG_ERR, "IOERROR: Mailbox name too long",
                             "mailbox=<%s>",
                             mailbox_name(mailbox));
            r = IMAP_MAILBOX_BADNAME;
            goto done;
        }
        if (cyrus_mkdir(fname, 0755) == -1) {
            xsyslog(LOG_ERR, "IOERROR: mkdir failed",
                             "fname=<%s>",
                             fname);
            r = IMAP_IOERROR;
            goto done;
        }
    }

    /* ensure we can fit the longest possible file name */
    fname = mailbox_datapath(mailbox, 0);
    if (!fname) {
        xsyslog(LOG_ERR, "IOERROR: Mailbox name too long",
                         "mailbox=<%s>",
                         mailbox_name(mailbox));
        r = IMAP_MAILBOX_BADNAME;
        goto done;
    }
    /* and create the directory too :) */
    if (cyrus_mkdir(fname, 0755) == -1) {
        xsyslog(LOG_ERR, "IOERROR: mkdir failed",
                         "fname=<%s>",
                         fname);
        r = IMAP_IOERROR;
        goto done;
    }

    fname = mailbox_meta_fname(mailbox, META_INDEX);
    if (!fname) {
        xsyslog(LOG_ERR, "IOERROR: Mailbox name too long",
                         "mailbox=<%s>",
                         mailbox_name(mailbox));
        r = IMAP_MAILBOX_BADNAME;
        goto done;
    }
    mailbox->index_fd = open(fname, O_RDWR|O_TRUNC|O_CREAT, 0666);
    if (mailbox->index_fd == -1) {
        xsyslog(LOG_ERR, "IOERROR: create index failed",
                         "fname=<%s>",
                         fname);
        r = IMAP_IOERROR;
        goto done;
    }
    r = lock_blocking(mailbox->index_fd, fname);
    if (r) {
        xsyslog(LOG_ERR, "IOERROR: lock index failed",
                         "fname=<%s>",
                         fname);
        r = IMAP_IOERROR;
        goto done;
    }
    mailbox->index_locktype = LOCK_EXCLUSIVE;

    /* ensure a UIDVALIDITY is set */
    if (!uidvalidity)
        uidvalidity = mboxname_nextuidvalidity(name, time(0));
    else
        mboxname_setuidvalidity(mailbox_name(mailbox), uidvalidity);
    mailbox->mbentry->uidvalidity = uidvalidity;

    /* and highest modseq */
    if (!highestmodseq)
        highestmodseq = mboxname_nextmodseq(mailbox_name(mailbox), 0, mbtype,
                                            MBOXMODSEQ_ISFOLDER);
    else
        mboxname_setmodseq(mailbox_name(mailbox), highestmodseq, mbtype,
                           MBOXMODSEQ_ISFOLDER);

    /* and created modseq */
    if (!createdmodseq || createdmodseq > highestmodseq)
        createdmodseq = highestmodseq;

    /* init non-zero fields */
    mailbox_index_dirty(mailbox);
    mailbox->i.minor_version = MAILBOX_MINOR_VERSION;
    mailbox->i.start_offset = INDEX_HEADER_SIZE;
    mailbox->i.record_size = INDEX_RECORD_SIZE;
    mailbox->i.options = options;
    mailbox->i.uidvalidity = uidvalidity;
    mailbox->i.createdmodseq = createdmodseq;
    mailbox->i.highestmodseq = highestmodseq;
    mailbox->i.changes_epoch = time(0);
    mailbox->i.synccrcs.basic = CRC_INIT_BASIC;
    mailbox->i.synccrcs.annot = CRC_INIT_ANNOT;

    /* initialise header size field so appends calculate the
     * correct map size */
    mailbox->index_size = INDEX_HEADER_SIZE;

    mailbox->header_dirty = 1;

    r = seen_create_mailbox(NULL, mailbox);
    if (r) goto done;
    r = mailbox_commit(mailbox);
    if (r) goto done;

    if (config_auditlog)
        syslog(LOG_NOTICE, "auditlog: create sessionid=<%s> "
                           "mailbox=<%s> uniqueid=<%s> uidvalidity=<%u>",
                           session_id(), mailbox_name(mailbox),
                           mailbox_uniqueid(mailbox), mailbox->i.uidvalidity);

done:
    if (!r && mailboxptr)
        *mailboxptr = mailbox;
    else
        mailbox_close(&mailbox);

    return r;
}
EXPORTED int mailbox_create(const char *name,
                   uint32_t mbtype,
                   const char *part,
                   const char *acl,
                   const char *uniqueid,
                   int options,
                   unsigned uidvalidity,
                   modseq_t createdmodseq,
                   modseq_t highestmodseq,
                   struct mailbox **mailboxptr)
{
    int r = 0;
    char quotaroot[MAX_MAILBOX_BUFFER];
    int hasquota;
    const char *fname;
    struct mailbox *mailbox = NULL;
    int n;
    int createfnames[] = { META_INDEX, META_HEADER, 0 };
    struct mailboxlist *listitem;

    assert(uniqueid);

     
    listitem = find_listitem(name);
    if (listitem) return IMAP_MAILBOX_LOCKED;

    listitem = create_listitem(name);
    mailbox = &listitem->m;

     
    char *userid = mboxname_to_userid(name);
    if (userid) {
        int haslock = user_isnamespacelocked(userid);
        assert(haslock == LOCK_EXCLUSIVE);
        free(userid);
    }

    uint32_t legacy_dirs = (mbtype & MBTYPE_LEGACY_DIRS);
    r = mboxname_lock(legacy_dirs ? name : uniqueid, &listitem->l, LOCK_EXCLUSIVE);
    if (r) {
        if (mailbox->local_namespacelock)
            mboxname_release(&mailbox->local_namespacelock);
        remove_listitem(listitem);
        return r;
    }

     
    mailbox->mbentry = mboxlist_entry_create();
    mailbox->mbentry->name = xstrdup(name);
    mailbox->mbentry->partition = xstrdup(part);
    mailbox->mbentry->acl = xstrdup(acl);
    mailbox->mbentry->mbtype = mbtype;
    mailbox->mbentry->uniqueid = xstrdup(uniqueid);

     
    mailbox->h.name = xstrdup(name);
    mailbox->h.acl = xstrdup(acl);
    mailbox->h.mbtype = mbtype;
    mailbox->h.uniqueid = xstrdup(uniqueid);

     
    if (highestmodseq) mailbox->silentchanges = 1;

     
    hasquota = quota_findroot(quotaroot, sizeof(quotaroot), name);
    if (hasquota) {
        mailbox->h.quotaroot = xstrdup(quotaroot);
        mailbox->quota_dirty = 1;
    }

     
    for (n = 0; createfnames[n]; n++) {
        fname = mailbox_meta_fname(mailbox, createfnames[n]);
        if (!fname) {
            xsyslog(LOG_ERR, "IOERROR: Mailbox name too long",
                             "mailbox=<%s>",
                             mailbox_name(mailbox));
            r = IMAP_MAILBOX_BADNAME;
            goto done;
        }
        if (cyrus_mkdir(fname, 0755) == -1) {
            xsyslog(LOG_ERR, "IOERROR: mkdir failed",
                             "fname=<%s>",
                             fname);
            r = IMAP_IOERROR;
            goto done;
        }
    }

     
    fname = mailbox_datapath(mailbox, 0);
    if (!fname) {
        xsyslog(LOG_ERR, "IOERROR: Mailbox name too long",
                         "mailbox=<%s>",
                         mailbox_name(mailbox));
        r = IMAP_MAILBOX_BADNAME;
        goto done;
    }
     
    if (cyrus_mkdir(fname, 0755) == -1) {
        xsyslog(LOG_ERR, "IOERROR: mkdir failed",
                         "fname=<%s>",
                         fname);
        r = IMAP_IOERROR;
        goto done;
    }

    fname = mailbox_meta_fname(mailbox, META_INDEX);
    if (!fname) {
        xsyslog(LOG_ERR, "IOERROR: Mailbox name too long",
                         "mailbox=<%s>",
                         mailbox_name(mailbox));
        r = IMAP_MAILBOX_BADNAME;
        goto done;
    }
    mailbox->index_fd = open(fname, O_RDWR|O_TRUNC|O_CREAT, 0666);
    if (mailbox->index_fd == -1) {
        xsyslog(LOG_ERR, "IOERROR: create index failed",
                         "fname=<%s>",
                         fname);
        r = IMAP_IOERROR;
        goto done;
    }
    r = lock_blocking(mailbox->index_fd, fname);
    if (r) {
        xsyslog(LOG_ERR, "IOERROR: lock index failed",
                         "fname=<%s>",
                         fname);
        r = IMAP_IOERROR;
        goto done;
    }
    mailbox->index_locktype = LOCK_EXCLUSIVE;

     
    if (!uidvalidity)
        uidvalidity = mboxname_nextuidvalidity(name, time(0));
    else
        mboxname_setuidvalidity(mailbox_name(mailbox), uidvalidity);
    mailbox->mbentry->uidvalidity = uidvalidity;

     
    if (!highestmodseq)
        highestmodseq = mboxname_nextmodseq(mailbox_name(mailbox), 0, mbtype,
                                            MBOXMODSEQ_ISFOLDER);
    else
        mboxname_setmodseq(mailbox_name(mailbox), highestmodseq, mbtype,
                           MBOXMODSEQ_ISFOLDER);

     
    if (!createdmodseq || createdmodseq > highestmodseq)
        createdmodseq = highestmodseq;

     
    mailbox_index_dirty(mailbox);
    mailbox->i.minor_version = MAILBOX_MINOR_VERSION;
    mailbox->i.start_offset = INDEX_HEADER_SIZE;
    mailbox->i.record_size = INDEX_RECORD_SIZE;
    mailbox->i.options = options;
    mailbox->i.uidvalidity = uidvalidity;
    mailbox->i.createdmodseq = createdmodseq;
    mailbox->i.highestmodseq = highestmodseq;
    mailbox->i.changes_epoch = time(0);
    mailbox->i.synccrcs.basic = CRC_INIT_BASIC;
    mailbox->i.synccrcs.annot = CRC_INIT_ANNOT;

     
    mailbox->index_size = INDEX_HEADER_SIZE;

    mailbox->header_dirty = 1;

    r = seen_create_mailbox(NULL, mailbox);
    if (r) goto done;
    r = mailbox_commit(mailbox);
    if (r) goto done;

    if (config_auditlog)
        syslog(LOG_NOTICE, "auditlog: create sessionid=<%s> "
                           "mailbox=<%s> uniqueid=<%s> uidvalidity=<%u>",
                           session_id(), mailbox_name(mailbox),
                           mailbox_uniqueid(mailbox), mailbox->i.uidvalidity);

done:
    if (!r && mailboxptr)
        *mailboxptr = mailbox;
    else
        mailbox_close(&mailbox);

    return r;
}