Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F120824152
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
35 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/imap/ctl_mboxlist.c b/imap/ctl_mboxlist.c
index b6e2c1775..ff5d1cf55 100644
--- a/imap/ctl_mboxlist.c
+++ b/imap/ctl_mboxlist.c
@@ -1,1069 +1,1170 @@
/* ctl_mboxlist.c -- do DB related operations on mboxlist
*
* 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.
*/
/* currently doesn't catch signals; probably SHOULD */
#include <config.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 <sys/types.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <sysexits.h>
#include <syslog.h>
#include <stdlib.h>
#include <string.h>
#include <sasl/sasl.h>
#include "assert.h"
#include "annotate.h"
#include "dlist.h"
#include "global.h"
#include "libcyr_cfg.h"
#include "mboxlist.h"
#include "mupdate.h"
#include "user.h"
#include "util.h"
#include "xmalloc.h"
#include "xstrlcpy.h"
/* generated headers are not necessarily in current directory */
#include "imap/imap_err.h"
#include "imap/mupdate_err.h"
extern int optind;
extern char *optarg;
enum mboxop { DUMP,
M_POPULATE,
RECOVER,
CHECKPOINT,
UNDUMP,
VERIFY,
+ ADD_ROOTS,
NONE };
struct dumprock {
enum mboxop op;
const char *partition;
int purge;
mupdate_handle *h;
};
struct mb_node
{
char mailbox[MAX_MAILBOX_BUFFER];
char location[MAX_MAILBOX_BUFFER];
char *acl;
struct mb_node *next;
};
static struct mb_node *act_head = NULL, **act_tail = &act_head;
static struct mb_node *del_head = NULL;
static struct mb_node *wipe_head = NULL, *unflag_head = NULL;
/* assume the local copy is authoritative and that it should just overwrite
* mupdate */
static int local_authoritative = 0;
static int warn_only = 0;
static int interactive = 0;
/* For each mailbox that this guy gets called for, check that
* it is a mailbox that:
* a) mupdate server thinks *we* host
* -> Because we were called, this is the case, provided we
* -> gave the prefix parameter to the remote.
* -> (And assuming bugs don't exist.)
* b) we do not actually host
*
* if that's the case, enqueue a delete
* otherwise, we both agree that it exists, but we still need
* to verify that its info is up to date.
*/
static int mupdate_list_cb(struct mupdate_mailboxdata *mdata,
const char *cmd,
void *context __attribute__((unused)))
{
int ret;
/* the server thinks we have it, do we think we have it? */
ret = mboxlist_lookup(mdata->mailbox, NULL, NULL);
if (ret) {
struct mb_node *next;
next = xzmalloc(sizeof(struct mb_node));
strlcpy(next->mailbox, mdata->mailbox, sizeof(next->mailbox));
next->next = del_head;
del_head = next;
} else {
/* we both agree that it exists */
/* throw it onto the back of the activate queue */
/* we may or may not need to send an update */
struct mb_node *next;
next = xzmalloc(sizeof(struct mb_node));
strlcpy(next->mailbox, mdata->mailbox, sizeof(next->mailbox));
strlcpy(next->location, mdata->location, sizeof(next->location));
if (!strncmp(cmd, "MAILBOX", 7))
next->acl = xstrdup(mdata->acl);
*act_tail = next;
act_tail = &(next->next);
}
return 0;
}
static int dump_cb(const mbentry_t *mbentry, void *rockp)
{
struct dumprock *d = (struct dumprock *) rockp;
int r = 0;
switch (d->op) {
case DUMP:
if (!d->partition || !strcmpsafe(d->partition, mbentry->partition)) {
printf("%s\t%d ", mbentry->name, mbentry->mbtype);
if (mbentry->server) printf("%s!", mbentry->server);
printf("%s %s\n", mbentry->partition, mbentry->acl);
if (d->purge) {
mboxlist_delete(mbentry->name);
}
}
break;
case M_POPULATE:
{
if (mbentry->mbtype & MBTYPE_DELETED)
return 0;
/* realpart is 'hostname!partition' */
char *realpart =
strconcat(config_servername, "!", mbentry->partition, (char *)NULL);
int skip_flag = 0;
/* If it is marked MBTYPE_MOVING, and it DOES match the entry,
* we need to unmark it. If it does not match the entry in our
* list, then we assume that it successfully made the move and
* we delete it from the local disk */
/* If they match, then we should check that we actually need
* to update it. If they *don't* match, then we believe that we
* need to send fresh data. There will be no point at which something
* is in the act_head list that we do not have locally, because that
* is a condition of being in the act_head list */
if (act_head && !strcmp(mbentry->name, act_head->mailbox)) {
struct mb_node *tmp;
/* If this mailbox was moving, we want to unmark the movingness,
* since the MUPDATE server agreed that it lives here. */
/* (and later also force an mupdate push) */
if (mbentry->mbtype & MBTYPE_MOVING) {
struct mb_node *next;
syslog(LOG_WARNING, "Remove remote flag on: %s", mbentry->name);
if (warn_only) {
printf("Remove remote flag on: %s\n", mbentry->name);
} else {
next = xzmalloc(sizeof(struct mb_node));
strlcpy(next->mailbox, mbentry->name, sizeof(next->mailbox));
next->next = unflag_head;
unflag_head = next;
}
/* No need to update mupdate NOW, we'll get it when we
* untag the mailbox */
skip_flag = 1;
} else if (act_head->acl) {
if (
!strcmp(realpart, act_head->location) &&
!strcmp(mbentry->acl, act_head->acl)
) {
/* Do not update if location does match, and there is an acl,
* and the acl matches */
skip_flag = 1;
}
}
/* in any case, free the node. */
if (act_head->acl) free(act_head->acl);
tmp = act_head;
act_head = act_head->next;
if (tmp) free(tmp);
} else {
/* if they do not match, do an explicit MUPDATE find on the
* mailbox, and if it is living somewhere else, delete the local
* data, if it is NOT living somewhere else, recreate it in
* mupdate */
struct mupdate_mailboxdata *mdata;
/* if this is okay, we found it (so it is on another host, since
* it wasn't in our list in this position) */
if (!local_authoritative &&
!mupdate_find(d->h, mbentry->name, &mdata)) {
/* since it lives on another server, schedule it for a wipe */
struct mb_node *next;
/*
* Verify that what we found points at another host,
* not back to this host. Good idea, since if our assumption
* if wrong, we'll end up removing the authoritative
* mailbox.
*/
if (strcmp(realpart, mdata->location) == 0 ) {
if ( act_head ) {
fprintf( stderr, "mupdate said: %s %s %s\n",
act_head->mailbox, act_head->location, act_head->acl );
}
fprintf( stderr, "mailboxes.db said: %s %s %s\n",
mbentry->name, realpart, mbentry->acl );
fprintf( stderr, "mupdate says: %s %s %s\n",
mdata->mailbox, mdata->location, mdata->acl );
fatal("mupdate said not us before it said us", EX_SOFTWARE);
}
/*
* Where does "unified" murder fit into ctl_mboxlist?
* 1. Only check locally hosted mailboxes.
* 2. Check everything.
* Either way, this check is just wrong!
*/
if (config_mupdate_config !=
IMAP_ENUM_MUPDATE_CONFIG_UNIFIED) {
/* But not for a unified configuration */
syslog(LOG_WARNING, "Remove Local Mailbox: %s", mbentry->name);
if (warn_only) {
printf("Remove Local Mailbox: %s\n", mbentry->name);
} else {
next = xzmalloc(sizeof(struct mb_node));
strlcpy(next->mailbox, mbentry->name, sizeof(next->mailbox));
next->next = wipe_head;
wipe_head = next;
}
}
skip_flag = 1;
} else {
/* Check that it isn't flagged moving */
if (mbentry->mbtype & MBTYPE_MOVING) {
/* it's flagged moving, we'll fix it later (and
* push it then too) */
struct mb_node *next;
syslog(LOG_WARNING, "Remove remote flag on: %s", mbentry->name);
if (warn_only) {
printf("Remove remote flag on: %s\n", mbentry->name);
} else {
next = xzmalloc(sizeof(struct mb_node));
strlcpy(next->mailbox, mbentry->name, sizeof(next->mailbox));
next->next = unflag_head;
unflag_head = next;
}
/* No need to update mupdate now, we'll get it when we
* untag the mailbox */
skip_flag = 1;
}
}
}
if (skip_flag) {
free(realpart);
break;
}
syslog(LOG_WARNING, "Force Activate: %s", mbentry->name);
if (warn_only) {
printf("Force Activate: %s\n", mbentry->name);
free(realpart);
break;
}
r = mupdate_activate(d->h, mbentry->name, realpart, mbentry->acl);
if (r == MUPDATE_NOCONN) {
fprintf(stderr, "permanent failure storing '%s'\n", mbentry->name);
r = IMAP_IOERROR;
} else if (r == MUPDATE_FAIL) {
fprintf(stderr,
"temporary failure storing '%s' (update continuing)\n",
mbentry->name);
r = 0;
} else if (r) {
fprintf(
stderr,
"error storing '%s' (update continuing): %s\n",
mbentry->name,
error_message(r)
);
r = 0;
}
free(realpart);
break;
}
default: /* yikes ! */
abort();
break;
}
return r;
}
/*
* True if user types Y\n or y\n. Anything else is false.
*/
static int yes(void)
{
int c, answer = 0;
c = getchar();
if (c == 'Y' || c == 'y') {
answer = 1;
while ((c = getchar()) != EOF) {
if (c == '\n') {
break;
} else {
answer = 0;
}
}
}
return(answer);
}
/* Resyncing with mupdate:
*
* If it is local and not present on mupdate at all, push to mupdate.
* If it is local and present on mupdate for another host, delete local mailbox
* If it is local and present on mupdate but with incorrect partition/acl,
* update mupdate.
* If it is not local and present on mupdate for this host, delete it from
* mupdate.
*/
static void do_dump(enum mboxop op, const char *part, int purge, int intermediary)
{
struct dumprock d;
int ret;
char buf[8192];
assert(op == DUMP || op == M_POPULATE);
assert(op == DUMP || !purge);
assert(op == DUMP || !part);
d.op = op;
d.partition = part;
d.purge = purge;
if (op == M_POPULATE) {
ret = mupdate_connect(NULL, NULL, &(d.h), NULL);
if (ret) {
fprintf(stderr, "couldn't connect to mupdate server\n");
exit(1);
}
/* now we need a list of what the remote thinks we have
* To generate it, ask for a prefix of '<our hostname>!',
* (to ensure we get exactly our hostname) */
snprintf(buf, sizeof(buf), "%s!", config_servername);
ret = mupdate_list(d.h, mupdate_list_cb, buf, NULL);
if (ret) {
fprintf(stderr, "couldn't do LIST command on mupdate server\n");
exit(1);
}
/* Run pending mupdate deletes */
while (del_head) {
struct mb_node *me = del_head;
del_head = del_head->next;
syslog(LOG_WARNING, "Remove from MUPDATE: %s", me->mailbox);
if (warn_only) {
printf("Remove from MUPDATE: %s\n", me->mailbox);
} else {
ret = mupdate_delete(d.h, me->mailbox);
if (ret) {
fprintf(stderr,
"couldn't mupdate delete %s\n", me->mailbox);
exit(1);
}
}
free(me);
}
}
/* Dump Database */
int flags = MBOXTREE_TOMBSTONES;
if (intermediary) flags |= MBOXTREE_INTERMEDIATES;
mboxlist_allmbox("", &dump_cb, &d, flags);
if (op == M_POPULATE) {
/* Remove MBTYPE_MOVING flags (unflag_head) */
while (unflag_head) {
mbentry_t *mbentry = NULL;
struct mb_node *me = unflag_head;
unflag_head = unflag_head->next;
ret = mboxlist_lookup(me->mailbox, &mbentry, NULL);
if (ret) {
fprintf(stderr,
"couldn't perform lookup to un-remote-flag %s\n",
me->mailbox);
exit(1);
}
/* Reset the partition! */
free(mbentry->server);
mbentry->server = NULL;
mbentry->mbtype &= ~(MBTYPE_MOVING|MBTYPE_REMOTE);
ret = mboxlist_update(mbentry, 1);
if (ret) {
fprintf(stderr,
"couldn't perform update to un-remote-flag %s\n",
me->mailbox);
exit(1);
}
/* force a push to mupdate */
snprintf(buf, sizeof(buf), "%s!%s", config_servername, mbentry->partition);
ret = mupdate_activate(d.h, me->mailbox, buf, mbentry->acl);
if (ret) {
fprintf(stderr,
"couldn't perform mupdatepush to un-remote-flag %s\n",
me->mailbox);
exit(1);
}
mboxlist_entry_free(&mbentry);
free(me);
}
/* Delete local mailboxes where needed (wipe_head) */
if (interactive) {
int count = 0;
struct mb_node *me;
for (me = wipe_head; me != NULL; me = me->next) count++;
if ( count > 0 ) {
fprintf(stderr, "OK to delete %d local mailboxes? ", count);
if (!yes()) {
fprintf(stderr, "Cancelled!\n");
exit(1);
}
}
}
while (wipe_head) {
struct mb_node *me = wipe_head;
wipe_head = wipe_head->next;
struct mboxlock *namespacelock = mboxname_usernamespacelock(me->mailbox);
if (!mboxlist_delayed_delete_isenabled()) {
ret = mboxlist_deletemailbox(me->mailbox, 1, "", NULL, NULL,
MBOXLIST_DELETE_LOCALONLY|MBOXLIST_DELETE_FORCE);
} else if (mboxname_isdeletedmailbox(me->mailbox, NULL)) {
ret = mboxlist_deletemailbox(me->mailbox, 1, "", NULL, NULL,
MBOXLIST_DELETE_LOCALONLY|MBOXLIST_DELETE_FORCE);
} else {
ret = mboxlist_delayed_deletemailbox(me->mailbox, 1, "", NULL, NULL,
MBOXLIST_DELETE_LOCALONLY|MBOXLIST_DELETE_FORCE);
}
mboxname_release(&namespacelock);
if (ret) {
fprintf(stderr, "couldn't delete defunct mailbox %s\n",
me->mailbox);
exit(1);
}
free(me);
}
/* Done with mupdate */
mupdate_disconnect(&(d.h));
sasl_done();
}
return;
}
static void do_undump(void)
{
int r = 0;
char buf[16384];
int line = 0;
const char *name, *partition, *acl;
int mbtype;
char *p;
while (fgets(buf, sizeof(buf), stdin)) {
mbentry_t *newmbentry = NULL;
const char *server = NULL;
line++;
name = buf;
for (p = buf; *p && *p != '\t'; p++) ;
if (!*p) {
fprintf(stderr, "line %d: no partition found\n", line);
continue;
}
*p++ = '\0';
if (Uisdigit(*p)) {
/* new style dump */
mbtype = strtol(p, &p, 10);
/* skip trailing space */
if (*p == ' ') p++;
}
else mbtype = 0;
partition = p;
for (; *p && (*p != ' ') && (*p != '\t'); p++) {
if (*p == '!') {
*p++ = '\0';
server = partition;
partition = p;
}
}
if (!*p) {
fprintf(stderr, "line %d: no acl found\n", line);
continue;
}
*p++ = '\0';
acl = p;
/* chop off the newline */
for (; *p && *p != '\r' && *p != '\n'; p++) ;
*p++ = '\0';
if (strlen(name) >= MAX_MAILBOX_BUFFER) {
fprintf(stderr, "line %d: mailbox name too long\n", line);
continue;
}
if (strlen(partition) >= MAX_PARTITION_LEN) {
fprintf(stderr, "line %d: partition name too long\n", line);
continue;
}
/* generate a new entry */
newmbentry = mboxlist_entry_create();
newmbentry->name = xstrdup(name);
newmbentry->mbtype = mbtype;
newmbentry->server = xstrdupnull(server);
newmbentry->partition = xstrdupnull(partition);
newmbentry->acl = xstrdupnull(acl);
/* XXX - still missing all the new fields */
r = mboxlist_update(newmbentry, /*localonly*/1);
mboxlist_entry_free(&newmbentry);
if (r) break;
}
return;
}
enum {
ROOT = (1<<0),
DOMAIN = (1<<1),
MBOX = (1<<2)
};
struct found_data {
int type;
char mboxname[MAX_MAILBOX_BUFFER];
char partition[MAX_MAILBOX_BUFFER];
char path[MAX_MAILBOX_PATH+1];
};
struct found_list {
int idx;
int size;
int alloc;
struct found_data *data;
};
static void add_path(struct found_list *found, int type,
const char *name, const char *part, const char *path)
{
struct found_data *new;
if (found->size == found->alloc) {
/* reached the end of our allocated array, double it */
found->alloc *= 2;
found->data = xrealloc(found->data,
found->alloc * sizeof(struct found_data));
}
/* add our new node to the end of the array */
new = &found->data[found->size++];
new->type = type;
strcpy(new->mboxname, name);
strcpy(new->partition, part);
strcpy(new->path, path);
}
static void add_part(struct found_list *found,
const char *part, const char *path, int override)
{
int i;
/* see if we already added a partition having this name */
for (i = 0; i < found->size; i++){
if (!strcmp(found->data[i].partition, part)) {
/* found it */
if (override) {
/* replace the path with the one containing cyrus.header */
strcpy(found->data[i].path, path);
}
/* we already have the proper path, so we're done */
return;
}
}
/* add the new partition path */
add_path(found, ROOT, "", part, path);
}
static void get_partitions(const char *key, const char *value, void *rock)
{
static int check_meta = -1;
struct found_list *found = (struct found_list *) rock;
if (check_meta == -1) {
/* see if cyrus.header might be contained in a metapartition */
check_meta = (config_metapartition_files &
IMAP_ENUM_METAPARTITION_FILES_HEADER);
}
if (!strncmp(key, "partition-", 10)) {
add_part(found, key+10, value, 0);
}
else if (check_meta && !strncmp(key, "metapartition-", 14)) {
add_part(found, key+14, value, 1);
}
/* skip any other overflow strings */
}
static int compar_mbox(const void *v1, const void *v2)
{
struct found_data *d1 = (struct found_data *) v1;
struct found_data *d2 = (struct found_data *) v2;
/* non-mailboxes get pushed to the end of the array,
otherwise we do an ASCII sort */
if (d1->type & MBOX) {
if (d2->type & MBOX) return strcmp(d1->mboxname, d2->mboxname);
else return -1;
}
else if (d2->type & MBOX) return 1;
else return 0;
}
static int verify_cb(const mbentry_t *mbentry, void *rockp)
{
// This function is called for every entry in the database,
// and supplied an inventory in &found. *data however does
// not pass dlist_parsemap() unlike is the case with dump_db().
struct found_list *found = (struct found_list *) rockp;
int r = 0;
if (r) {
printf("'%s' has a directory '%s' but no DB entry\n",
found->data[found->idx].mboxname,
found->data[found->idx].path
);
} else {
// Walk the directories to see if the mailbox from data does have
// paths on the filesystem.
do {
r = -1;
if (
(found->idx >= found->size) || /* end of array */
!(found->data[found->idx].type & MBOX) || /* end of mailboxes */
(r = strcmp(mbentry->name, found->data[found->idx].mboxname)) < 0
) {
printf("'%s' has a DB entry but no directory on partition '%s'\n",
mbentry->name, mbentry->partition);
}
else if (r > 0) {
printf("'%s' has a directory '%s' but no DB entry\n",
found->data[found->idx].mboxname,
found->data[found->idx].path
);
found->idx++;
}
else found->idx++;
} while (r > 0);
}
return 0;
}
static void do_verify(void)
{
struct found_list found;
int i;
found.idx = 0;
found.size = 0;
found.alloc = 10;
found.data = xmalloc(found.alloc * sizeof(struct found_data));
/* gather a list of partition paths to search */
config_foreachoverflowstring(get_partitions, &found);
/* scan all paths in our list, tagging valid mailboxes,
and adding paths as we find them */
for (i = 0; i < found.size; i++) {
DIR *dirp;
struct dirent *dirent;
char name[MAX_MAILBOX_BUFFER];
char part[MAX_MAILBOX_BUFFER];
char path[MAX_MAILBOX_PATH+1];
int type;
if (config_hashimapspool && (found.data[i].type & ROOT)) {
/* need to add hashed directories */
int config_fulldirhash = libcyrus_config_getswitch(CYRUSOPT_FULLDIRHASH);
char *tail;
int j, c;
/* make the toplevel partition /a */
if (config_fulldirhash) {
strcat(found.data[i].path, "/A");
c = 'B';
} else {
strcat(found.data[i].path, "/a");
c = 'b';
}
type = (found.data[i].type &= ~ROOT);
/* make a template path for /b - /z */
strcpy(name, found.data[i].mboxname);
strcpy(part, found.data[i].partition);
strcpy(path, found.data[i].path);
tail = path + strlen(path) - 1;
for (j = 1; j < 26; j++, c++) {
*tail = c;
add_path(&found, type, name, part, path);
}
if (config_virtdomains && !type) {
/* need to add root domain directory */
strcpy(tail, "domain");
add_path(&found, DOMAIN | ROOT, name, part, path);
}
}
if (!(dirp = opendir(found.data[i].path))) continue;
while ((dirent = readdir(dirp))) {
const char *fname = FNAME_HEADER;
if (dirent->d_name[0] == '.') continue;
else if (!strcmp(dirent->d_name, fname+1)) {
/* XXX - check that it can be opened */
found.data[i].type |= MBOX;
}
else if (!strchr(dirent->d_name, '.') ||
(found.data[i].type & DOMAIN)) {
/* probably a directory, add it to the array */
type = 0;
strcpy(name, found.data[i].mboxname);
if (config_virtdomains &&
(found.data[i].type == ROOT) &&
!strcmp(dirent->d_name, "domain")) {
/* root domain directory */
type = DOMAIN | ROOT;
}
else if (!name[0] && found.data[i].type & DOMAIN) {
/* toplevel domain directory */
strcat(name, dirent->d_name);
strcat(name, "!");
type = DOMAIN | ROOT;
}
else {
/* possibly a mailbox directory */
if (name[0] && !(found.data[i].type & DOMAIN)) strcat(name, ".");
strcat(name, dirent->d_name);
}
strcpy(part, found.data[i].partition);
strcpy(path, found.data[i].path);
strcat(path, "/");
strcat(path, dirent->d_name);
add_path(&found, type, name, part, path);
}
}
closedir(dirp);
}
qsort(found.data, found.size, sizeof(struct found_data), compar_mbox);
mboxlist_allmbox("", &verify_cb, &found, MBOXTREE_TOMBSTONES);
}
+struct root_rock {
+ mbname_t *mbname;
+ char *mboxid;
+};
+
+static int addroots_cb(const mbentry_t *mbentry, void *rockp)
+{
+ mbname_t *mbname = mbname_from_intname(mbentry->name);
+ struct root_rock *rrock = rockp;
+ int is_root = 0, is_child = 0;
+
+ /* Skip DELETED.* mailboxes */
+ if (mbname_isdeleted(mbname)) {
+ mbname_free(&mbname);
+ return 0;
+ }
+
+ const char *userid = mbname_userid(mbname);
+
+ if (userid) {
+ if (strarray_size(mbname_boxes(mbname)) == 0) {
+ /* INBOX */
+ is_root = 1;
+ }
+ else if (rrock->mbname &&
+ !strcmpnull(userid, mbname_userid(rrock->mbname))) {
+ /* Same user */
+ is_child = 1;
+ }
+ }
+ else {
+ /* Shared mailbox */
+ const strarray_t *boxes = mbname_boxes(mbname);
+
+ if (strarray_size(boxes) == 1) {
+ /* Toplevel mailbox */
+ is_root = 1;
+ }
+ else if (rrock->mbname &&
+ !strcmpnull(strarray_nth(boxes, 0),
+ strarray_nth(mbname_boxes(rrock->mbname), 0))) {
+ /* Same root */
+ is_child = 1;
+ }
+ }
+
+ if (is_root) {
+ /* Switch to new root */
+ mbname_free(&rrock->mbname);
+ free(rrock->mboxid);
+
+ rrock->mbname = mbname;
+ rrock->mboxid = xstrdup(mbentry->uniqueid);
+ }
+ else if (is_child) {
+ /* Add root mailboxid to child */
+ mbentry_t *newentry = mboxlist_entry_copy(mbentry);
+
+ newentry->root_mailboxid = xstrdup(rrock->mboxid);
+ mboxlist_update(newentry, 1 /* localonly */);
+ mboxlist_entry_free(&newentry);
+
+ mbname_free(&mbname);
+ }
+ else {
+ /* Orphaned mailbox */
+ mbname_free(&mbname);
+ }
+
+ return 0;
+}
+
+static void do_addroots(int intermediary)
+{
+ struct root_rock rrock = { NULL, NULL };
+ int flags = MBOXTREE_TOMBSTONES;
+
+ if (intermediary) flags |= MBOXTREE_INTERMEDIATES;
+
+ mboxlist_allmbox("", &addroots_cb, &rrock, flags);
+
+ mbname_free(&rrock.mbname);
+ free(rrock.mboxid);
+}
+
static void usage(void)
{
fprintf(stderr, "DUMP:\n");
fprintf(stderr, " ctl_mboxlist [-C <alt_config>] -d [-x] [-y] [-p partition] [-f filename]\n");
fprintf(stderr, "UNDUMP:\n");
fprintf(stderr,
" ctl_mboxlist [-C <alt_config>] -u [-f filename]"
" [< mboxlist.dump]\n");
fprintf(stderr, "MUPDATE populate:\n");
fprintf(stderr, " ctl_mboxlist [-C <alt_config>] -m [-a] [-w] [-i] [-f filename]\n");
fprintf(stderr, "VERIFY:\n");
fprintf(stderr, " ctl_mboxlist [-C <alt_config>] -v [-f filename]\n");
exit(1);
}
int main(int argc, char *argv[])
{
const char *partition = NULL;
char *mboxdb_fname = NULL;
int dopurge = 0;
int opt;
enum mboxop op = NONE;
char *alt_config = NULL;
int dointermediary = 0;
- while ((opt = getopt(argc, argv, "C:awmdurcxf:p:viy")) != EOF) {
+ while ((opt = getopt(argc, argv, "C:awmdurcxf:p:vsiy")) != EOF) {
switch (opt) {
case 'C': /* alt config file */
alt_config = optarg;
break;
case 'r':
/* deprecated, but we still support it */
fprintf(stderr, "ctl_mboxlist -r is deprecated: "
"use ctl_cyrusdb -r instead\n");
syslog(LOG_WARNING, "ctl_mboxlist -r is deprecated: "
"use ctl_cyrusdb -r instead");
if (op == NONE) op = RECOVER;
else usage();
break;
case 'c':
/* deprecated, but we still support it */
fprintf(stderr, "ctl_mboxlist -c is deprecated: "
"use ctl_cyrusdb -c instead\n");
syslog(LOG_WARNING, "ctl_mboxlist -c is deprecated: "
"use ctl_cyrusdb -c instead");
if (op == NONE) op = CHECKPOINT;
else usage();
break;
case 'f':
if (!mboxdb_fname) {
mboxdb_fname = optarg;
} else {
usage();
}
break;
case 'd':
if (op == NONE) op = DUMP;
else usage();
break;
case 'u':
if (op == NONE) op = UNDUMP;
else usage();
break;
case 'm':
if (op == NONE) op = M_POPULATE;
else usage();
break;
case 'p':
partition = optarg;
break;
case 'x':
dopurge = 1;
break;
case 'a':
local_authoritative = 1;
break;
case 'w':
warn_only = 1;
break;
case 'v':
if (op == NONE) op = VERIFY;
else usage();
break;
+ case 's':
+ if (op == NONE) op = ADD_ROOTS;
+ else usage();
+ break;
+
case 'i':
interactive = 1;
break;
case 'y':
dointermediary = 1;
break;
default:
usage();
break;
}
}
if (op != M_POPULATE && (local_authoritative || warn_only)) usage();
if (op != DUMP && partition) usage();
if (op != DUMP && dopurge) usage();
if (op != DUMP && dointermediary) usage();
if (op == RECOVER) {
syslog(LOG_NOTICE, "running mboxlist recovery");
libcyrus_config_setint(CYRUSOPT_DB_INIT_FLAGS, CYRUSDB_RECOVER);
}
cyrus_init(alt_config, "ctl_mboxlist", 0, 0);
global_sasl_init(1,0,NULL);
switch (op) {
case RECOVER:
/* this was done by the call to cyrus_init via libcyrus */
syslog(LOG_NOTICE, "done running mboxlist recovery");
break;
case CHECKPOINT:
syslog(LOG_NOTICE, "checkpointing mboxlist");
mboxlist_init(MBOXLIST_SYNC);
mboxlist_open(NULL);
mboxlist_close();
mboxlist_done();
syslog(LOG_NOTICE, "done checkpointing mboxlist");
break;
case M_POPULATE:
syslog(LOG_NOTICE, "%spopulating mupdate", warn_only ? "test " : "");
GCC_FALLTHROUGH
case DUMP:
mboxlist_init(0);
mboxlist_open(mboxdb_fname);
do_dump(op, partition, dopurge, dointermediary);
mboxlist_close();
mboxlist_done();
if (op == M_POPULATE) {
syslog(LOG_NOTICE,
"done %spopulating mupdate", warn_only ? "test " : "");
}
break;
case UNDUMP:
mboxlist_init(0);
mboxlist_open(mboxdb_fname);
do_undump();
mboxlist_close();
mboxlist_done();
break;
case VERIFY:
mboxlist_init(0);
mboxlist_open(mboxdb_fname);
do_verify();
mboxlist_close();
mboxlist_done();
break;
+ case ADD_ROOTS:
+ mboxlist_init(0);
+ mboxlist_open(mboxdb_fname);
+
+ do_addroots(dointermediary);
+
+ mboxlist_close();
+ mboxlist_done();
+ break;
+
default:
usage();
cyrus_done();
return 1;
}
cyrus_done();
return 0;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Apr 24, 10:17 AM (1 d, 23 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18896498
Default Alt Text
(35 KB)
Attached To
Mode
R111 cyrus-imapd
Attached
Detach File
Event Timeline