/* reconstruct.c -- program to reconstruct a mailbox
*
* 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: reconstruct.c,v 1.112 2010/01/06 17:01:39 murch Exp $
*/
#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 <utime.h>
#include <syslog.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <stdlib.h>
#ifdef HAVE_ZLIB
#include <zlib.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 "crc32.h"
#include "imparse.h"
#include "global.h"
#include "exitcodes.h"
#include "imap_err.h"
#include "mailbox.h"
#include "map.h"
#include "message.h"
#include "message_guid.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"
#include "cyr_lock.h"
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;
/* current namespace */
static struct namespace recon_namespace;
/* config.c stuff */
const int config_need_data = CONFIG_NEED_PARTITION_DATA;
/* forward declarations */
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);
extern cyrus_acl_canonproc_t mboxlist_ensureOwnerRights;
int reconstruct_flags = RECONSTRUCT_MAKE_CHANGES | RECONSTRUCT_DO_STAT;
int main(int argc, char **argv)
{
int opt, i, r;
int rflag = 0;
int mflag = 0;
int fflag = 0;
int xflag = 0;
char buf[MAX_MAILBOX_PATH+1];
char *fname;
struct discovered head;
char *alt_config = NULL;
char *start_part = NULL;
memset(&head, 0, sizeof(head));
if ((geteuid()) == 0 && (become_cyrus() != 0)) {
fatal("must run as the Cyrus user", EC_USAGE);
}
/* Ensure we're up-to-date on the index file format */
assert(INDEX_HEADER_SIZE == (OFFSET_HEADER_CRC+4));
assert(INDEX_RECORD_SIZE == (OFFSET_RECORD_CRC+4));
while ((opt = getopt(argc, argv, "C:kp:rmfsxgGqRUoOn")) != EOF) {
switch (opt) {
case 'C': /* alt config file */
alt_config = optarg;
break;
case 'p':
start_part = optarg;
break;
case 'r':
rflag = 1;
break;
case 'm':
mflag = 1;
break;
case 'n':
reconstruct_flags &= ~RECONSTRUCT_MAKE_CHANGES;
break;
case 'g':
fprintf(stderr, "deprecated option -g used\n");
break;
case 'G':
reconstruct_flags |= RECONSTRUCT_ALWAYS_PARSE;
break;
case 'f':
fflag = 1;
break;
case 'x':
xflag = 1;
break;
case 'k':
fprintf(stderr, "deprecated option -k used\n");
break;
case 's':
reconstruct_flags &= ~RECONSTRUCT_DO_STAT;
break;
case 'q':
reconstruct_flags |= RECONSTRUCT_QUIET;
break;
case 'R':
reconstruct_flags |= RECONSTRUCT_GUID_REWRITE;
break;
case 'U':
reconstruct_flags |= RECONSTRUCT_GUID_UNLINK;
break;
case 'o':
reconstruct_flags |= RECONSTRUCT_IGNORE_ODDFILES;
break;
case 'O':
reconstruct_flags |= RECONSTRUCT_REMOVE_ODDFILES;
break;
default:
usage();
}
}
cyrus_init(alt_config, "reconstruct", 0);
global_sasl_init(1,0,NULL);
/* Set namespace -- force standard (internal) */
if ((r = mboxname_init_namespace(&recon_namespace, 1)) != 0) {
syslog(LOG_ERR, "%s", error_message(r));
fatal(error_message(r), EC_CONFIG);
}
sync_log_init();
if (mflag) {
if (rflag || fflag || optind != argc) {
cyrus_done();
usage();
}
do_mboxlist();
}
mboxlist_init(0);
mboxlist_open(NULL);
quotadb_init(0);
quotadb_open(NULL);
/* Deal with nonexistent mailboxes */
if (start_part) {
/* We were handed a mailbox that does not exist currently */
if(optind == argc) {
fprintf(stderr,
"When using -p, you must specify a mailbox to attempt to reconstruct.");
exit(EC_USAGE);
}
/* do any of the mailboxes exist in mboxlist already? */
/* Do they look like mailboxes? */
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);
}
/* Translate mailboxname */
(*recon_namespace.mboxname_tointernal)(&recon_namespace, argv[i],
NULL, buf);
/* Does it exist */
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);
}
/* Does the suspected path *look* like a mailbox? */
fname = mboxname_metapath(start_part, buf, META_HEADER, 0);
if (stat(fname, &sbuf) < 0) {
fprintf(stderr,
"%s does not appear to be a mailbox (no %s).\n",
argv[i], fname);
exit(EC_USAGE);
}
}
/* None of them exist. Create them. */
for (i = optind; i < argc; i++) {
/* Translate mailboxname */
(*recon_namespace.mboxname_tointernal)(&recon_namespace, argv[i],
NULL, buf);
r = mboxlist_createmailbox(buf, 0, start_part, 1,
"cyrus", NULL, 0, 0, !xflag);
if(r) {
fprintf(stderr, "could not create %s\n", argv[i]);
}
}
}
/* Normal Operation */
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;
/* save domain */
if (config_virtdomains) domain = strchr(argv[i], '@');
strlcpy(buf, argv[i], sizeof(buf));
/* Translate any separators in mailboxname */
mboxname_hiersep_tointernal(&recon_namespace, buf,
config_virtdomains ?
strcspn(buf, "@") : 0);
/* reconstruct the first mailbox/pattern */
(*recon_namespace.mboxlist_findall)(&recon_namespace, buf, 1, 0,
0, do_reconstruct,
fflag ? &head : NULL);
if (rflag) {
/* build a pattern for submailboxes */
char *p = strchr(buf, '@');
if (p) *p = '\0';
strlcat(buf, ".*", sizeof(buf));
/* append the domain */
if (domain) strlcat(buf, domain, sizeof(buf));
/* reconstruct the submailboxes */
(*recon_namespace.mboxlist_findall)(&recon_namespace, buf, 1, 0,
0, do_reconstruct,
fflag ? &head : NULL);
}
}
/* examine our list to see if we discovered anything */
while (head.next) {
struct discovered *p;
int r = 0;
p = head.next;
head.next = p->next;
/* create p (database only) and reconstruct it */
/* partition is defined by the parent mailbox */
r = mboxlist_createmailbox(p->name, 0, NULL, 1,
"cyrus", NULL, 0, 0, !xflag);
if (r) {
fprintf(stderr, "createmailbox %s: %s\n",
p->name, error_message(r));
} else {
do_reconstruct(p->name, strlen(p->name), 0, &head);
}
/* may have added more things into our list */
free(p->name);
free(p);
}
sync_log_done();
mboxlist_close();
mboxlist_done();
quotadb_close();
quotadb_done();
cyrus_done();
return 0;
}
void usage(void)
{
fprintf(stderr,
"usage: reconstruct [-C <alt_config>] [-p partition] [-ksrfx] mailbox...\n");
fprintf(stderr, " reconstruct [-C <alt_config>] -m\n");
exit(EC_USAGE);
}
/*
* mboxlist_findall() callback function to reconstruct a mailbox
*/
int
do_reconstruct(char *name,
int matchlen,
int maycreate __attribute__((unused)),
void *rock)
{
struct discovered *found = (struct discovered *)rock;
int r;
char buf[MAX_MAILBOX_NAME];
static char lastname[MAX_MAILBOX_NAME] = "";
struct mailbox *mailbox = NULL;
char outpath[MAX_MAILBOX_PATH];
signals_poll();
/* don't repeat */
if (matchlen == (int) strlen(lastname) &&
!strncmp(name, lastname, matchlen)) return 0;
if(matchlen >= (int) sizeof(lastname))
matchlen = sizeof(lastname) - 1;
strncpy(lastname, name, matchlen);
lastname[matchlen] = '\0';
r = mailbox_reconstruct(lastname, reconstruct_flags);
if (r) {
com_err(lastname, r, "%s",
(r == IMAP_IOERROR) ? error_message(errno) : NULL);
return 0;
}
r = mailbox_open_iwl(lastname, &mailbox);
if (r) {
com_err(lastname, r, "Failed to open after reconstruct");
return 0;
}
if (!add_uniqid(lastname, mailbox->uniqueid)) {
syslog (LOG_ERR, "Failed adding mailbox: %s unique id: %s\n",
mailbox->name, mailbox->uniqueid );
}
/* Convert internal name to external */
(*recon_namespace.mboxname_toexternal)(&recon_namespace, lastname,
NULL, buf);
if (!(reconstruct_flags & RECONSTRUCT_QUIET))
printf("%s\n", buf);
strncpy(outpath, mailbox_meta_fname(mailbox, META_HEADER), MAX_MAILBOX_NAME);
mailbox_close(&mailbox);
if (found) {
char fnamebuf[MAX_MAILBOX_PATH];
char *ptr;
DIR *dirp;
struct dirent *dirent;
struct stat sbuf;
ptr = strstr(outpath, "cyrus.header");
if (!ptr) return 0;
*ptr = 0;
r = chdir(outpath);
if (r) return 0;
/* we recurse down this directory to see if there's any mailboxes
under this not in the mailboxes database */
dirp = opendir(".");
if (!dirp) return 0;
while ((dirent = readdir(dirp)) != NULL) {
struct discovered *new;
/* mailbox directories never have a dot in them */
if (strchr(dirent->d_name, '.')) continue;
if (stat(dirent->d_name, &sbuf) < 0) continue;
if (!S_ISDIR(sbuf.st_mode)) continue;
/* ok, we found a directory that doesn't have a dot in it;
is there a cyrus.header file? */
snprintf(fnamebuf, MAX_MAILBOX_PATH, "%s%s",
dirent->d_name, FNAME_HEADER);
if (stat(fnamebuf, &sbuf) < 0) continue;
/* ok, we have a real mailbox directory */
snprintf(buf, MAX_MAILBOX_NAME, "%s.%s",
name, dirent->d_name);
/* does fnamebuf exist as a mailbox in mboxlist? */
do {
r = mboxlist_lookup(buf, NULL, NULL);
} while (r == IMAP_AGAIN);
if (!r) continue; /* mailbox exists; it'll be reconstructed
with a -r */
if (r != IMAP_MAILBOX_NONEXISTENT) break; /* erg? */
else r = 0; /* reset error condition */
printf("discovered %s\n", buf);
new = (struct discovered *) xmalloc(sizeof(struct discovered));
new->name = strdup(buf);
new->next = found->next;
found->next = new;
}
closedir(dirp);
}
return 0;
}
char *getmailname(char *mailboxname)
{
static char namebuf[MAX_MAILBOX_PATH + 1];
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);
}
/*
* Reconstruct the mailboxes list.
*/
void do_mboxlist(void)
{
fprintf(stderr, "reconstructing mailboxes.db currently not supported\n");
exit(EC_USAGE);
}