Page MenuHomePhorge

mailbox.c
No OneTemporary

Authored By
Unknown
Size
50 KB
Referenced Files
None
Subscribers
None

mailbox.c

/* mailbox.c -- Mailbox manipulation routines
*
* (C) Copyright 1994 by Carnegie Mellon University
*
* All Rights Reserved
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without
* fee, provided that the above copyright notice appear in all copies
* and that both that copyright notice and this permission notice
* appear in supporting documentation, and that the name of Carnegie
* Mellon University not be used in advertising or publicity
* pertaining to distribution of the software without specific,
* written prior permission. Carnegie Mellon University makes no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied
* warranty.
*
* 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.
*
*/
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <sys/types.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/stat.h>
#if HAVE_DIRENT_H
# include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
#else
# define dirent direct
# define NAMLEN(dirent) (dirent)->d_namlen
# if HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# if HAVE_NDIR_H
# include <ndir.h>
# endif
#endif
#include "assert.h"
#include "config.h"
#include "acl.h"
#include "map.h"
#include "retry.h"
#include "util.h"
#include "lock.h"
#include "sysexits.h"
#include "imap_err.h"
#include "mailbox.h"
#include "xmalloc.h"
static int mailbox_doing_reconstruct = 0;
static struct mailbox zeromailbox = {-1, -1, -1};
/*
* Names of the headers we cache in the cyrus.cache file.
* Any changes to this list require corresponding changes to
* message_parse_headers() in message.c
*/
char *mailbox_cache_header_name[] = {
/* "in-reply-to", in ENVELOPE */
"priority",
"references",
"resent-from",
"newsgroups",
"followup-to",
};
int mailbox_num_cache_header =
sizeof(mailbox_cache_header_name)/sizeof(char *);
/*
* Calculate relative filename for the message with UID 'uid'
* in 'mailbox'. Returns pointer to static buffer.
*/
char *mailbox_message_fname(mailbox, uid)
struct mailbox *mailbox;
unsigned long uid;
{
static char buf[256];
char *p = buf;
assert(mailbox->format != MAILBOX_FORMAT_NETNEWS);
sprintf(p, "%lu.", uid);
return buf;
}
/*
* Maps in the content for the message with UID 'uid' in 'mailbox'.
* Returns map in 'basep' and 'lenp'
*/
int
mailbox_map_message(mailbox, iscurrentdir, uid, basep, lenp)
struct mailbox *mailbox;
int iscurrentdir;
unsigned long uid;
const char **basep;
unsigned long *lenp;
{
int msgfd;
static char buf[4096];
char *p = buf;
struct stat sbuf;
static int newsprefixlen = -1;
if (newsprefixlen == -1) {
const char *newsprefix;
newsprefix = config_getstring("newsprefix", 0);
if (newsprefix) {
newsprefixlen = strlen(newsprefix);
if (newsprefix[newsprefixlen-1] != '.') newsprefixlen++;
}
else newsprefixlen = 0;
}
if (mailbox->format == MAILBOX_FORMAT_NETNEWS && config_newsspool) {
strcpy(buf, config_newsspool);
p = buf + strlen(buf);
if (p[-1] != '/') *p++ = '/';
strcpy(p, mailbox->name + newsprefixlen);
while (*p) {
if (*p == '.') *p = '/';
p++;
}
*p++ = '/';
}
else if (!iscurrentdir) {
strcpy(buf, mailbox->path);
p = buf + strlen(buf);
}
sprintf(p, "%lu%s", uid,
mailbox->format == MAILBOX_FORMAT_NETNEWS ? "" : ".");
msgfd = open(buf, O_RDONLY, 0666);
if (msgfd == -1) return errno;
if (fstat(msgfd, &sbuf) == -1) {
syslog(LOG_ERR, "IOERROR: fstat on %s: %m", buf);
fatal("can't fstat message file", EX_OSFILE);
}
*basep = 0;
*lenp = 0;
map_refresh(msgfd, 1, basep, lenp, sbuf.st_size, buf, mailbox->name);
close(msgfd);
return 0;
}
/*
* Releases the buffer obtained from mailbox_map_message()
*/
void
mailbox_unmap_message(mailbox, uid, basep, lenp)
struct mailbox *mailbox;
unsigned long uid;
const char **basep;
unsigned long *lenp;
{
map_free(basep, lenp);
}
/*
* Set the "reconstruct" mode. Causes most errors to be ignored.
*/
void
mailbox_reconstructmode()
{
mailbox_doing_reconstruct = 1;
}
/*
* Open and read the header of the mailbox with name 'name'
* The structure pointed to by 'mailbox' is initialized.
*/
int
mailbox_open_header(name, auth_state, mailbox)
const char *name;
struct auth_state *auth_state;
struct mailbox *mailbox;
{
char *path, *acl;
int r;
r = mboxlist_lookup(name, &path, &acl);
if (r) return r;
return mailbox_open_header_path(name, path, acl, auth_state, mailbox, 0);
}
/*
* Open and read the header of the mailbox with name 'name'
* path 'path', and ACL 'acl'.
* The structure pointed to by 'mailbox' is initialized.
*/
int
mailbox_open_header_path (name, path, acl, auth_state, mailbox, suppresslog)
const char *name;
const char *path;
const char *acl;
struct auth_state *auth_state;
struct mailbox *mailbox;
int suppresslog;
{
char fnamebuf[MAX_MAILBOX_PATH];
struct stat sbuf;
int r;
*mailbox = zeromailbox;
mailbox->quota.fd = -1;
strcpy(fnamebuf, path);
strcat(fnamebuf, FNAME_HEADER);
mailbox->header_fd = open(fnamebuf, O_RDWR, 0);
if (mailbox->header_fd == -1 && !mailbox_doing_reconstruct) {
if (!suppresslog) {
syslog(LOG_ERR, "IOERROR: opening %s: %m", fnamebuf);
}
return IMAP_IOERROR;
}
if (mailbox->header_fd != -1) {
if (fstat(mailbox->header_fd, &sbuf) == -1) {
syslog(LOG_ERR, "IOERROR: fstating %s: %m", fnamebuf);
fatal("can't fstat header file", EX_OSFILE);
}
map_refresh(mailbox->header_fd, 1, &mailbox->header_base,
&mailbox->header_len, sbuf.st_size, "header", name);
mailbox->header_ino = sbuf.st_ino;
}
mailbox->name = xstrdup(name);
mailbox->path = xstrdup(path);
mailbox->acl = xstrdup(acl);
mailbox->myrights = acl_myrights(auth_state, mailbox->acl);
if (mailbox->header_fd == -1) return 0;
r = mailbox_read_header(mailbox);
if (r && !mailbox_doing_reconstruct) {
mailbox_close(mailbox);
return r;
}
return 0;
}
#define MAXTRIES 60
/*
* Open the index and cache files for 'mailbox'. Also
* read the index header.
*/
int
mailbox_open_index(mailbox)
struct mailbox *mailbox;
{
char fnamebuf[MAX_MAILBOX_PATH];
bit32 index_gen = 0, cache_gen = 0;
int tries = 0;
if (mailbox->index_fd != -1) {
close(mailbox->index_fd);
map_free(&mailbox->index_base, &mailbox->index_len);
}
if (mailbox->cache_fd != -1) {
close(mailbox->cache_fd);
map_free(&mailbox->cache_base, &mailbox->cache_len);
}
do {
strcpy(fnamebuf, mailbox->path);
strcat(fnamebuf, FNAME_INDEX);
mailbox->index_fd = open(fnamebuf, O_RDWR, 0);
if (mailbox->index_fd != -1) {
map_refresh(mailbox->index_fd, 0, &mailbox->index_base,
&mailbox->index_len, MAP_UNKNOWN_LEN, "index",
mailbox->name);
}
if (mailbox_doing_reconstruct) break;
if (mailbox->index_fd == -1) {
syslog(LOG_ERR, "IOERROR: opening %s: %m", fnamebuf);
return IMAP_IOERROR;
}
strcpy(fnamebuf, mailbox->path);
strcat(fnamebuf, FNAME_CACHE);
mailbox->cache_fd = open(fnamebuf, O_RDWR, 0);
if (mailbox->cache_fd != -1) {
map_refresh(mailbox->cache_fd, 0, &mailbox->cache_base,
&mailbox->cache_len, MAP_UNKNOWN_LEN, "cache",
mailbox->name);
}
if (mailbox->cache_fd == -1) {
syslog(LOG_ERR, "IOERROR: opening %s: %m", fnamebuf);
return IMAP_IOERROR;
}
/* Check generation number matches */
if (mailbox->index_len < 4 || mailbox->cache_len < 4) {
return IMAP_MAILBOX_BADFORMAT;
}
index_gen = *(bit32 *)mailbox->index_base;
cache_gen = *(bit32 *)mailbox->cache_base;
if (index_gen != cache_gen) {
close(mailbox->index_fd);
map_free(&mailbox->index_base, &mailbox->index_len);
close(mailbox->cache_fd);
map_free(&mailbox->cache_base, &mailbox->cache_len);
sleep(1);
}
} while (index_gen != cache_gen && tries++ < MAXTRIES);
if (index_gen != cache_gen) {
return IMAP_MAILBOX_BADFORMAT;
}
mailbox->generation_no = index_gen;
return mailbox_read_index_header(mailbox);
}
/*
* Close the mailbox 'mailbox', freeing all associated resources.
*/
void
mailbox_close(mailbox)
struct mailbox *mailbox;
{
int flag;
close(mailbox->header_fd);
map_free(&mailbox->header_base, &mailbox->header_len);
if (mailbox->index_fd != -1) {
close(mailbox->index_fd);
map_free(&mailbox->index_base, &mailbox->index_len);
}
if (mailbox->cache_fd != -1) {
close(mailbox->cache_fd);
map_free(&mailbox->cache_base, &mailbox->cache_len);
}
if (mailbox->quota.fd != -1) {
close(mailbox->quota.fd);
}
free(mailbox->name);
free(mailbox->path);
free(mailbox->acl);
if (mailbox->quota.root) free(mailbox->quota.root);
for (flag = 0; flag < MAX_USER_FLAGS; flag++) {
if (mailbox->flagname[flag]) free(mailbox->flagname[flag]);
}
*mailbox = zeromailbox;
mailbox->quota.fd = -1;
}
/*
* Read the header of 'mailbox'
*/
int
mailbox_read_header(mailbox)
struct mailbox *mailbox;
{
int flag;
const char *name, *p, *eol;
/* Check magic number */
if (mailbox->header_len < sizeof(MAILBOX_HEADER_MAGIC)-1 ||
strncmp(mailbox->header_base, MAILBOX_HEADER_MAGIC,
sizeof(MAILBOX_HEADER_MAGIC)-1)) {
return IMAP_MAILBOX_BADFORMAT;
}
/* Read quota file pathname */
p = mailbox->header_base + sizeof(MAILBOX_HEADER_MAGIC)-1;
eol = memchr(p, '\n', mailbox->header_len - (p - mailbox->header_base));
if (!eol) {
return IMAP_MAILBOX_BADFORMAT;
}
if (mailbox->quota.root) {
if (strlen(mailbox->quota.root) != eol-p ||
strncmp(mailbox->quota.root, p, eol-p) != 0) {
assert(mailbox->quota.lock_count == 0);
if (mailbox->quota.fd != -1) {
close(mailbox->quota.fd);
}
mailbox->quota.fd = -1;
}
free(mailbox->quota.root);
}
if (p < eol) {
mailbox->quota.root = xstrndup(p, eol - p);
}
else {
mailbox->quota.root = 0;
}
/* Read names of user flags */
p = eol + 1;
eol = memchr(p, '\n', mailbox->header_len - (p - mailbox->header_base));
if (!eol) {
return IMAP_MAILBOX_BADFORMAT;
}
name = p;
flag = 0;
while (name <= eol && flag < MAX_USER_FLAGS) {
p = memchr(name, ' ', eol-name);
if (!p) p = eol;
if (mailbox->flagname[flag]) free(mailbox->flagname[flag]);
if (name != p) {
mailbox->flagname[flag++] = xstrndup(p, p-name);
}
else {
mailbox->flagname[flag++] = NULL;
}
name = p+1;
}
while (flag < MAX_USER_FLAGS) {
if (mailbox->flagname[flag]) free(mailbox->flagname[flag]);
mailbox->flagname[flag++] = NULL;
}
return 0;
}
/*
* Read the acl out of the header of 'mailbox'
*/
int
mailbox_read_header_acl(mailbox)
struct mailbox *mailbox;
{
const char *p, *eol;
/* Check magic number */
if (mailbox->header_len < sizeof(MAILBOX_HEADER_MAGIC)-1 ||
strncmp(mailbox->header_base, MAILBOX_HEADER_MAGIC,
sizeof(MAILBOX_HEADER_MAGIC)-1)) {
return IMAP_MAILBOX_BADFORMAT;
}
/* Skip quota file pathname */
p = mailbox->header_base + sizeof(MAILBOX_HEADER_MAGIC)-1;
eol = memchr(p, '\n', mailbox->header_len - (p - mailbox->header_base));
if (!eol) {
return IMAP_MAILBOX_BADFORMAT;
}
/* Skip names of user flags */
p = eol + 1;
eol = memchr(p, '\n', mailbox->header_len - (p - mailbox->header_base));
if (!eol) {
return IMAP_MAILBOX_BADFORMAT;
}
/* Read ACL */
p = eol + 1;
eol = memchr(p, '\n', mailbox->header_len - (p - mailbox->header_base));
if (!eol) {
return IMAP_MAILBOX_BADFORMAT;
}
free(mailbox->acl);
mailbox->acl = xstrndup(p, eol-p);
return 0;
}
/*
* Read the the ACL for 'mailbox'.
*/
int
mailbox_read_acl(mailbox, auth_state)
struct mailbox *mailbox;
struct auth_state *auth_state;
{
int r;
char *acl;
r = mboxlist_lookup(mailbox->name, (char **)0, &acl);
if (r) return r;
free(mailbox->acl);
mailbox->acl = xstrdup(acl);
mailbox->myrights = acl_myrights(auth_state, mailbox->acl);
return 0;
}
/*
* Read the header of the index file for mailbox
*/
int
mailbox_read_index_header(mailbox)
struct mailbox *mailbox;
{
struct stat sbuf;
char buf[INDEX_HEADER_SIZE];
int n;
if (mailbox->index_fd == -1) return IMAP_MAILBOX_BADFORMAT;
fstat(mailbox->index_fd, &sbuf);
mailbox->index_ino = sbuf.st_ino;
mailbox->index_mtime = sbuf.st_mtime;
map_refresh(mailbox->index_fd, 0, &mailbox->index_base,
&mailbox->index_len, sbuf.st_size, "index",
mailbox->name);
if (mailbox->index_len < OFFSET_POP3_LAST_LOGIN ||
(mailbox->index_len <
ntohl(*((bit32 *)(mailbox->index_base+OFFSET_START_OFFSET))))) {
return IMAP_MAILBOX_BADFORMAT;
}
if (mailbox_doing_reconstruct) {
mailbox->generation_no =
ntohl(*((bit32 *)(mailbox->index_base+OFFSET_GENERATION_NO)));
}
mailbox->format =
ntohl(*((bit32 *)(mailbox->index_base+OFFSET_FORMAT)));
mailbox->minor_version =
ntohl(*((bit32 *)(mailbox->index_base+OFFSET_MINOR_VERSION)));
mailbox->start_offset =
ntohl(*((bit32 *)(mailbox->index_base+OFFSET_START_OFFSET)));
mailbox->record_size =
ntohl(*((bit32 *)(mailbox->index_base+OFFSET_RECORD_SIZE)));
mailbox->exists =
ntohl(*((bit32 *)(mailbox->index_base+OFFSET_EXISTS)));
mailbox->last_appenddate =
ntohl(*((bit32 *)(mailbox->index_base+OFFSET_LAST_APPENDDATE)));
mailbox->last_uid =
ntohl(*((bit32 *)(mailbox->index_base+OFFSET_LAST_UID)));
mailbox->quota_mailbox_used =
ntohl(*((bit32 *)(mailbox->index_base+OFFSET_QUOTA_MAILBOX_USED)));
if (mailbox->start_offset < OFFSET_POP3_LAST_LOGIN+sizeof(bit32)) {
mailbox->pop3_last_login = 0;
}
else {
mailbox->pop3_last_login =
ntohl(*((bit32 *)(mailbox->index_base+OFFSET_POP3_LAST_LOGIN)));
}
if (mailbox->start_offset < OFFSET_UIDVALIDITY+sizeof(bit32)) {
mailbox->uidvalidity = 1;
}
else {
mailbox->uidvalidity =
ntohl(*((bit32 *)(mailbox->index_base+OFFSET_UIDVALIDITY)));
}
if (!mailbox_doing_reconstruct &&
(mailbox->minor_version < MAILBOX_MINOR_VERSION)) {
return IMAP_MAILBOX_BADFORMAT;
}
return 0;
}
/*
* Read an index record from a mailbox
*/
int
mailbox_read_index_record(mailbox, msgno, record)
struct mailbox *mailbox;
unsigned msgno;
struct index_record *record;
{
unsigned long offset;
unsigned const char *buf;
int n;
offset = mailbox->start_offset + (msgno-1) * mailbox->record_size;
if (offset + INDEX_RECORD_SIZE > mailbox->index_len) {
syslog(LOG_ERR,
"IOERROR: index record %u for %s past end of file",
msgno, mailbox->name);
return IMAP_IOERROR;
}
buf = mailbox->index_base + offset;
record->uid = htonl(*((bit32 *)(buf+OFFSET_UID)));
record->internaldate = htonl(*((bit32 *)(buf+OFFSET_INTERNALDATE)));
record->sentdate = htonl(*((bit32 *)(buf+OFFSET_SENTDATE)));
record->size = htonl(*((bit32 *)(buf+OFFSET_SIZE)));
record->header_size = htonl(*((bit32 *)(buf+OFFSET_HEADER_SIZE)));
record->content_offset = htonl(*((bit32 *)(buf+OFFSET_CONTENT_OFFSET)));
record->cache_offset = htonl(*((bit32 *)(buf+OFFSET_CACHE_OFFSET)));
record->last_updated = htonl(*((bit32 *)(buf+OFFSET_LAST_UPDATED)));
record->system_flags = htonl(*((bit32 *)(buf+OFFSET_SYSTEM_FLAGS)));
for (n = 0; n < MAX_USER_FLAGS/32; n++) {
record->user_flags[n] = htonl(*((bit32 *)(buf+OFFSET_USER_FLAGS+4*n)));
}
return 0;
}
/*
* Open and read the quota file 'quota'
*/
int
mailbox_read_quota(quota)
struct quota *quota;
{
const char *p, *eol;
char buf[4096];
const char *quota_base = 0;
unsigned long quota_len = 0;
if (!quota->root) {
quota->used = 0;
quota->limit = -1;
return 0;
}
if (quota->fd == -1) {
sprintf(buf, "%s%s%s", config_dir, FNAME_QUOTADIR,
quota->root);
quota->fd = open(buf, O_RDWR, 0);
if (quota->fd == -1) {
syslog(LOG_ERR, "IOERROR: opening quota file %s: %m", buf);
return IMAP_IOERROR;
}
}
map_refresh(quota->fd, 1, &quota_base, &quota_len,
MAP_UNKNOWN_LEN, buf, 0);
p = quota_base;
eol = memchr(p, '\n', quota_len - (p - quota_base));
if (!eol) {
map_free(&quota_base, &quota_len);
return IMAP_MAILBOX_BADFORMAT;
}
quota->used = atol(p);
p = eol + 1;
eol = memchr(p, '\n', quota_len - (p - quota_base));
if (!eol) {
map_free(&quota_base, &quota_len);
return IMAP_MAILBOX_BADFORMAT;
}
quota->limit = atoi(p);
map_free(&quota_base, &quota_len);
return 0;
}
/*
* Lock the header for 'mailbox'. Reread header if necessary.
*/
int
mailbox_lock_header(mailbox)
struct mailbox *mailbox;
{
char fnamebuf[MAX_MAILBOX_PATH];
struct stat sbuf;
const char *lockfailaction;
int r;
if (mailbox->header_lock_count++) return 0;
assert(mailbox->index_lock_count == 0);
assert(mailbox->quota.lock_count == 0);
assert(mailbox->seen_lock_count == 0);
strcpy(fnamebuf, mailbox->path);
strcat(fnamebuf, FNAME_HEADER);
r = lock_reopen(mailbox->header_fd, fnamebuf, &sbuf, &lockfailaction);
if (r) {
mailbox->header_lock_count--;
syslog(LOG_ERR, "IOERROR: %s header for %s: %m",
lockfailaction, mailbox->name);
return IMAP_IOERROR;
}
if (sbuf.st_ino != mailbox->header_ino) {
map_free(&mailbox->header_base, &mailbox->header_len);
map_refresh(mailbox->header_fd, 1, &mailbox->header_base,
&mailbox->header_len, sbuf.st_size, "header",
mailbox->name);
mailbox->header_ino = sbuf.st_ino;
r = mailbox_read_header(mailbox);
if (r && !mailbox_doing_reconstruct) {
mailbox_unlock_header(mailbox);
return r;
}
}
return 0;
}
/*
* Lock the index file for 'mailbox'. Reread index file header if necessary.
*/
int
mailbox_lock_index(mailbox)
struct mailbox *mailbox;
{
char fnamebuf[MAX_MAILBOX_PATH];
struct stat sbuffd, sbuffile;
int r;
if (mailbox->index_lock_count++) return 0;
assert(mailbox->quota.lock_count == 0);
assert(mailbox->seen_lock_count == 0);
strcpy(fnamebuf, mailbox->path);
strcat(fnamebuf, FNAME_INDEX);
for (;;) {
r = lock_blocking(mailbox->index_fd);
if (r == -1) {
mailbox->index_lock_count--;
syslog(LOG_ERR, "IOERROR: locking index for %s: %m",
mailbox->name);
return IMAP_IOERROR;
}
fstat(mailbox->index_fd, &sbuffd);
r = stat(fnamebuf, &sbuffile);
if (r == -1) {
syslog(LOG_ERR, "IOERROR: stating index for %s: %m",
mailbox->name);
mailbox_unlock_index(mailbox);
return IMAP_IOERROR;
}
if (sbuffd.st_ino == sbuffile.st_ino) break;
if (r = mailbox_open_index(mailbox)) {
return r;
}
}
r = mailbox_read_index_header(mailbox);
if (r && !mailbox_doing_reconstruct) {
mailbox_unlock_index(mailbox);
return r;
}
return 0;
}
/*
* Place a POP lock on 'mailbox'.
*/
int
mailbox_lock_pop(mailbox)
struct mailbox *mailbox;
{
int r = -1;
if (mailbox->pop_lock_count++) return 0;
r = lock_nonblocking(mailbox->cache_fd);
if (r == -1) {
mailbox->pop_lock_count--;
if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EACCES) {
return IMAP_MAILBOX_POPLOCKED;
}
syslog(LOG_ERR, "IOERROR: locking cache for %s: %m", mailbox->name);
return IMAP_IOERROR;
}
return 0;
}
/*
* Lock the quota file 'quota'. Reread quota file if necessary.
*/
int
mailbox_lock_quota(quota)
struct quota *quota;
{
char quota_path[MAX_MAILBOX_PATH];
struct stat sbuf;
const char *lockfailaction;
int r;
/* assert(mailbox->header_lock_count != 0); */
if (quota->lock_count++) return 0;
/* assert(mailbox->seen_lock_count == 0); */
if (!quota->root) {
quota->used = 0;
quota->limit = -1;
return 0;
}
sprintf(quota_path, "%s%s%s", config_dir, FNAME_QUOTADIR, quota->root);
if (quota->fd == -1) {
quota->fd = open(quota_path, O_RDWR, 0);
if (quota->fd == -1) {
syslog(LOG_ERR, "IOERROR: opening quota file %s: %m",
quota_path);
return IMAP_IOERROR;
}
}
r = lock_reopen(quota->fd, quota_path, &sbuf, &lockfailaction);
if (r == -1) {
quota->lock_count--;
syslog(LOG_ERR, "IOERROR: %s quota %s: %m", lockfailaction,
quota->root);
return IMAP_IOERROR;
}
return mailbox_read_quota(quota);
}
/*
* Release lock on the header for 'mailbox'
*/
void
mailbox_unlock_header(mailbox)
struct mailbox *mailbox;
{
assert(mailbox->header_lock_count != 0);
if (--mailbox->header_lock_count == 0) {
lock_unlock(mailbox->header_fd);
}
}
/*
* Release lock on the index file for 'mailbox'
*/
void
mailbox_unlock_index(mailbox)
struct mailbox *mailbox;
{
assert(mailbox->index_lock_count != 0);
if (--mailbox->index_lock_count == 0) {
lock_unlock(mailbox->index_fd);
}
}
/*
* Release POP lock for 'mailbox'
*/
void
mailbox_unlock_pop(mailbox)
struct mailbox *mailbox;
{
assert(mailbox->pop_lock_count != 0);
if (--mailbox->pop_lock_count == 0) {
lock_unlock(mailbox->cache_fd);
}
}
/*
* Release lock on the quota file 'quota'
*/
void
mailbox_unlock_quota(quota)
struct quota *quota;
{
assert(quota->lock_count != 0);
if (--quota->lock_count == 0 && quota->root) {
lock_unlock(quota->fd);
}
}
/*
* Write the header file for 'mailbox'
*/
int
mailbox_write_header(mailbox)
struct mailbox *mailbox;
{
int flag;
FILE *newheader;
int newheader_fd;
char fnamebuf[MAX_MAILBOX_PATH];
char newfnamebuf[MAX_MAILBOX_PATH];
struct stat sbuf;
assert(mailbox->header_lock_count != 0);
strcpy(fnamebuf, mailbox->path);
strcat(fnamebuf, FNAME_HEADER);
strcpy(newfnamebuf, fnamebuf);
strcat(newfnamebuf, ".NEW");
newheader = fopen(newfnamebuf, "w+");
if (!newheader) {
syslog(LOG_ERR, "IOERROR: writing %s: %m", newfnamebuf);
return IMAP_IOERROR;
}
fputs(MAILBOX_HEADER_MAGIC, newheader);
fprintf(newheader, "%s\n", mailbox->quota.root ? mailbox->quota.root : "");
for (flag = 0; flag < MAX_USER_FLAGS; flag++) {
if (mailbox->flagname[flag]) {
fprintf(newheader, "%s ", mailbox->flagname[flag]);
}
}
fprintf(newheader, "\n");
fprintf(newheader, "%s\n", mailbox->acl);
fflush(newheader);
newheader_fd = dup(fileno(newheader));
if (ferror(newheader) || fsync(fileno(newheader)) ||
lock_blocking(newheader_fd) == -1 ||
rename(newfnamebuf, fnamebuf) == -1) {
syslog(LOG_ERR, "IOERROR: writing %s: %m", newfnamebuf);
fclose(newheader);
close(newheader_fd);
unlink(newfnamebuf);
return IMAP_IOERROR;
}
fclose(newheader);
if (mailbox->header_fd != -1) {
close(mailbox->header_fd);
map_free(&mailbox->header_base, &mailbox->header_len);
}
mailbox->header_fd = newheader_fd;
if (fstat(mailbox->header_fd, &sbuf) == -1) {
syslog(LOG_ERR, "IOERROR: fstating %s: %m", fnamebuf);
fatal("can't fstat header file", EX_OSFILE);
}
map_refresh(mailbox->header_fd, 1, &mailbox->header_base,
&mailbox->header_len, sbuf.st_size, "header", mailbox->name);
mailbox->header_ino = sbuf.st_ino;
return 0;
}
/*
* Write the index header for 'mailbox'
*/
int
mailbox_write_index_header(mailbox)
struct mailbox *mailbox;
{
char buf[INDEX_HEADER_SIZE];
int header_size = INDEX_HEADER_SIZE;
int n;
assert(mailbox->index_lock_count != 0);
*((bit32 *)(buf+OFFSET_GENERATION_NO)) = mailbox->generation_no;
*((bit32 *)(buf+OFFSET_FORMAT)) = htonl(mailbox->format);
*((bit32 *)(buf+OFFSET_MINOR_VERSION)) = htonl(mailbox->minor_version);
*((bit32 *)(buf+OFFSET_START_OFFSET)) = htonl(mailbox->start_offset);
*((bit32 *)(buf+OFFSET_RECORD_SIZE)) = htonl(mailbox->record_size);
*((bit32 *)(buf+OFFSET_EXISTS)) = htonl(mailbox->exists);
*((bit32 *)(buf+OFFSET_LAST_APPENDDATE)) = htonl(mailbox->last_appenddate);
*((bit32 *)(buf+OFFSET_LAST_UID)) = htonl(mailbox->last_uid);
*((bit32 *)(buf+OFFSET_QUOTA_MAILBOX_USED)) = htonl(mailbox->quota_mailbox_used);
*((bit32 *)(buf+OFFSET_POP3_LAST_LOGIN)) = htonl(mailbox->pop3_last_login);
*((bit32 *)(buf+OFFSET_UIDVALIDITY)) = htonl(mailbox->uidvalidity);
if (mailbox->start_offset < header_size) header_size = mailbox->start_offset;
lseek(mailbox->index_fd, 0, SEEK_SET);
n = retry_write(mailbox->index_fd, buf, header_size);
if (n != header_size || fsync(mailbox->index_fd)) {
syslog(LOG_ERR, "IOERROR: writing index header for %s: %m",
mailbox->name);
return IMAP_IOERROR;
}
return 0;
}
/*
* Write an index record to a mailbox
*/
int
mailbox_write_index_record(mailbox, msgno, record)
struct mailbox *mailbox;
unsigned msgno;
struct index_record *record;
{
int n;
char buf[INDEX_RECORD_SIZE];
*((bit32 *)(buf+OFFSET_UID)) = htonl(record->uid);
*((bit32 *)(buf+OFFSET_INTERNALDATE)) = htonl(record->internaldate);
*((bit32 *)(buf+OFFSET_SENTDATE)) = htonl(record->sentdate);
*((bit32 *)(buf+OFFSET_SIZE)) = htonl(record->size);
*((bit32 *)(buf+OFFSET_HEADER_SIZE)) = htonl(record->header_size);
*((bit32 *)(buf+OFFSET_CONTENT_OFFSET)) = htonl(record->content_offset);
*((bit32 *)(buf+OFFSET_CACHE_OFFSET)) = htonl(record->cache_offset);
*((bit32 *)(buf+OFFSET_LAST_UPDATED)) = htonl(record->last_updated);
*((bit32 *)(buf+OFFSET_SYSTEM_FLAGS)) = htonl(record->system_flags);
for (n = 0; n < MAX_USER_FLAGS/32; n++) {
*((bit32 *)(buf+OFFSET_USER_FLAGS+4*n)) = htonl(record->user_flags[n]);
}
n = lseek(mailbox->index_fd,
mailbox->start_offset + (msgno-1) * mailbox->record_size,
SEEK_SET);
if (n == -1) {
syslog(LOG_ERR, "IOERROR: seeking index record %u for %s: %m",
msgno, mailbox->name);
return IMAP_IOERROR;
}
n = retry_write(mailbox->index_fd, buf, INDEX_RECORD_SIZE);
if (n != INDEX_RECORD_SIZE || fsync(mailbox->index_fd)) {
syslog(LOG_ERR, "IOERROR: writing index record %u for %s: %m",
msgno, mailbox->name);
return IMAP_IOERROR;
}
return 0;
}
/*
* Append a new record to the index file
*/
int
mailbox_append_index(mailbox, record, start, num)
struct mailbox *mailbox;
struct index_record *record;
unsigned start;
unsigned num;
{
int i, j, len, n;
char *buf, *p;
long last_offset;
assert(mailbox->index_lock_count != 0);
if (mailbox->record_size < INDEX_RECORD_SIZE) {
return IMAP_MAILBOX_BADFORMAT;
}
len = num * mailbox->record_size;
buf = xmalloc(len);
memset(buf, 0, len);
for (i = 0; i < num; i++) {
p = buf + i*mailbox->record_size;
*((bit32 *)(p+OFFSET_UID)) = htonl(record[i].uid);
*((bit32 *)(p+OFFSET_INTERNALDATE)) = htonl(record[i].internaldate);
*((bit32 *)(p+OFFSET_SENTDATE)) = htonl(record[i].sentdate);
*((bit32 *)(p+OFFSET_SIZE)) = htonl(record[i].size);
*((bit32 *)(p+OFFSET_HEADER_SIZE)) = htonl(record[i].header_size);
*((bit32 *)(p+OFFSET_CONTENT_OFFSET)) = htonl(record[i].content_offset);
*((bit32 *)(p+OFFSET_CACHE_OFFSET)) = htonl(record[i].cache_offset);
*((bit32 *)(p+OFFSET_LAST_UPDATED)) = htonl(record[i].last_updated);
*((bit32 *)(p+OFFSET_SYSTEM_FLAGS)) = htonl(record[i].system_flags);
p += OFFSET_USER_FLAGS;
for (j = 0; j < MAX_USER_FLAGS/32; j++, p += 4) {
*((bit32 *)p) = htonl(record[i].user_flags[j]);
}
}
last_offset = mailbox->start_offset + start * mailbox->record_size;
lseek(mailbox->index_fd, last_offset, SEEK_SET);
n = retry_write(mailbox->index_fd, buf, len);
free(buf);
if (n != len || fsync(mailbox->index_fd)) {
syslog(LOG_ERR, "IOERROR: appending index records for %s: %m",
mailbox->name);
ftruncate(mailbox->index_fd, last_offset);
return IMAP_IOERROR;
}
return 0;
}
/*
* Write out the quota 'quota'
*/
int
mailbox_write_quota(quota)
struct quota *quota;
{
int r;
char quota_path[MAX_MAILBOX_PATH];
char new_quota_path[MAX_MAILBOX_PATH];
FILE *newfile;
int newfd;
assert(quota->lock_count != 0);
if (!quota->root) return 0;
sprintf(quota_path, "%s%s%s", config_dir, FNAME_QUOTADIR,
quota->root);
strcpy(new_quota_path, quota_path);
strcat(new_quota_path, ".NEW");
newfile = fopen(new_quota_path, "w+");
if (!newfile) {
syslog(LOG_ERR, "IOERROR: creating quota file %s: %m", new_quota_path);
return IMAP_IOERROR;
}
newfd = dup(fileno(newfile));
r = lock_blocking(newfd);
if (r) {
syslog(LOG_ERR, "IOERROR: locking quota file %s: %m",
new_quota_path);
fclose(newfile);
close(newfd);
return IMAP_IOERROR;
}
fprintf(newfile, "%lu\n%d\n", quota->used, quota->limit);
fflush(newfile);
if (ferror(newfile) || fsync(fileno(newfile))) {
syslog(LOG_ERR, "IOERROR: writing quota file %s: %m",
new_quota_path);
fclose(newfile);
close(newfd);
return IMAP_IOERROR;
}
if (rename(new_quota_path, quota_path)) {
syslog(LOG_ERR, "IOERROR: renaming quota file %s: %m",
quota_path);
fclose(newfile);
close(newfd);
return IMAP_IOERROR;
}
fclose(newfile);
if (quota->fd != -1) {
close(quota->fd);
quota->fd = -1;
}
quota->fd = newfd;
return 0;
}
/*
* Remove the quota root 'quota'
*/
int
mailbox_delete_quota(quota)
struct quota *quota;
{
int r;
char quota_path[MAX_MAILBOX_PATH];
FILE *newfile;
assert(quota->lock_count != 0);
if (!quota->root) return 0;
sprintf(quota_path, "%s%s%s", config_dir, FNAME_QUOTADIR,
quota->root);
unlink(quota_path);
if (quota->fd != -1) {
close(quota->fd);
quota->fd = -1;
}
free(quota->root);
quota->root = 0;
return 0;
}
/*
* Perform an expunge operation on 'mailbox'. If 'iscurrentdir' is nonzero,
* the current directory is set to the mailbox directory. If nonzero, the
* function pointed to by 'decideproc' is called (with 'deciderock') to
* determine which messages to expunge. If 'decideproc' is a null pointer,
* then messages with the \Deleted flag are expunged.
*/
int
mailbox_expunge(mailbox, iscurrentdir, decideproc, deciderock)
struct mailbox *mailbox;
int iscurrentdir;
mailbox_decideproc_t *decideproc;
void *deciderock;
{
int r, n;
char fnamebuf[MAX_MAILBOX_PATH], fnamebufnew[MAX_MAILBOX_PATH];
FILE *newindex, *newcache;
unsigned long *deleted;
unsigned numdeleted = 0, quotadeleted = 0;
unsigned newexists;
char *buf;
unsigned msgno;
int lastmsgdeleted = 1;
unsigned long cachediff = 0;
unsigned long cachestart = sizeof(bit32);
unsigned long cache_len;
unsigned long cache_offset;
struct stat sbuf;
long left;
char *fnametail;
/* Lock files and open new index/cache files */
r = mailbox_lock_header(mailbox);
if (r) return r;
r = mailbox_lock_index(mailbox);
if (r) {
mailbox_unlock_header(mailbox);
return r;
}
r = mailbox_lock_pop(mailbox);
if (r) {
mailbox_unlock_index(mailbox);
mailbox_unlock_header(mailbox);
return r;
}
if (fstat(mailbox->cache_fd, &sbuf) == -1) {
syslog(LOG_ERR, "IOERROR: fstating %s: %m", fnamebuf);
fatal("can't fstat cache file", EX_OSFILE);
}
cache_len = sbuf.st_size;
map_refresh(mailbox->cache_fd, 0, &mailbox->cache_base,
&mailbox->cache_len, cache_len, "cache", mailbox->name);
strcpy(fnamebuf, mailbox->path);
strcat(fnamebuf, FNAME_INDEX);
strcat(fnamebuf, ".NEW");
newindex = fopen(fnamebuf, "w+");
if (!newindex) {
syslog(LOG_ERR, "IOERROR: creating %s: %m", fnamebuf);
mailbox_unlock_pop(mailbox);
mailbox_unlock_index(mailbox);
mailbox_unlock_header(mailbox);
return IMAP_IOERROR;
}
strcpy(fnamebuf, mailbox->path);
strcat(fnamebuf, FNAME_CACHE);
strcat(fnamebuf, ".NEW");
newcache = fopen(fnamebuf, "w+");
if (!newcache) {
syslog(LOG_ERR, "IOERROR: creating %s: %m", fnamebuf);
fclose(newindex);
mailbox_unlock_pop(mailbox);
mailbox_unlock_index(mailbox);
mailbox_unlock_header(mailbox);
return IMAP_IOERROR;
}
/* Allocate temporary buffers */
deleted = (unsigned long *)xmalloc(mailbox->exists*sizeof(unsigned long));
buf = xmalloc(mailbox->start_offset > mailbox->record_size ?
mailbox->start_offset : mailbox->record_size);
/* Copy over headers */
memcpy(buf, mailbox->index_base, mailbox->start_offset);
(*(bit32 *)buf)++; /* Increment generation number */
fwrite(buf, 1, mailbox->start_offset, newindex);
/* Grow the index header if necessary */
for (n = mailbox->start_offset; n < INDEX_HEADER_SIZE; n++) {
if (n == OFFSET_UIDVALIDITY+3) {
putc(1, newindex);
}
else {
putc(0, newindex);
}
}
fwrite(buf, 1, sizeof(bit32), newcache);
/* Copy over records for nondeleted messages */
for (msgno = 1; msgno <= mailbox->exists; msgno++) {
memcpy(buf,
mailbox->index_base + mailbox->start_offset +
(msgno - 1)*mailbox->record_size, mailbox->record_size);
/* Sanity check */
if (*((bit32 *)(buf+OFFSET_UID)) == 0) {
syslog(LOG_ERR, "IOERROR: %s zero index record %u/%u",
mailbox->name, msgno, mailbox->exists);
goto fail;
}
if (decideproc ? decideproc(deciderock, buf) :
(ntohl(*((bit32 *)(buf+OFFSET_SYSTEM_FLAGS))) & FLAG_DELETED)) {
/* Remember UID and size */
deleted[numdeleted++] = ntohl(*((bit32 *)(buf+OFFSET_UID)));
quotadeleted += ntohl(*((bit32 *)(buf+OFFSET_SIZE)));
/* Copy over cache file data */
if (!lastmsgdeleted) {
cache_offset = ntohl(*((bit32 *)(buf+OFFSET_CACHE_OFFSET)));
fwrite(mailbox->cache_base + cachestart,
1, cache_offset - cachestart, newcache);
cachestart = cache_offset;
lastmsgdeleted = 1;
}
}
else {
cache_offset = ntohl(*((bit32 *)(buf+OFFSET_CACHE_OFFSET)));
/* Set up for copying cache file data */
if (lastmsgdeleted) {
cachediff += cache_offset - cachestart;
cachestart = cache_offset;
lastmsgdeleted = 0;
}
/* Fix up cache file offset */
*((bit32 *)(buf+OFFSET_CACHE_OFFSET)) = htonl(cache_offset - cachediff);
fwrite(buf, 1, mailbox->record_size, newindex);
}
}
/* Copy over any remaining cache file data */
if (!lastmsgdeleted) {
fwrite(mailbox->cache_base + cachestart, 1,
cache_len - cachestart, newcache);
}
/* Fix up information in index header */
rewind(newindex);
n = fread(buf, 1, mailbox->start_offset, newindex);
if (n != mailbox->start_offset) {
syslog(LOG_ERR, "IOERROR: reading index header for %s: got %d of %d",
mailbox->name, n, mailbox->start_offset);
goto fail;
}
/* Fix up exists */
newexists = ntohl(*((bit32 *)(buf+OFFSET_EXISTS)))-numdeleted;
*((bit32 *)(buf+OFFSET_EXISTS)) = htonl(newexists);
/* Fix up quota_mailbox_used */
*((bit32 *)(buf+OFFSET_QUOTA_MAILBOX_USED)) =
htonl(ntohl(*((bit32 *)(buf+OFFSET_QUOTA_MAILBOX_USED)))-quotadeleted);
/* Fix up start offset if necessary */
if (mailbox->start_offset < INDEX_HEADER_SIZE) {
*((bit32 *)(buf+OFFSET_START_OFFSET)) = htonl(INDEX_HEADER_SIZE);
}
rewind(newindex);
fwrite(buf, 1, mailbox->start_offset, newindex);
/* Ensure everything made it to disk */
fflush(newindex);
fflush(newcache);
if (ferror(newindex) || ferror(newcache) ||
fsync(fileno(newindex)) || fsync(fileno(newcache))) {
syslog(LOG_ERR, "IOERROR: writing index/cache for %s: %m",
mailbox->name);
goto fail;
}
/* Record quota release */
r = mailbox_lock_quota(&mailbox->quota);
if (r) goto fail;
if (mailbox->quota.used >= quotadeleted) {
mailbox->quota.used -= quotadeleted;
}
else {
mailbox->quota.used = 0;
}
r = mailbox_write_quota(&mailbox->quota);
if (r) {
syslog(LOG_ERR,
"LOSTQUOTA: unable to record free of %u bytes in quota %s",
quotadeleted, mailbox->quota.root);
}
mailbox_unlock_quota(&mailbox->quota);
strcpy(fnamebuf, mailbox->path);
fnametail = fnamebuf + strlen(fnamebuf);
strcpy(fnametail, FNAME_INDEX);
strcpy(fnamebufnew, fnamebuf);
strcat(fnamebufnew, ".NEW");
if (rename(fnamebufnew, fnamebuf)) {
syslog(LOG_ERR, "IOERROR: renaming index file for %s: %m",
mailbox->name);
goto fail;
}
strcpy(fnametail, FNAME_CACHE);
strcpy(fnamebufnew, fnamebuf);
strcat(fnamebufnew, ".NEW");
if (rename(fnamebufnew, fnamebuf)) {
syslog(LOG_CRIT,
"CRITICAL IOERROR: renaming cache file for %s, need to reconstruct: %m",
mailbox->name);
/* Fall through and delete message files anyway */
}
if (numdeleted) {
toimsp(mailbox->name, mailbox->uidvalidity,
"UIDNnn", mailbox->last_uid, newexists, 0);
}
mailbox_unlock_pop(mailbox);
mailbox_unlock_index(mailbox);
mailbox_unlock_header(mailbox);
fclose(newindex);
fclose(newcache);
/* Delete message files */
if (mailbox->format != MAILBOX_FORMAT_NETNEWS) {
*fnametail++ = '/';
for (msgno = 0; msgno < numdeleted; msgno++) {
if (iscurrentdir) {
unlink(mailbox_message_fname(mailbox, deleted[msgno]));
}
else {
strcpy(fnametail, mailbox_message_fname(mailbox, deleted[msgno]));
unlink(fnamebuf);
}
}
}
free(buf);
free(deleted);
return 0;
fail:
free(buf);
free(deleted);
fclose(newindex);
fclose(newcache);
mailbox_unlock_pop(mailbox);
mailbox_unlock_index(mailbox);
mailbox_unlock_header(mailbox);
return IMAP_IOERROR;
}
struct newsexpunge {
int ctr;
char *msg_seen;
};
/*
* Expunge decision proc which removes news articles not in msg_seen array
*/
static int
expunge_expired(rock, indexbuf)
void *rock;
char *indexbuf;
{
struct newsexpunge *newsexpunge = (struct newsexpunge *)rock;
return !newsexpunge->msg_seen[newsexpunge->ctr++];
}
/*
* Expunge the expired news articles out of the netnews mailbox 'mailbox'
*/
int
mailbox_expungenews(mailbox)
struct mailbox *mailbox;
{
int r;
struct newsexpunge newsexpunge;
DIR *dirp;
struct dirent *dirent;
char newspath[4096], *end_newspath;
const char *group;
int newsprefixlen;
unsigned long uid, miduid;
char *p;
int i;
int low, high, mid;
assert(mailbox->format == MAILBOX_FORMAT_NETNEWS);
assert(mailbox->index_lock_count == 0);
/* Lock files and open new index/cache files */
r = mailbox_lock_header(mailbox);
if (r) return r;
r = mailbox_lock_index(mailbox);
if (r) {
mailbox_unlock_header(mailbox);
return r;
}
newsexpunge.msg_seen = xmalloc(mailbox->exists);
memset(newsexpunge.msg_seen, 0, mailbox->exists);
if (config_newsspool) {
strcpy(newspath, config_newsspool);
end_newspath = newspath + strlen(newspath);
if (end_newspath == newspath || end_newspath[-1] != '/') {
*end_newspath++ = '/';
}
group = mailbox->name;
if (newsprefixlen = strlen(config_getstring("newsprefix", ""))) {
group += newsprefixlen;
if (*group == '.') group++;
}
strcpy(end_newspath, group);
while (*end_newspath) {
if (*end_newspath == '.') *end_newspath = '/';
end_newspath++;
}
dirp = opendir(newspath);
}
else {
dirp = opendir(mailbox->path);
}
if (!dirp) {
free(newsexpunge.msg_seen);
mailbox_unlock_index(mailbox);
mailbox_unlock_header(mailbox);
return IMAP_IOERROR;
}
/* For each article in directory, mark it in msg_seen */
while (dirent = readdir(dirp)) {
if (!isdigit(dirent->d_name[0]) || dirent->d_name[0] == '0') continue;
uid = 0;
p = dirent->d_name;
while (isdigit(*p)) {
uid = uid * 10 + *p++ - '0';
}
if (*p) continue;
/* Search for uid in index file */
low = 0;
high = mailbox->exists-1;
while (low <= high) {
mid = (high - low)/2 + low;
miduid = ntohl(*((bit32 *)(mailbox->index_base +
mailbox->start_offset +
(mid * mailbox->record_size) +
OFFSET_UID)));
if (uid == miduid) break;
if (uid < miduid) {
high = mid - 1;
}
else {
low = mid + 1;
}
}
if (low <= high) {
newsexpunge.msg_seen[mid] = 1;
}
}
closedir(dirp);
/* see if there's anything to expunge */
for (i = 0; i < mailbox->exists; i++) {
if (!newsexpunge.msg_seen[i]) break;
}
if (i == mailbox->exists) {
free(newsexpunge.msg_seen);
mailbox_unlock_index(mailbox);
mailbox_unlock_header(mailbox);
return 0;
}
newsexpunge.ctr = 0;
r = mailbox_expunge(mailbox, 0, expunge_expired, &newsexpunge);
free(newsexpunge.msg_seen);
mailbox_unlock_index(mailbox);
mailbox_unlock_header(mailbox);
(void) mailbox_open_index(mailbox);
return r;
}
char *
mailbox_findquota(name)
const char *name;
{
static char quota_path[MAX_MAILBOX_PATH];
char *start, *tail;
struct stat sbuf;
strcpy(quota_path, config_dir);
strcat(quota_path, FNAME_QUOTADIR);
start = quota_path + strlen(quota_path);
strcpy(start, name);
lcase(start);
while (stat(quota_path, &sbuf) == -1) {
tail = strrchr(start, '.');
if (!tail) return 0;
*tail = '\0';
}
return start;
}
int
mailbox_create(name, path, acl, format, mailboxp)
const char *name;
char *path;
const char *acl;
int format;
struct mailbox *mailboxp;
{
int r;
char *p=path;
char *quota_root;
char fnamebuf[MAX_MAILBOX_PATH];
static struct mailbox mailbox;
int save_errno;
int n;
struct stat sbuf;
while (p = strchr(p+1, '/')) {
*p = '\0';
if (mkdir(path, 0777) == -1 && errno != EEXIST) {
save_errno = errno;
if (stat(path, &sbuf) == -1) {
errno = save_errno;
syslog(LOG_ERR, "IOERROR: creating directory %s: %m", path);
return IMAP_IOERROR;
}
}
*p = '/';
}
if (mkdir(path, 0777) == -1 && errno != EEXIST) {
save_errno = errno;
if (stat(path, &sbuf) == -1) {
errno = save_errno;
syslog(LOG_ERR, "IOERROR: creating directory %s: %m", path);
return IMAP_IOERROR;
}
}
mailbox = zeromailbox;
mailbox.quota.fd = -1;
quota_root = mailbox_findquota(name);
strcpy(fnamebuf, path);
p = fnamebuf + strlen(fnamebuf);
strcpy(p, FNAME_HEADER);
mailbox.header_fd = open(fnamebuf, O_WRONLY|O_TRUNC|O_CREAT, 0666);
if (mailbox.header_fd == -1) {
syslog(LOG_ERR, "IOERROR: creating %s: %m", fnamebuf);
return IMAP_IOERROR;
}
mailbox.name = xstrdup(name);
mailbox.path = xstrdup(path);
mailbox.acl = xstrdup(acl);
strcpy(p, FNAME_INDEX);
mailbox.index_fd = open(fnamebuf, O_WRONLY|O_TRUNC|O_CREAT, 0666);
if (mailbox.index_fd == -1) {
syslog(LOG_ERR, "IOERROR: creating %s: %m", fnamebuf);
mailbox_close(&mailbox);
return IMAP_IOERROR;
}
strcpy(p, FNAME_CACHE);
mailbox.cache_fd = open(fnamebuf, O_WRONLY|O_TRUNC|O_CREAT, 0666);
if (mailbox.cache_fd == -1) {
syslog(LOG_ERR, "IOERROR: creating %s: %m", fnamebuf);
mailbox_close(&mailbox);
return IMAP_IOERROR;
}
mailbox.header_lock_count = 1;
mailbox.index_lock_count = 1;
if (quota_root) mailbox.quota.root = xstrdup(quota_root);
mailbox.generation_no = 0;
mailbox.format = format;
mailbox.minor_version = MAILBOX_MINOR_VERSION;
mailbox.start_offset = INDEX_HEADER_SIZE;
mailbox.record_size = INDEX_RECORD_SIZE;
mailbox.exists = 0;
mailbox.last_appenddate = 0;
mailbox.last_uid = 0;
mailbox.quota_mailbox_used = 0;
mailbox.pop3_last_login = 0;
mailbox.uidvalidity = time(0);
r = mailbox_write_header(&mailbox);
if (!r) r = mailbox_write_index_header(&mailbox);
if (!r) {
n = retry_write(mailbox.cache_fd, (char *)&mailbox.generation_no, 4);
if (n != 4 || fsync(mailbox.cache_fd)) {
syslog(LOG_ERR, "IOERROR: writing initial cache for %s: %m",
mailbox.name);
r = IMAP_IOERROR;
}
}
if (!r) r = seen_create(&mailbox);
if (mailboxp) {
*mailboxp = mailbox;
}
else {
mailbox_close(&mailbox);
}
return r;
}
/*
* Delete and close the mailbox 'mailbox'. Closes 'mailbox' whether
* or not the deletion was successful.
*/
int mailbox_delete(mailbox, delete_quota_root)
struct mailbox *mailbox;
int delete_quota_root;
{
int r;
DIR *dirp;
struct dirent *f;
char buf[MAX_MAILBOX_PATH];
char *tail;
/* Lock everything in sight */
r = mailbox_lock_header(mailbox);
if (!r && mailbox->index_fd == -1) r = mailbox_open_index(mailbox);
if (!r) r = mailbox_lock_index(mailbox);
if (!r) r = mailbox_lock_quota(&mailbox->quota);
if (r) {
mailbox_close(mailbox);
return r;
}
seen_delete(mailbox);
if (delete_quota_root) {
mailbox_delete_quota(&mailbox->quota);
}
else {
/* Free any quota being used by this mailbox */
if (mailbox->quota.used >= mailbox->quota_mailbox_used) {
mailbox->quota.used -= mailbox->quota_mailbox_used;
}
else {
mailbox->quota.used = 0;
}
r = mailbox_write_quota(&mailbox->quota);
if (r) {
syslog(LOG_ERR,
"LOSTQUOTA: unable to record free of %u bytes in quota %s",
mailbox->quota_mailbox_used, mailbox->quota.root);
}
mailbox_unlock_quota(&mailbox->quota);
}
/* remove all files in directory */
strcpy(buf, mailbox->path);
tail = buf + strlen(buf);
*tail++ = '/';
dirp = opendir(mailbox->path);
if (dirp) {
while (f = readdir(dirp)) {
strcpy(tail, f->d_name);
(void) unlink(buf);
}
closedir(dirp);
}
/* Remove empty directories, going up path */
tail--;
do {
*tail = '\0';
} while (rmdir(buf) == 0 && (tail = strrchr(buf, '/')));
mailbox_close(mailbox);
return 0;
}
/*
* Expunge decision proc used by mailbox_rename() to expunge all messages
* in INBOX
*/
static int expungeall(rock, indexbuf)
void *rock;
char *indexbuf;
{
return 1;
}
int
mailbox_rename(oldname, newname, newpath, isinbox, olduidvalidityp,
newuidvalidityp)
const char *oldname;
const char *newname;
char *newpath;
int isinbox;
bit32 *olduidvalidityp;
bit32 *newuidvalidityp;
{
int r, r2;
struct mailbox oldmailbox, newmailbox;
int flag, msgno;
struct index_record record;
char oldfname[MAX_MAILBOX_PATH], newfname[MAX_MAILBOX_PATH];
char *oldfnametail, *newfnametail;
/* Open old mailbox and lock */
r = mailbox_open_header(oldname, 0, &oldmailbox);
if (r) {
return r;
}
if (oldmailbox.format == MAILBOX_FORMAT_NETNEWS) {
mailbox_close(&oldmailbox);
return IMAP_MAILBOX_NOTSUPPORTED;
}
r = mailbox_lock_header(&oldmailbox);
if (!r) r = mailbox_open_index(&oldmailbox);
if (!r) r = mailbox_lock_index(&oldmailbox);
if (r) {
mailbox_close(&oldmailbox);
return r;
}
/* Create new mailbox */
r = mailbox_create(newname, newpath, oldmailbox.acl, oldmailbox.format,
&newmailbox);
if (r) {
mailbox_close(&oldmailbox);
return r;
}
*olduidvalidityp = oldmailbox.uidvalidity;
*newuidvalidityp = newmailbox.uidvalidity;
/* Copy flag names */
for (flag = 0; flag < MAX_USER_FLAGS; flag++) {
if (oldmailbox.flagname[flag]) {
newmailbox.flagname[flag] = xstrdup(oldmailbox.flagname[flag]);
}
}
r = mailbox_write_header(&newmailbox);
if (r) {
mailbox_close(&newmailbox);
mailbox_close(&oldmailbox);
return r;
}
/* Check quota if necessary */
if (newmailbox.quota.root) {
r = mailbox_lock_quota(&newmailbox.quota);
if (!oldmailbox.quota.root ||
strcmp(oldmailbox.quota.root, newmailbox.quota.root) != 0) {
if (!r && newmailbox.quota.limit >= 0 &&
newmailbox.quota.used + oldmailbox.quota_mailbox_used >
newmailbox.quota.limit * QUOTA_UNITS) {
r = IMAP_QUOTA_EXCEEDED;
}
}
if (r) {
mailbox_close(&newmailbox);
mailbox_close(&oldmailbox);
return r;
}
}
strcpy(oldfname, oldmailbox.path);
oldfnametail = oldfname + strlen(oldfname);
strcpy(newfname, newmailbox.path);
newfnametail = newfname + strlen(newfname);
/* Copy over index/cache files */
strcpy(oldfnametail, FNAME_INDEX);
strcpy(newfnametail, FNAME_INDEX);
unlink(newfname); /* Make link() possible */
r = mailbox_copyfile(oldfname, newfname);
strcpy(oldfnametail, FNAME_CACHE);
strcpy(newfnametail, FNAME_CACHE);
unlink(newfname);
if (!r) r = mailbox_copyfile(oldfname, newfname);
if (r) {
mailbox_close(&newmailbox);
mailbox_close(&oldmailbox);
return r;
}
/* Copy over message files */
oldfnametail++;
newfnametail++;
for (msgno = 1; msgno <= oldmailbox.exists; msgno++) {
r = mailbox_read_index_record(&oldmailbox, msgno, &record);
if (r) break;
strcpy(oldfnametail, mailbox_message_fname(&oldmailbox, record.uid));
strcpy(newfnametail, oldfnametail);
r = mailbox_copyfile(oldfname, newfname);
if (r) break;
}
if (!r) r = seen_copy(&oldmailbox, &newmailbox);
/* Record new quota usage */
if (!r && newmailbox.quota.root) {
newmailbox.quota_mailbox_used = oldmailbox.quota_mailbox_used;
newmailbox.quota.used += oldmailbox.quota_mailbox_used;
r = mailbox_write_quota(&newmailbox.quota);
mailbox_unlock_quota(&newmailbox.quota);
}
if (r) goto fail;
if (isinbox) {
/* Expunge old mailbox */
r = mailbox_expunge(&oldmailbox, 0, expungeall, (char *)0);
mailbox_close(&oldmailbox);
}
else {
r = mailbox_delete(&oldmailbox, 0);
}
if (r && newmailbox.quota.root) {
r2 = mailbox_lock_quota(&newmailbox.quota);
newmailbox.quota.used += newmailbox.quota_mailbox_used;
if (!r2) {
r2 = mailbox_write_quota(&newmailbox.quota);
mailbox_unlock_quota(&newmailbox.quota);
}
if (r2) {
syslog(LOG_ERR,
"LOSTQUOTA: unable to record use of %u bytes in quota %s",
newmailbox.quota_mailbox_used, newmailbox.quota.root);
}
}
if (r) goto fail;
mailbox_close(&newmailbox);
return 0;
fail:
for (msgno = 1; msgno <= oldmailbox.exists; msgno++) {
if (mailbox_read_index_record(&oldmailbox, msgno, &record)) continue;
strcpy(newfnametail, mailbox_message_fname(&oldmailbox, record.uid));
(void) unlink(newfname);
}
mailbox_close(&newmailbox);
mailbox_close(&oldmailbox);
return r;
}
/*
* Copy (or link) the file 'from' to the file 'to'
*/
int mailbox_copyfile(from, to)
const char *from;
const char *to;
{
int srcfd, destfd;
struct stat sbuf;
const char *src_base = 0;
unsigned long src_size = 0;
int n;
if (link(from, to) == 0) return 0;
destfd = open(to, O_RDWR|O_TRUNC|O_CREAT, 0666);
if (destfd == -1) {
syslog(LOG_ERR, "IOERROR: creating %s: %m", to);
return IMAP_IOERROR;
}
srcfd = open(from, O_RDONLY, 0666);
if (srcfd == -1) {
syslog(LOG_ERR, "IOERROR: opening %s: %m", from);
close(destfd);
return IMAP_IOERROR;
}
if (fstat(srcfd, &sbuf) == -1) {
syslog(LOG_ERR, "IOERROR: fstat on %s: %m", from);
close(srcfd);
close(destfd);
return IMAP_IOERROR;
}
map_refresh(srcfd, 1, &src_base, &src_size, sbuf.st_size, from, 0);
n = retry_write(destfd, src_base, src_size);
if (n == -1 || fsync(destfd)) {
map_free(&src_base, &src_size);
close(srcfd);
close(destfd);
syslog(LOG_ERR, "IOERROR: writing %s: %m", to);
return IMAP_IOERROR;
}
map_free(&src_base, &src_size);
close(srcfd);
close(destfd);
return 0;
}

File Metadata

Mime Type
text/x-c
Expires
Sat, Apr 4, 1:48 AM (1 w, 2 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18749361
Default Alt Text
mailbox.c (50 KB)

Event Timeline