#include <config.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <ctype.h>
#include <syslog.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <stdlib.h>
#ifdef APPLE_OS_X_SERVER
#include <pwd.h>
#include <stdint.h>
#endif
#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 "acl.h"
#include "assert.h"
#include "bsearch.h"
#include "imparse.h"
#include "global.h"
#include "exitcodes.h"
#include "imap_err.h"
#include "mailbox.h"
#include "map.h"
#include "message.h"
#include "xmalloc.h"
#include "xstrlcpy.h"
#include "xstrlcat.h"
#include "global.h"
#include "mboxname.h"
#include "mboxlist.h"
#include "quota.h"
#include "seen.h"
#include "retry.h"
#include "convert_code.h"
#include "util.h"
#include "sync_log.h"
#ifdef APPLE_OS_X_SERVER
#include "AppleOD.h"
#endif
extern int optind;
extern char *optarg;
struct discovered {
char *name;
struct discovered *next;
};
struct uniqmailid {
char * uniqmbxid;
char *uniqname;
struct uniqmailid *uniqnext;
};
struct uniqmailid *uniqmid_head;
static struct namespace recon_namespace;
const int config_need_data = CONFIG_NEED_PARTITION_DATA;
void do_mboxlist(void);
int do_reconstruct(char *name, int matchlen, int maycreate, void *rock);
int reconstruct(char *name, struct discovered *l);
void usage(void);
char * getmailname (char * mailboxname);
struct uniqmailid * add_uniqid (char * mailboxname, char * mailboxid);
struct uniqmailid * find_uniqid (char * mailboxname, char * mailboxid);
#ifdef APPLE_OS_X_SERVER
void import_mailboxes ( const char *inStartPart );
void add_all_mailboxes ( const char *inBasePath, const char *inPath );
int set_seen_flag ( struct mailbox *inMailbox, const char *inUser, const char *inUID );
#endif
extern cyrus_acl_canonproc_t mboxlist_ensureOwnerRights;
int code = 0;
int keepflag = 0;
int syncflag = 0;
int main(int argc, char **argv)
{
int opt, i, r;
int rflag = 0;
int mflag = 0;
int fflag = 0;
#ifdef APPLE_OS_X_SERVER
int iflag = 0;
struct passwd *pwd;
int uid, gid;
#endif
int xflag = 0;
char buf[MAX_MAILBOX_PATH+1];
char mbbuf[MAX_MAILBOX_PATH+1];
struct discovered head;
char *alt_config = NULL;
char *start_part = NULL;
const char *start_part_path = NULL, *start_part_mpath = NULL, *path;
memset(&head, 0, sizeof(head));
#ifdef APPLE_OS_X_SERVER
if ( geteuid() == 0 )
{
pwd = getpwnam( CYRUS_USER );
if ( pwd == NULL )
{
fatal( "No directory entry found for cyrus user", EC_USAGE );
}
uid = pwd->pw_uid;
gid = pwd->pw_gid;
if ( initgroups( CYRUS_USER, gid) )
{
fatal( "Unable to initialize groups for cyrus user", EC_USAGE );
}
if ( setgid( gid ) )
{
fatal( "Unable to set group id to mail for cyrus user", EC_USAGE );
}
r = setuid( uid );
if (r != 0)
{
fatal( "Unable to set uid to cyrus user", EC_USAGE );
}
}
else
{
fatal("must run as root", EC_USAGE);
}
#endif
assert(INDEX_HEADER_SIZE == (OFFSET_SPARE4+4));
assert(INDEX_RECORD_SIZE == (OFFSET_MODSEQ+4));
#ifdef APPLE_OS_X_SERVER
while ((opt = getopt(argc, argv, "C:kp:rmfisx")) != EOF) {
#else
while ((opt = getopt(argc, argv, "C:kp:rmfsx")) != EOF) {
#endif
switch (opt) {
case 'C':
alt_config = optarg;
break;
case 'k':
keepflag = 1;
break;
case 'p':
start_part = optarg;
break;
case 'r':
rflag = 1;
break;
case 'm':
mflag = 1;
break;
case 'f':
fflag = 1;
break;
#ifdef APPLE_OS_X_SERVER
case 'i':
iflag = 1;
break;
#endif
case 's':
syncflag = 1;
break;
case 'x':
xflag = 1;
break;
default:
usage();
}
}
cyrus_init(alt_config, "reconstruct", 0);
global_sasl_init(1,0,NULL);
if ((r = mboxname_init_namespace(&recon_namespace, 1)) != 0) {
syslog(LOG_ERR, error_message(r));
fatal(error_message(r), EC_CONFIG);
}
if (syncflag)
sync_log_init();
if(start_part) {
start_part_path = config_partitiondir(start_part);
if (!start_part_path) {
fatal(error_message(IMAP_PARTITION_UNKNOWN), EC_USAGE);
}
start_part_mpath = config_metapartitiondir(start_part);
}
if (mflag) {
if (rflag || fflag || optind != argc) {
cyrus_done();
usage();
}
do_mboxlist();
}
#ifdef APPLE_OS_X_SERVER
if (iflag==1) {
if (rflag||mflag||fflag||xflag)
usage();
}
#endif
mboxlist_init(0);
mboxlist_open(NULL);
quotadb_init(0);
quotadb_open(NULL);
mailbox_reconstructmode();
#ifdef APPLE_OS_X_SERVER
if (iflag==1){
import_mailboxes(start_part);
start_part=NULL;
}
#endif
if (start_part) {
if(optind == argc) {
fprintf(stderr,
"When using -p, you must specify a mailbox to attempt to reconstruct.");
exit(EC_USAGE);
}
for (i = optind; i < argc; i++) {
struct stat sbuf;
if(strchr(argv[i],'%') || strchr(argv[i],'*')) {
fprintf(stderr, "Using wildcards with -p is not supported.\n");
exit(EC_USAGE);
}
(*recon_namespace.mboxname_tointernal)(&recon_namespace, argv[i],
NULL, buf);
do {
r = mboxlist_lookup(buf, NULL, NULL);
} while (r == IMAP_AGAIN);
if(r != IMAP_MAILBOX_NONEXISTENT) {
fprintf(stderr,
"Mailbox %s already exists. Cannot specify -p.\n",
argv[i]);
exit(EC_USAGE);
}
path = (start_part_mpath &&
(config_metapartition_files &
IMAP_ENUM_METAPARTITION_FILES_HEADER)) ?
start_part_mpath : start_part_path;
mailbox_hash_mbox(mbbuf, sizeof(mbbuf), path, buf);
strlcat(mbbuf, FNAME_HEADER, sizeof(mbbuf));
if(stat(mbbuf, &sbuf) < 0) {
fprintf(stderr,
"%s does not appear to be a mailbox (no %s).\n",
argv[i], mbbuf);
exit(EC_USAGE);
}
}
for (i = optind; i < argc; i++) {
(*recon_namespace.mboxname_tointernal)(&recon_namespace, argv[i],
NULL, buf);
r = mboxlist_createmailbox(buf, 0, start_part, 1,
#ifdef APPLE_OS_X_SERVER
"_cyrus", NULL, 0, 0, !xflag);
#else
"cyrus", NULL, 0, 0, !xflag);
#endif
if(r) {
fprintf(stderr, "could not create %s\n", argv[i]);
}
}
}
if (optind == argc) {
if (rflag) {
fprintf(stderr, "please specify a mailbox to recurse from\n");
cyrus_done();
exit(EC_USAGE);
}
assert(!rflag);
strlcpy(buf, "*", sizeof(buf));
(*recon_namespace.mboxlist_findall)(&recon_namespace, buf, 1, 0, 0,
do_reconstruct, NULL);
}
for (i = optind; i < argc; i++) {
char *domain = NULL;
if (config_virtdomains) domain = strchr(argv[i], '@');
strlcpy(buf, argv[i], sizeof(buf));
mboxname_hiersep_tointernal(&recon_namespace, buf,
config_virtdomains ?
strcspn(buf, "@") : 0);
(*recon_namespace.mboxlist_findall)(&recon_namespace, buf, 1, 0,
0, do_reconstruct,
fflag ? &head : NULL);
if (rflag) {
strlcat(buf, ".*", sizeof(buf));
if (domain) strlcat(buf, domain, sizeof(buf));
(*recon_namespace.mboxlist_findall)(&recon_namespace, buf, 1, 0,
0, do_reconstruct,
fflag ? &head : NULL);
}
}
while (head.next) {
struct discovered *p;
int r = 0;
p = head.next;
head.next = p->next;
r = mboxlist_createmailbox(p->name, 0, NULL, 1,
#ifdef APPLE_OS_X_SERVER
"_cyrus", NULL, 0, 0, !xflag);
#else
"cyrus", NULL, 0, 0, !xflag);
#endif
if (!r) {
do_reconstruct(p->name, strlen(p->name), 0, &head);
} else {
fprintf(stderr, "createmailbox %s: %s\n",
p->name, error_message(r));
}
free(p->name);
free(p);
}
mboxlist_close();
mboxlist_done();
quotadb_close();
quotadb_done();
cyrus_done();
return code;
}
#ifdef APPLE_OS_X_SERVER
void import_mailboxes ( const char *inStartPart )
{
const char *path = NULL;
char partition[ 512 ];
if ( !inStartPart )
{
path = config_partitiondir( config_defpartition );
}
else
{
path = config_partitiondir( inStartPart );
}
if ( path != NULL )
{
syslog( LOG_INFO, "Importing mail from: %s\n", path );
add_all_mailboxes( path, "user" );
}
}
void add_all_mailboxes ( const char *inBasePath, const char *inPath )
{
int r = 0;
int quotaroot = 0;
DIR *dirp = NULL;
struct dirent *dirent = NULL;
struct od_user_opts useropts;
char mailboxname[ 2048 + 1 ];
char fullpath[ 2048 + 1 ];
strcpy( fullpath, inBasePath );
if ( inPath != NULL )
{
strcat( fullpath, "/" );
strcat( fullpath, inPath );
}
dirp = opendir( fullpath );
if ( !dirp )
{
fprintf( stderr, "reconstruct: couldn't open partition: %s \n", fullpath );
}
else
{
if ( (r = mboxname_init_namespace( &recon_namespace, 1)) != 0 )
{
syslog( LOG_ERR, error_message( r ) );
fatal( error_message(r), EC_CONFIG );
}
while ( (dirent = readdir( dirp )) != NULL )
{
quotaroot = 0;
memset( &useropts, 0, sizeof ( struct od_user_opts ) );
if ( strchr(dirent->d_name, '.') == 0 )
{
if ( inPath != NULL )
{
strcpy( mailboxname, inPath );
strcat( mailboxname, "/" );
strcat( mailboxname, dirent->d_name );
strcpy( fullpath, inPath );
strcat( fullpath, "/" );
strcat( fullpath, dirent->d_name );
if ( strcmp( inPath, "user" ) == 0 )
{
quotaroot = 1;
odGetUserOpts( dirent->d_name, &useropts );
}
}
else
{
strcpy( mailboxname, dirent->d_name );
strcpy( fullpath, dirent->d_name );
}
mboxname_hiersep_tointernal( &recon_namespace, mailboxname, 0);
r = mboxlist_lookup( mailboxname, NULL, NULL );
if ( r == IMAP_MAILBOX_NONEXISTENT )
{
char *partition = NULL;
if ( useropts.fAltDataLocPtr != NULL )
{
partition = useropts.fAltDataLocPtr;
}
r = mboxlist_createmailbox( mailboxname, MAILBOX_FORMAT_NORMAL, partition, 1, "_cyrus", NULL, 0, 0, 0);
if ( !r )
{
syslog( LOG_INFO, "Adding mailbox = %s", mailboxname );
}
else
{
syslog( LOG_ERR, "Mailbox add error (%d)", r );
}
}
if ( quotaroot == 1 )
{
if ( useropts.fDiskQuota == 0 )
{
mboxlist_setquota( mailboxname, 0, 0 );
}
else
{
mboxlist_setquota( mailboxname, useropts.fDiskQuota * 1024, 0 );
}
}
add_all_mailboxes( inBasePath, fullpath );
}
}
closedir( dirp );
}
}
#endif
void usage(void)
{
#ifdef APPLE_OS_X_SERVER
fprintf(stderr,
"usage: reconstruct [-C <alt_config>] [-p partition] [-rfx] mailbox...\n");
fprintf(stderr, " reconstruct [-C <alt_config>] -m\n");
fprintf(stderr, " reconstruct [-C <alt_config>] -i\n" );
exit(EC_USAGE);
#else
fprintf(stderr,
"usage: reconstruct [-C <alt_config>] [-p partition] [-ksrfx] mailbox...\n");
fprintf(stderr, " reconstruct [-C <alt_config>] -m\n");
exit(EC_USAGE);
#endif
}
int compare_uid(const void *a, const void *b)
{
return *(unsigned long *)a - *(unsigned long *)b;
}
#define UIDGROW 300
int
do_reconstruct(char *name,
int matchlen,
int maycreate __attribute__((unused)),
void *rock)
{
int r;
char buf[MAX_MAILBOX_PATH+1];
static char lastname[MAX_MAILBOX_PATH+1] = "";
signals_poll();
if (matchlen == strlen(lastname) &&
!strncmp(name, lastname, matchlen)) return 0;
if(matchlen >= sizeof(lastname))
matchlen = sizeof(lastname) - 1;
strncpy(lastname, name, matchlen);
lastname[matchlen] = '\0';
r = reconstruct(lastname, rock);
if (r) {
com_err(name, r, (r == IMAP_IOERROR) ? error_message(errno) : NULL);
code = convert_code(r);
} else {
(*recon_namespace.mboxname_toexternal)(&recon_namespace, lastname,
NULL, buf);
printf("%s\n", buf);
}
return 0;
}
int
reconstruct_expunge (char * path, struct mailbox * mailbox,
unsigned long **expuid, int *expuid_num)
{
char fnamebuf[MAX_MAILBOX_PATH+1];
char newfnamebuf[MAX_MAILBOX_PATH+1];
struct stat sbuf;
char buf[((INDEX_HEADER_SIZE > INDEX_RECORD_SIZE) ?
INDEX_HEADER_SIZE : INDEX_RECORD_SIZE)];
int expunge_fd;
FILE *fexpunge;
const char *index_base = NULL;
unsigned long index_len = 0;
int format;
int minor_version;
unsigned long start_offset;
unsigned long record_size;
unsigned long exists;
unsigned long uid;
char msgfname[MAILBOX_FNAME_LEN+1];
unsigned msgno;
const char *p;
int n;
unsigned long *expuid_array = NULL;
int expuid_idx = 0;
int expuid_alloc = 0;
int expmsg;
*expuid = NULL;
*expuid_num = 0;
strlcpy(fnamebuf, path, sizeof(fnamebuf));
strlcat(fnamebuf, FNAME_EXPUNGE_INDEX, sizeof(fnamebuf));
if (stat(fnamebuf, &sbuf) == -1 ||
sbuf.st_size <= OFFSET_LEAKED_CACHE ||
(expunge_fd = open(fnamebuf, O_RDONLY, 0666)) == -1) {
unlink(fnamebuf);
return (0);
}
map_refresh(expunge_fd, 1, &index_base,
&index_len, sbuf.st_size, "expunge", mailbox->name);
format = ntohl(*((bit32 *)(index_base+OFFSET_FORMAT)));
minor_version = ntohl(*((bit32 *)(index_base+OFFSET_MINOR_VERSION)));
start_offset = ntohl(*((bit32 *)(index_base+OFFSET_START_OFFSET)));
record_size = ntohl(*((bit32 *)(index_base+OFFSET_RECORD_SIZE)));
exists = ntohl(*((bit32 *)(index_base+OFFSET_EXISTS)));
if ((format != 0) || (exists == 0) ||
(minor_version == 0) || (minor_version > MAILBOX_MINOR_VERSION) ||
(start_offset == 0) || (start_offset > INDEX_HEADER_SIZE) ||
(record_size == 0) || (record_size > INDEX_RECORD_SIZE) ||
(sbuf.st_size < (start_offset + exists * record_size))) {
syslog(LOG_ERR, "Unable to verify header - deleting: %s", fnamebuf);
close (expunge_fd);
map_free(&index_base, &index_len);
unlink(fnamebuf);
return (0);
}
p = index_base + start_offset;
for (msgno = 1; msgno <= exists; msgno++, p += record_size) {
uid = ntohl(*((bit32 *)(p + OFFSET_UID)));
if (uid == 0) {
syslog(LOG_ERR, "IOERROR: %s zero expunge record %u/%lu",
fnamebuf, msgno, exists);
break;
}
mailbox_message_get_fname(mailbox, uid, msgfname, sizeof(msgfname));
if (keepflag == 0) {
unlink(msgfname);
continue;
}
if ( (stat (msgfname, &sbuf)) != 0) {
syslog (LOG_ERR, "IOERROR: %m while stat\'ing message %s", msgfname);
continue;
}
if (sbuf.st_size == 0) {
unlink(msgfname);
continue;
}
if (expuid_idx == expuid_alloc) {
expuid_alloc += UIDGROW;
expuid_array = (unsigned long *) xrealloc((char *)*expuid,
expuid_alloc * sizeof(unsigned long));
}
expuid_array[expuid_idx] = uid;
expuid_idx++;
}
*expuid = expuid_array;
*expuid_num = expuid_idx;
if (keepflag == 0) {
map_free(&index_base, &index_len);
close (expunge_fd);
unlink(fnamebuf);
return (0);
}
if (exists == expuid_idx) {
close(expunge_fd);
map_free(&index_base, &index_len);
} else if ( expuid_idx == 0) {
close(expunge_fd);
map_free(&index_base, &index_len);
unlink (fnamebuf);
} else {
strcpy (newfnamebuf, fnamebuf);
strcat (newfnamebuf, ".NEW");
fexpunge = fopen(newfnamebuf, "w+");
if (fexpunge == NULL) {
syslog (LOG_ERR, "IOERROR: %m while creating: %s", newfnamebuf);
close(expunge_fd);
map_free(&index_base, &index_len);
free(expuid);
return IMAP_IOERROR;
}
unsigned msgno;
const char *p = index_base + start_offset;
for (msgno = 1; msgno <= exists; msgno++, p += record_size) {
unsigned long fileuid = ntohl(*((bit32 *)(p + OFFSET_UID)));
for (expmsg = 0; expmsg < expuid_idx; expmsg++) {
if (fileuid == expuid_array [expmsg]) {
n = fwrite(buf, 1, INDEX_RECORD_SIZE, fexpunge);
if (n != INDEX_RECORD_SIZE) {
syslog (LOG_ERR,
"IOERROR: %m writing cyrus.expunge record: %s", newfnamebuf);
close(expunge_fd);
map_free(&index_base, &index_len);
free(expuid);
fclose(fexpunge);
return IMAP_IOERROR;
}
break;
}
}
}
rewind (fexpunge);
*((bit32 *)(buf+OFFSET_EXISTS)) = htonl(*expuid_num);
n = fwrite(buf, 1, INDEX_HEADER_SIZE, fexpunge);
if (n != INDEX_HEADER_SIZE || ferror(fexpunge) ) {
syslog (LOG_ERR, "IOERROR: %m writing expunge header: %s", newfnamebuf);
close(expunge_fd);
map_free(&index_base, &index_len);
free(expuid);
fclose(fexpunge);
return IMAP_IOERROR;
}
close(expunge_fd);
map_free(&index_base, &index_len);
unlink(fnamebuf);
fchmod (fileno (fexpunge),
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH |S_IWOTH);
fclose(fexpunge);
if (rename(newfnamebuf, fnamebuf)) {
free(expuid);
return IMAP_IOERROR;
}
}
if (expuid_idx > 0) {
qsort((char *)expuid_array, expuid_idx, sizeof(expuid_array), compare_uid);
}
return (0);
}
char *
getmailname (char * mailboxname)
{
static char namebuf[MAX_MAILBOX_PATH + 1];
static int namebuflen;
char * pname;
strlcpy (namebuf, mailboxname, sizeof (namebuf));
pname = strchr (namebuf, '.');
if (pname) {
pname = strchr(pname + 1, '.');
if (pname)
*pname = '\0';
}
return (namebuf);
}
struct uniqmailid *
find_uniqid ( char * mailboxname, char * mailboxid)
{
struct uniqmailid *puniq;
char * nameptr;
nameptr = getmailname (mailboxname);
for (puniq = uniqmid_head; puniq != NULL; puniq = puniq->uniqnext) {
if (strcmp (puniq->uniqmbxid, mailboxid) == 0) {
if (strcmp (puniq->uniqname, nameptr) == 0) {
return (puniq);
}
}
}
return NULL;
}
struct uniqmailid *
add_uniqid ( char * mailboxname, char * mailboxid)
{
struct uniqmailid *puniq;
char *pboxname;
pboxname = getmailname (mailboxname);
puniq = xmalloc (sizeof (struct uniqmailid));
puniq->uniqmbxid = xstrdup(mailboxid);
puniq->uniqname = xstrdup(pboxname);
puniq->uniqnext = uniqmid_head;
uniqmid_head = puniq;
return (puniq);
}
int reconstruct(char *name, struct discovered *found)
{
char buf[((INDEX_HEADER_SIZE > INDEX_RECORD_SIZE) ?
INDEX_HEADER_SIZE : INDEX_RECORD_SIZE)];
char quota_root[MAX_MAILBOX_PATH+1];
bit32 valid_user_flags[MAX_USER_FLAGS/32];
struct mailbox mailbox;
int r = 0;
int i, n, hasquota, flag;
int format = MAILBOX_FORMAT_NORMAL;
char *p;
#ifdef APPLE_OS_X_SERVER
char msgUID[ MAX_MAILBOX_NAME + 1 ];
char userID[ MAX_MAILBOX_NAME + 1 ];
char fnamebufflags[ MAILBOX_FNAME_LEN + 15 ];
#endif
char fnamebuf[MAX_MAILBOX_PATH+1], newfnamebuf[MAX_MAILBOX_PATH+1];
FILE *newindex, *msgfile;
DIR *dirp;
struct dirent *dirent;
struct stat sbuf;
int newcache_fd;
unsigned long *uid;
int uid_num, uid_alloc;
unsigned long *expuid;
int expuid_num;
int expmsg;
int msg, old_msg = 0;
int new_exists = 0,
new_answered = 0,
new_flagged = 0,
new_deleted = 0;
char *list_acl, *list_part;
int list_type;
uquota_t new_quota = 0;
struct index_record message_index, old_index;
static struct index_record zero_index;
struct body *body = NULL;
#ifdef APPLE_OS_X_SERVER
FILE *flagsfile;
struct stat sbufflags;
#endif
char *mypath, *mympath, *mypart, *myacl;
int mytype;
char mbpath[MAX_MAILBOX_PATH+1], *path;
int expunge_found;
char unique_buf[32];
r = mboxlist_detail(name, &mytype, &mypath, &mympath,
&mypart, &myacl, NULL);
if(r) return r;
path = (mympath &&
(config_metapartition_files &
IMAP_ENUM_METAPARTITION_FILES_HEADER)) ?
mympath : mypath;
snprintf(mbpath, sizeof(mbpath), "%s%s", path, FNAME_HEADER);
if(stat(mbpath, &sbuf) == -1) {
r = mailbox_create(name, mypart, myacl, NULL,
((mytype & MBTYPE_NETNEWS) ?
MAILBOX_FORMAT_NETNEWS :
MAILBOX_FORMAT_NORMAL), NULL);
if(r) return r;
}
r = mailbox_open_header(name, 0, &mailbox);
if (r) return r;
if (mailbox.header_fd != -1) {
(void) mailbox_lock_header(&mailbox);
}
mailbox.header_lock_count = 1;
if (chdir(mailbox.path) == -1) {
return IMAP_IOERROR;
}
hasquota = quota_findroot(quota_root, sizeof(quota_root), mailbox.name);
if (mailbox.quota.root) free(mailbox.quota.root);
if (hasquota) {
mailbox.quota.root = xstrdup(quota_root);
}
else {
mailbox.quota.root = 0;
}
for (i = 0; i < MAX_USER_FLAGS/32; i++) {
valid_user_flags[i] = 0;
}
for (flag = 0; flag < MAX_USER_FLAGS; flag++) {
if (!mailbox.flagname[flag]) continue;
if ((flag && !mailbox.flagname[flag-1]) ||
!imparse_isatom(mailbox.flagname[flag])) {
free(mailbox.flagname[flag]);
mailbox.flagname[flag] = 0;
}
valid_user_flags[flag/32] |= 1<<(flag&31);
}
r = mailbox_read_header_acl(&mailbox);
if (r) return r;
r = mboxlist_detail(name, &list_type, NULL, NULL,
&list_part, &list_acl, NULL);
if (r) return r;
if(strcmp(list_acl, mailbox.acl)) {
r = mboxlist_update(name, list_type, list_part, mailbox.acl, 0);
}
if(r) return r;
r = mailbox_open_index(&mailbox);
if (r) {
mailbox.exists = 0;
mailbox.last_uid = 0;
mailbox.last_appenddate = 0;
mailbox.uidvalidity = time(0);
mailbox.options = OPT_POP3_NEW_UIDL;
mailbox.highestmodseq = 1;
}
else {
(void) mailbox_lock_index(&mailbox);
}
mailbox.index_lock_count = 1;
mailbox.pop3_last_login = 0;
path = (mailbox.mpath &&
(config_metapartition_files &
IMAP_ENUM_METAPARTITION_FILES_INDEX)) ?
mailbox.mpath : mailbox.path;
strlcpy(fnamebuf, path, sizeof(fnamebuf));
strlcat(fnamebuf, FNAME_INDEX, sizeof(fnamebuf));
strlcat(fnamebuf, ".NEW", sizeof(fnamebuf));
newindex = fopen(fnamebuf, "w+");
if (!newindex) {
mailbox_close(&mailbox);
return IMAP_IOERROR;
}
path = (mailbox.mpath &&
(config_metapartition_files &
IMAP_ENUM_METAPARTITION_FILES_CACHE)) ?
mailbox.mpath : mailbox.path;
strlcpy(fnamebuf, path, sizeof(fnamebuf));
strlcat(fnamebuf, FNAME_CACHE, sizeof(fnamebuf));
strlcat(fnamebuf, ".NEW", sizeof(fnamebuf));
newcache_fd = open(fnamebuf, O_RDWR|O_TRUNC|O_CREAT, 0666);
if (newcache_fd == -1) {
fclose(newindex);
mailbox_close(&mailbox);
return IMAP_IOERROR;
}
memset(buf, 0, sizeof(buf));
*((bit32 *)(buf+OFFSET_GENERATION_NO)) = htonl(mailbox.generation_no + 1);
fwrite(buf, 1, INDEX_HEADER_SIZE, newindex);
retry_write(newcache_fd, buf, sizeof(bit32));
if (reconstruct_expunge ( path, &mailbox, &expuid, &expuid_num) != 0) {
fclose(newindex);
mailbox_close(&mailbox);
return IMAP_IOERROR;
}
uid = (unsigned long *) xmalloc(UIDGROW * sizeof(unsigned long));
uid_num = 0;
uid_alloc = UIDGROW;
dirp = opendir(".");
if (!dirp) {
fclose(newindex);
close(newcache_fd);
mailbox_close(&mailbox);
free(uid);
return IMAP_IOERROR;
}
while ((dirent = readdir(dirp))!=NULL) {
if (!isdigit((int) (dirent->d_name[0])) || dirent->d_name[0] == '0')
continue;
p = dirent->d_name;
msg = 0;
while (isdigit((int) *p)) {
msg = msg * 10 + *p++ - '0';
}
if (*p++ != '.') continue;
if (*p) continue;
if (uid_num == uid_alloc) {
uid_alloc += UIDGROW;
uid = (unsigned long *)
xrealloc((char *)uid, uid_alloc * sizeof(unsigned long));
}
uid[uid_num] = msg;
uid_num++;
}
closedir(dirp);
qsort((char *)uid, uid_num, sizeof(*uid), compare_uid);
old_msg = 0;
old_index.uid = 0;
mailbox.format = format;
if (mailbox.cache_fd) close(mailbox.cache_fd);
mailbox.cache_fd = newcache_fd;
for (msg = 0; msg < uid_num; msg++) {
char msgfname[MAILBOX_FNAME_LEN+1];
message_index = zero_index;
message_index.uid = uid[msg];
mailbox_message_get_fname(&mailbox, uid[msg], msgfname, sizeof(msgfname));
msgfile = fopen(msgfname, "r");
if (!msgfile) {
fprintf(stderr, "reconstruct: fopen() failed for '%s' [error=%d] -- skipping.\n",
msgfname, errno);
continue;
}
if (fstat(fileno(msgfile), &sbuf)) {
fclose(msgfile);
continue;
}
if (sbuf.st_size == 0) {
fclose(msgfile);
unlink(msgfname);
continue;
}
expunge_found = 0;
for (expmsg = 0; expmsg < expuid_num; expmsg++) {
if (uid[msg] == expuid [expmsg]) {
expunge_found = 1;
break;
}
if (uid[msg] > expuid [expmsg]) {
break;
}
}
if ( expunge_found == 0 ) {
while (old_msg < mailbox.exists && old_index.uid < uid[msg]) {
if (mailbox_read_index_record(&mailbox, ++old_msg, &old_index)) {
old_index.uid = 0;
}
}
#ifdef APPLE_OS_X_SERVER
sprintf( fnamebufflags, "%sams_extra_data", fnamebuf );
#endif
if (old_index.uid == uid[msg]) {
message_index.internaldate = old_index.internaldate;
message_index.modseq = old_index.modseq;
if (!message_index.modseq) message_index.modseq = 1;
message_index.system_flags = old_index.system_flags &
(FLAG_ANSWERED|FLAG_FLAGGED|FLAG_DELETED|FLAG_DRAFT);
for (i = 0; i < MAX_USER_FLAGS/32; i++) {
message_index.user_flags[i] =
old_index.user_flags[i] & valid_user_flags[i];
}
message_uuid_copy(&message_index.uuid, &old_index.uuid);
}
#ifdef APPLE_OS_X_SERVER
else if ( !stat( fnamebufflags, &sbufflags ) )
{
flagsfile = fopen( fnamebufflags, "r");
if ( flagsfile && (sbufflags.st_size < 1024) )
{
char data[ 1024 ];
bit32 seenflag = 0;
memset( data, 0, 1024 );
read( fileno( flagsfile ), data, sbufflags.st_size );
sscanf( data, "%lu %lu %lu", &message_index.internaldate,
&message_index.system_flags, &seenflag );
if ( seenflag != 0 )
{
memset( msgUID, 0, sizeof( msgUID ) );
memset( userID, 0, sizeof( userID ) );
if ( strncmp( mailbox.name, "user.", 5 ) == 0 )
{
strlcpy( userID, mailbox.name + 5, sizeof( userID ) );
p = strchr( userID, '.' );
if ( p != NULL )
{
*p = '\0';
}
strlcpy( msgUID, fnamebuf, sizeof( msgUID ) );
p = strchr( msgUID, '.' );
if ( p != NULL )
{
*p = '\0';
}
set_seen_flag( &mailbox, userID, msgUID );
}
}
fclose( flagsfile );
remove( fnamebufflags );
}
else
{
message_index.internaldate = 0;
}
mailbox.options |= OPT_POP3_NEW_UIDL;
}
#endif
else {
message_index.internaldate = sbuf.st_mtime;
mailbox.options |= OPT_POP3_NEW_UIDL;
message_uuid_set_null(&message_index.uuid);
message_index.modseq = 1;
}
message_index.last_updated = time(0);
if (message_index.modseq > mailbox.highestmodseq) {
mailbox.highestmodseq = message_index.modseq;
}
}
if (((r = message_parse_file(msgfile, NULL, NULL, &body)) != 0) ||
((r = message_create_record(&mailbox, &message_index, body)) != 0)) {
fclose(msgfile);
fclose(newindex);
mailbox_close(&mailbox);
free(uid);
if (expuid_num) {
free(expuid);
}
return r;
}
fclose(msgfile);
if (body) message_free_body(body);
if (expunge_found == 0) {
mailbox_index_record_to_buf(&message_index, buf);
n = fwrite(buf, 1, INDEX_RECORD_SIZE, newindex);
if (n != INDEX_RECORD_SIZE) {
fclose(newindex);
mailbox_close(&mailbox);
free(uid);
if (expuid_num) {
free(expuid);
}
return IMAP_IOERROR;
}
new_exists++;
if (message_index.system_flags & FLAG_ANSWERED) new_answered++;
if (message_index.system_flags & FLAG_FLAGGED) new_flagged++;
if (message_index.system_flags & FLAG_DELETED) new_deleted++;
new_quota += message_index.size;
}
}
if (expuid_num) {
free (expuid);
}
if (body) free(body);
rewind(newindex);
if (uid_num && mailbox.last_uid < uid[uid_num-1]) {
mailbox.last_uid = uid[uid_num-1] + 100;
}
if (mailbox.last_appenddate == 0 || mailbox.last_appenddate > time(0)) {
mailbox.last_appenddate = time(0);
}
if (mailbox.uidvalidity == 0 || mailbox.uidvalidity > time(0)) {
mailbox.uidvalidity = time(0);
}
free(uid);
*((bit32 *)(buf+OFFSET_GENERATION_NO)) = htonl(mailbox.generation_no + 1);
*((bit32 *)(buf+OFFSET_FORMAT)) = htonl(mailbox.format);
*((bit32 *)(buf+OFFSET_MINOR_VERSION)) = htonl(MAILBOX_MINOR_VERSION);
*((bit32 *)(buf+OFFSET_START_OFFSET)) = htonl(INDEX_HEADER_SIZE);
*((bit32 *)(buf+OFFSET_RECORD_SIZE)) = htonl(INDEX_RECORD_SIZE);
*((bit32 *)(buf+OFFSET_EXISTS)) = htonl(new_exists);
*((bit32 *)(buf+OFFSET_LAST_APPENDDATE)) = htonl(mailbox.last_appenddate);
*((bit32 *)(buf+OFFSET_LAST_UID)) = htonl(mailbox.last_uid);
#ifdef HAVE_LONG_LONG_INT
*((bit64 *)(buf+OFFSET_QUOTA_MAILBOX_USED64)) = htonll(new_quota);
#else
*((bit32 *)(buf+OFFSET_QUOTA_MAILBOX_USED64)) = htonl(0);
*((bit32 *)(buf+OFFSET_QUOTA_MAILBOX_USED)) = htonl(new_quota);
#endif
*((bit32 *)(buf+OFFSET_POP3_LAST_LOGIN)) = htonl(mailbox.pop3_last_login);
*((bit32 *)(buf+OFFSET_UIDVALIDITY)) = htonl(mailbox.uidvalidity);
*((bit32 *)(buf+OFFSET_DELETED)) = htonl(new_deleted);
*((bit32 *)(buf+OFFSET_ANSWERED)) = htonl(new_answered);
*((bit32 *)(buf+OFFSET_FLAGGED)) = htonl(new_flagged);
*((bit32 *)(buf+OFFSET_MAILBOX_OPTIONS)) = htonl(mailbox.options);
*((bit32 *)(buf+OFFSET_LEAKED_CACHE)) = htonl(0);
#ifdef HAVE_LONG_LONG_INT
*((bit64 *)(buf+OFFSET_HIGHESTMODSEQ_64)) = htonll(mailbox.highestmodseq);
#else
*((bit32 *)(buf+OFFSET_HIGHESTMODSEQ_64)) = htonl(0);
*((bit32 *)(buf+OFFSET_HIGHESTMODSEQ)) = htonl(mailbox.highestmodseq);
#endif
*((bit32 *)(buf+OFFSET_SPARE0)) = htonl(0);
*((bit32 *)(buf+OFFSET_SPARE1)) = htonl(0);
*((bit32 *)(buf+OFFSET_SPARE2)) = htonl(0);
*((bit32 *)(buf+OFFSET_SPARE3)) = htonl(0);
*((bit32 *)(buf+OFFSET_SPARE4)) = htonl(0);
n = fwrite(buf, 1, INDEX_HEADER_SIZE, newindex);
fflush(newindex);
if (n != INDEX_HEADER_SIZE || ferror(newindex)
|| fsync(fileno(newindex)) || fsync(newcache_fd)) {
fclose(newindex);
mailbox_close(&mailbox);
return IMAP_IOERROR;
}
if (!mailbox.uniqueid) {
mailbox_make_uniqueid(mailbox.name, mailbox.uidvalidity, unique_buf,
sizeof(unique_buf));
mailbox.uniqueid = xstrdup(unique_buf);
} else {
if (find_uniqid (mailbox.name, mailbox.uniqueid) != NULL ) {
mailbox_make_uniqueid(mailbox.name, mailbox.uidvalidity, unique_buf,
sizeof(unique_buf));
free (mailbox.uniqueid);
mailbox.uniqueid = xstrdup(unique_buf);
}
}
if (add_uniqid (mailbox.name, mailbox.uniqueid) == NULL) {
syslog (LOG_ERR, "Failed adding mailbox: %s unique id: %s\n",
mailbox.name, mailbox.uniqueid );
}
r = mailbox_write_header(&mailbox);
if (r) {
mailbox_close(&mailbox);
return r;
}
path = (mailbox.mpath &&
(config_metapartition_files &
IMAP_ENUM_METAPARTITION_FILES_INDEX)) ?
mailbox.mpath : mailbox.path;
strlcpy(fnamebuf, path, sizeof(fnamebuf));
strlcat(fnamebuf, FNAME_INDEX, sizeof(fnamebuf));
strlcpy(newfnamebuf, fnamebuf, sizeof(newfnamebuf));
strlcat(newfnamebuf, ".NEW", sizeof(fnamebuf));
if (rename(newfnamebuf, fnamebuf)) {
fclose(newindex);
mailbox_close(&mailbox);
return IMAP_IOERROR;
}
path = (mailbox.mpath &&
(config_metapartition_files &
IMAP_ENUM_METAPARTITION_FILES_CACHE)) ?
mailbox.mpath : mailbox.path;
strlcpy(fnamebuf, path, sizeof(fnamebuf));
strlcat(fnamebuf, FNAME_CACHE, sizeof(fnamebuf));
strlcpy(newfnamebuf, fnamebuf, sizeof(newfnamebuf));
strlcat(newfnamebuf, ".NEW", sizeof(newfnamebuf));
if (rename(newfnamebuf, fnamebuf)) {
fclose(newindex);
mailbox_close(&mailbox);
return IMAP_IOERROR;
}
fclose(newindex);
r = seen_reconstruct(&mailbox, (time_t)0, (time_t)0, (int (*)())0, (void *)0);
if (syncflag) {
sync_log_mailbox(mailbox.name);
}
mailbox_close(&mailbox);
if (found) {
if (mympath &&
(config_metapartition_files & IMAP_ENUM_METAPARTITION_FILES_HEADER) &&
chdir(mympath) == -1) {
return IMAP_IOERROR;
}
dirp = opendir(".");
while ((dirent = readdir(dirp)) != NULL) {
struct discovered *new;
if (strchr(dirent->d_name, '.')) continue;
if (stat(dirent->d_name, &sbuf) < 0) continue;
if (!S_ISDIR(sbuf.st_mode)) continue;
snprintf(fnamebuf, sizeof(fnamebuf), "%s%s",
dirent->d_name, FNAME_HEADER);
if (stat(fnamebuf, &sbuf) < 0) continue;
snprintf(fnamebuf, sizeof(fnamebuf), "%s.%s",
name, dirent->d_name);
do {
r = mboxlist_lookup(fnamebuf, NULL, NULL);
} while (r == IMAP_AGAIN);
if (!r) continue;
if (r != IMAP_MAILBOX_NONEXISTENT) break;
else r = 0;
printf("discovered %s\n", fnamebuf);
new = (struct discovered *) xmalloc(sizeof(struct discovered));
new->name = strdup(fnamebuf);
new->next = found->next;
found->next = new;
}
closedir(dirp);
}
return r;
}
struct todo {
char *name;
char *path;
char *partition;
struct todo *next;
} *todo_head = 0, **todo_tail = &todo_head;
void
todo_append(name, path, partition)
char *name;
char *path;
char *partition;
{
struct todo *newentry;
newentry = (struct todo *)xmalloc(sizeof(struct todo));
newentry->name = name;
newentry->path = path;
newentry->partition = partition;
newentry->next = 0;
*todo_tail = newentry;
todo_tail = &newentry->next;
}
void
todo_append_hashed(char *name, char *path, char *partition)
{
DIR *dirp;
struct dirent *dirent;
dirp = opendir(path);
if (!dirp) {
fprintf(stderr, "reconstruct: couldn't open partition %s: %s\n",
partition, strerror(errno));
} else while ((dirent = readdir(dirp))!=NULL) {
struct todo *newentry;
if (strchr(dirent->d_name, '.')) {
continue;
}
newentry = (struct todo *)xmalloc(sizeof(struct todo));
newentry->name = xstrdup(name);
newentry->path = xmalloc(strlen(path) +
strlen(dirent->d_name) + 2);
sprintf(newentry->path, "%s/%s", path, dirent->d_name);
newentry->partition = partition;
newentry->next = 0;
*todo_tail = newentry;
todo_tail = &newentry->next;
}
}
char *cleanacl(char *acl, char *mboxname)
{
char owner[MAX_MAILBOX_NAME+1];
cyrus_acl_canonproc_t *aclcanonproc = 0;
char *p;
char *newacl;
char *identifier;
char *rights;
if ((p = mboxname_isusermailbox(mboxname, 0))) {
strlcpy(owner, p, sizeof(owner));
p = strchr(owner, '.');
if (p) *p = '\0';
aclcanonproc = mboxlist_ensureOwnerRights;
}
newacl = xstrdup("");
if (aclcanonproc) {
cyrus_acl_set(&newacl, owner, ACL_MODE_SET, ACL_ALL,
(cyrus_acl_canonproc_t *)0, (void *)0);
}
for (;;) {
identifier = acl;
rights = strchr(acl, '\t');
if (!rights) break;
*rights++ = '\0';
acl = strchr(rights, '\t');
if (!acl) break;
*acl++ = '\0';
cyrus_acl_set(&newacl, identifier, ACL_MODE_SET,
cyrus_acl_strtomask(rights), aclcanonproc,
(void *)owner);
}
return newacl;
}
void do_mboxlist(void)
{
fprintf(stderr, "reconstructing mailboxes.db currently not supported\n");
exit(EC_USAGE);
}
#ifdef APPLE_OS_X_SERVER
int set_seen_flag ( struct mailbox *inMailbox, const char *inUser, const char *inUID )
{
int r;
struct seen *seendb;
time_t last_read, last_change;
unsigned last_uid;
char *seenuids;
int last_seen;
char *tail, *p;
int oldlen, newlen;
int start;
start = atoi( inUID );
r = seen_open(inMailbox, inUser, SEEN_CREATE, &seendb);
if (r) return r;
r = seen_lockread(seendb, &last_read, &last_uid, &last_change, &seenuids);
if (r) return r;
oldlen = strlen(seenuids);
newlen = oldlen + strlen( inUID ) + 10;
seenuids = xrealloc(seenuids, newlen);
tail = seenuids + oldlen;
while (tail > seenuids && isdigit((int) tail[-1])) tail--;
for (p = tail, last_seen=0; *p; p++) last_seen = last_seen * 10 + *p - '0';
if (last_seen && last_seen >= start-1) {
if (tail > seenuids && tail[-1] == ':') p = tail - 1;
*p++ = ':';
}
else {
if (p > seenuids) *p++ = ',';
}
strlcpy(p, inUID, newlen-(p-seenuids+1));
r = seen_write(seendb, last_read, last_uid, time(NULL), seenuids);
seen_close(seendb);
free(seenuids);
return r;
}
#endif