Re: Building an interface to undelete for users

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



We implemented a self service restore page for our users, I'm not sure if
the code is anywhere publicly accessibe and I think it is fairly specific
to our installation since we use kerberos/murder.  Our implementation uses
remctl to make calls to unexpunge on the appropriate backend and we made
some changes to unexpunge to make it more user friendly (prints the output
in human readable form) and efficient (-r recursive + -c message count). 
I've attached the patches for unexpunge if your interested, since I haven't
gotten around to posting them in bugzilla. 

-Brian

On Mon, 29 Mar 2010 11:28:50 +0100, David Mayo <D.J.Mayo@xxxxxxxxxx> wrote:
> We have recently upgraded to Cyrus 2.3 and are making full use of the 
> "delayed delete" feature, and we are considering writing an interface to 
> allow users to undelete their own messages and mailboxes.
> 
> Before I start work on this myself, I thought I'd check with people here 
> to see if anyone has any tips or code they are willing to share. I hope 
> we will be able to publish the product we create.
> 
> Regards,
> 
> 
> Dave.
> 
> David Mayo
> Networks/Systems Administrator
> University of Bath Computing Services, UK
> 
> ----
> Cyrus Home Page: http://cyrusimap.web.cmu.edu/
> Cyrus Wiki/FAQ: http://cyrusimap.web.cmu.edu/twiki
> List Archives/Info: http://asg.web.cmu.edu/cyrus/mailing-list.html
From bf080b28e2b4389558d3bc6f25f605f6924773f9 Mon Sep 17 00:00:00 2001
From: Brian Awood <bawood@xxxxxxxxx>
Date: Thu, 24 Sep 2009 15:43:03 -0400
Subject: [PATCH 1/3] Add recursive (-r) and count expunged (-c) to unexpunge command

The -c flag prints a count of the messages in the expunged state
for a mailbox.  The output is in the form "mailbox: count".
The -r flag causes unexpunge to recurse from the specified
mailbox, it can be combined with the new -c flag, or the existing
-l and -a but not -u.

diff --git a/imap/unexpunge.c b/imap/unexpunge.c
index afbc6df..e9dd19e 100644
--- a/imap/unexpunge.c
+++ b/imap/unexpunge.c
@@ -79,17 +79,24 @@
 /* global state */
 const int config_need_data = 0;
 
+int  unsetdeleted = 0;
+time_t time_since;
+
 /* current namespace */
 static struct namespace unex_namespace;
 
+int do_unexpunge(char *name, int matchlen, int maycreate, void *rock);
+int unexpunge(char *mboxname, unsigned long *uids, unsigned nuids);
+
 int verbose = 0;
 
 void usage(void)
 {
     fprintf(stderr,
-	    "unexpunge [-C <altconfig>] -l <mailbox>\n"
+	    "unexpunge [-C <altconfig>] -l [-r] <mailbox>\n"
+	    "unexpunge [-C <altconfig>] -c [-r] <mailbox>\n"
             "unexpunge [-C <altconfig>] -t time-interval [ -d ] [ -v ] mailbox\n"
-	    "unexpunge [-C <altconfig>] -a [-d] [-v] <mailbox>\n"
+	    "unexpunge [-C <altconfig>] -a [-d] [-v] [-r] <mailbox>\n"
 	    "unexpunge [-C <altconfig>] -u [-d] [-v] <mailbox> <uid>...\n");
     exit(-1);
 }
@@ -99,8 +106,9 @@ enum {
     MODE_LIST,
     MODE_ALL,
     MODE_TIME,
-    MODE_UID
-};
+    MODE_UID,
+    MODE_COUNT
+} mode = MODE_UNKNOWN;
 
 struct msg {
     unsigned recno;
@@ -162,7 +170,7 @@ void list_expunged(struct mailbox *mailbox,
 int restore_expunged(struct mailbox *mailbox,
 		     struct msg *msgs, unsigned long eexists,
 		     const char *expunge_index_base,
-		     unsigned *numrestored, int unsetdeleted)
+		     unsigned *numrestored)
 {
     int r = 0;
     const char *irec;
@@ -415,114 +423,33 @@ int restore_expunged(struct mailbox *mailbox,
     return r;
 }
 
-int main(int argc, char *argv[])
+/*
+ * mboxlist_findall() callback function for unexpunge recursive features
+ */
+int
+do_unexpunge(char *name,
+	  int matchlen __attribute__((unused)),
+	  int maycreate __attribute__((unused)),
+	  void *rock __attribute__((unused)))
 {
-    extern char *optarg;
-    int opt, r = 0;
-    char *alt_config = NULL;
-    char buf[MAX_MAILBOX_PATH+1];
-    struct mailbox mailbox;
-    int doclose = 0, mode = MODE_UNKNOWN, unsetdeleted = 0;
-    char expunge_fname[MAX_MAILBOX_PATH+1];
+    return unexpunge(name, NULL, 0);
+}
+
+int
+unexpunge(char *mboxname, unsigned long *uids, unsigned nuids)
+{
+    int doclose = 0, r;
     int expunge_fd = -1;
-    struct stat sbuf;
+    char expunge_fname[MAX_MAILBOX_PATH+1];
     const char *lockfailaction;
-    struct msg *msgs;
     unsigned numrestored = 0;
-    time_t last_update, time_since = time(NULL);
-    int len, secs = 0;
-
-    if ((geteuid()) == 0 && (become_cyrus() != 0)) {
-	fatal("must run as the Cyrus user", EC_USAGE);
-    }
-
-    while ((opt = getopt(argc, argv, "C:laudt:v")) != EOF) {
-	switch (opt) {
-	case 'C': /* alt config file */
-	    alt_config = optarg;
-	    break;
-
-	case 'l':
-	    if (mode != MODE_UNKNOWN) usage();
-	    mode = MODE_LIST;
-	    break;
-	
-	case 'a':
-	    if (mode != MODE_UNKNOWN) usage();
-	    mode = MODE_ALL;
-	    break;
-	
-	case 't':
-	    if (mode != MODE_UNKNOWN) usage();
-
-	    mode = MODE_TIME;
-            secs = atoi(optarg);
-            len  = strlen(optarg);
-            
-            if ((secs > 0) && (len > 1)) {
-                switch (optarg[len-1]) {
-                case 'm':
-                    secs *= 60;
-                    break;
-                case 'h':
-                    secs *= (60*60);
-                    break;
-                case 'd':
-                    secs *= (24*60*60);
-                    break;
-                case 'w':
-                    secs *= (7*24*60*60);
-                    break;
-                }
-            }
-            time_since = time(NULL) - secs;
-	    break;
-
-	case 'u':
-	    if (mode != MODE_UNKNOWN) usage();
-	    mode = MODE_UID;
-	    break;
-
-	case 'd':
-	    unsetdeleted = 1;
-	    break;
-	
-	case 'v':
-	    verbose = 1;
-	    break;
-	
-	default:
-	    usage();
-	    break;
-	}
-    }
-
-    /* sanity check */
-    if (mode == MODE_UNKNOWN ||
-	(optind + (mode == MODE_UID ? 1 : 0)) >= argc) usage();
-
-    cyrus_init(alt_config, "unexpunge", 0);
-
-    mboxlist_init(0);
-    mboxlist_open(NULL);
-
-    quotadb_init(0);
-    quotadb_open(NULL);
-
-    sync_log_init();
-
-    /* Set namespace -- force standard (internal) */
-    if ((r = mboxname_init_namespace(&unex_namespace, 1)) != 0) {
-	syslog(LOG_ERR, error_message(r));
-	fatal(error_message(r), EC_CONFIG);
-    }
-
-    /* Translate mailboxname */
-    (*unex_namespace.mboxname_tointernal)(&unex_namespace, argv[optind],
-					  NULL, buf);
+    struct mailbox mailbox;
+    struct msg *msgs;
+    struct stat sbuf;
+    time_t last_update;
 
     /* Open/lock header */
-    r = mailbox_open_header(buf, 0, &mailbox);
+    r = mailbox_open_header(mboxname, 0, &mailbox);
     if (!r && mailbox.header_fd != -1) {
 	doclose = 1;
 	(void) mailbox_lock_header(&mailbox);
@@ -537,6 +464,13 @@ int main(int argc, char *argv[])
     }
     if (!r) r = mailbox_lock_pop(&mailbox);
 
+    if (r) {
+	/* mailbox corrupt/nonexistent -- skip it */
+	syslog(LOG_WARNING, "unable to open/lock mailbox %s", mboxname);
+	if (doclose) mailbox_close(&mailbox);
+	return 0;
+    }
+
     /* Open expunge index */
     if (!r) {
 	char *path =
@@ -550,9 +484,16 @@ int main(int argc, char *argv[])
 	expunge_fd = open(expunge_fname, O_RDWR, 0666);
     }
 
-    if (r || expunge_fd == -1) {
+    if (expunge_fd == -1) {
+	if (mode == MODE_COUNT) {
+	    /* Translate mailboxname */
+	    char buf[MAX_MAILBOX_PATH+1];
+	    (*unex_namespace.mboxname_toexternal)(&unex_namespace,
+			     mailbox.name, NULL, buf);
+	    printf("%s: 0\n", buf);
+	}
 	/* mailbox corrupt/nonexistent -- skip it */
-	syslog(LOG_WARNING, "unable to open/lock mailbox %s", argv[optind]);
+	syslog(LOG_WARNING, "unable to open %s: %m", expunge_fname);
 	if (doclose) mailbox_close(&mailbox);
 	return 0;
     }
@@ -567,31 +508,33 @@ int main(int argc, char *argv[])
 	unsigned long exists, uid;
 	const char *rec;
 	unsigned msgno;
-	unsigned long *uids = NULL;
-	unsigned nuids = 0;
 
 	map_refresh(expunge_fd, 1, &expunge_index_base,
 		    &expunge_index_len, sbuf.st_size, "expunge",
 		    mailbox.name);
 
+	if (sbuf.st_size < INDEX_HEADER_SIZE) {
+	    syslog(LOG_ERR, "ERROR: %s cyrus.expunge invalid",
+		    mailbox.name);
+	    goto done;
+	}
+
 	exists = ntohl(*((bit32 *)(expunge_index_base+OFFSET_EXISTS)));
+	if (sbuf.st_size < ( INDEX_HEADER_SIZE + (exists * INDEX_RECORD_SIZE))) {
+	    syslog(LOG_ERR,
+		    "ERROR: %s cyrus.expunge too short for %lu messages",
+		    mailbox.name, exists);
+	    goto done;
+	}
 
+    if (mode == MODE_COUNT) {
+	/* Translate mailboxname */
+	char buf[MAX_MAILBOX_PATH+1];
+	(*unex_namespace.mboxname_toexternal)(&unex_namespace, mailbox.name, NULL, buf);
+	printf("%s: %lu\n", buf, exists);
+    } else {
 	msgs = (struct msg *) xmalloc(exists * sizeof(struct msg));
 
-	/* Get UIDs of messages to restore */
-	if (mode == MODE_UID) {
-	    unsigned i;
-
-	    nuids = argc - ++optind;
-	    uids = (unsigned long *) xmalloc(nuids * sizeof(unsigned long));
-
-	    for (i = 0; i < nuids; i++)
-		uids[i] = strtoul(argv[optind+i], NULL, 10);
-
-	    /* Sort the UIDs so we can binary search */
-	    qsort(uids, nuids, sizeof(unsigned long), compare_uid);
-	}
-
 	/* Get UIDs of expunged messages */
 	for (msgno = 0; msgno < exists; msgno++) {
 	    /* Jump to index record for this message */
@@ -615,9 +558,11 @@ int main(int argc, char *argv[])
 					      sizeof(unsigned long),
 					      compare_uid) != NULL;
 		break;
+	    default:
+		/* mainly prevent compiler warning */
+		fprintf(stderr, "Unknown mode!\n");
 	    }
 	}
-	if (uids) free(uids);
 
 	/* Sort msgs by UID */
 	qsort(msgs, exists, sizeof(struct msg), compare_msg);
@@ -629,7 +574,7 @@ int main(int argc, char *argv[])
 		    mode == MODE_ALL ? "all " : "", mailbox.name);
 
 	    r = restore_expunged(&mailbox, msgs, exists, expunge_index_base,
-				 &numrestored, unsetdeleted);
+				 &numrestored);
 	    if (!r) {
 		printf("restored %u out of %lu expunged messages\n",
 			numrestored, exists);
@@ -638,16 +583,17 @@ int main(int argc, char *argv[])
 		       numrestored, exists, mailbox.name);
 	    }
 	}
-
-	map_free(&expunge_index_base, &expunge_index_len);
 	free(msgs);
+    }
+done:
+	map_free(&expunge_index_base, &expunge_index_len);
 
 	if (lock_unlock(expunge_fd))
 	    syslog(LOG_ERR,
 		   "IOERROR: unlocking expunge index of %s: %m", 
 		   mailbox.name);
     }
-    close(expunge_fd);
+    if (expunge_fd >= 0) close(expunge_fd);
 
     mailbox_unlock_pop(&mailbox);
     mailbox_unlock_index(&mailbox);
@@ -661,6 +607,144 @@ int main(int argc, char *argv[])
     quotadb_close();
     quotadb_done();
 
+    return r;
+}
+
+int main(int argc, char *argv[])
+{
+    extern char *optarg;
+    int opt, r = 0, recursive = 0;
+    char *alt_config = NULL;
+    char buf[MAX_MAILBOX_PATH+1];
+    int len, secs = 0;
+    unsigned long *uids = NULL;
+    unsigned nuids = 0;
+
+    if ((geteuid()) == 0 && (become_cyrus() != 0)) {
+	fatal("must run as the Cyrus user", EC_USAGE);
+    }
+
+    while ((opt = getopt(argc, argv, "C:laudt:vcr")) != EOF) {
+	switch (opt) {
+	case 'C': /* alt config file */
+	    alt_config = optarg;
+	    break;
+
+	case 'l':
+	    if (mode != MODE_UNKNOWN) usage();
+	    mode = MODE_LIST;
+	    break;
+	
+	case 'a':
+	    if (mode != MODE_UNKNOWN) usage();
+	    mode = MODE_ALL;
+	    break;
+	
+	case 't':
+	    if (mode != MODE_UNKNOWN) usage();
+
+	    mode = MODE_TIME;
+            secs = atoi(optarg);
+            len  = strlen(optarg);
+            
+            if ((secs > 0) && (len > 1)) {
+                switch (optarg[len-1]) {
+                case 'm':
+                    secs *= 60;
+                    break;
+                case 'h':
+                    secs *= (60*60);
+                    break;
+                case 'd':
+                    secs *= (24*60*60);
+                    break;
+                case 'w':
+                    secs *= (7*24*60*60);
+                    break;
+                }
+            }
+            time_since = time(NULL) - secs;
+	    break;
+
+	case 'u':
+	    if (mode != MODE_UNKNOWN) usage();
+	    mode = MODE_UID;
+	    break;
+
+	case 'd':
+	    unsetdeleted = 1;
+	    break;
+	
+	case 'v':
+	    verbose = 1;
+	    break;
+
+	case 'c':
+	    if (mode != MODE_UNKNOWN) usage();
+	    mode = MODE_COUNT;
+	    break;
+
+	case 'r':
+	    if (mode == MODE_UID) usage();
+	    recursive = 1;
+	    break;
+	
+	default:
+	    usage();
+	    break;
+	}
+    }
+
+    /* sanity check */
+    if (mode == MODE_UNKNOWN ||
+	(optind + (mode == MODE_UID ? 1 : 0)) >= argc) usage();
+
+    cyrus_init(alt_config, "unexpunge", 0);
+
+    mboxlist_init(0);
+    mboxlist_open(NULL);
+
+    quotadb_init(0);
+    quotadb_open(NULL);
+
+    sync_log_init();
+
+    /* Set namespace -- force standard (internal) */
+    if ((r = mboxname_init_namespace(&unex_namespace, 1)) != 0) {
+	syslog(LOG_ERR, error_message(r));
+	fatal(error_message(r), EC_CONFIG);
+    }
+
+    /* Translate mailboxname */
+    (*unex_namespace.mboxname_tointernal)(&unex_namespace, argv[optind],
+					  NULL, buf);
+
+    /* Get UIDs of messages to restore */
+    if (mode == MODE_UID) {
+        unsigned i;
+
+	nuids = argc - ++optind;
+	uids = (unsigned long *) xmalloc(nuids * sizeof(unsigned long));
+
+	for (i = 0; i < nuids; i++) {
+	    uids[i] = strtoul(argv[optind+i], NULL, 10);
+	}
+
+	/* Sort the UIDs so we can binary search */
+	qsort(uids, nuids, sizeof(unsigned long), compare_uid);
+    }
+
+    if ( !recursive ) {
+	r = unexpunge( buf, uids, nuids );
+    } else {
+	strlcat(buf, "*", sizeof(buf));
+	r = (*unex_namespace.mboxlist_findall)(&unex_namespace, buf, 1, 0, 0,
+					   do_unexpunge, NULL);
+/* XXX call unexpunge appropriately XXX */
+    }
+
+    if (uids) free(uids);
+
     mboxlist_close();
     mboxlist_done();
 
diff --git a/man/unexpunge.8 b/man/unexpunge.8
index e9b6043..3152af0 100644
--- a/man/unexpunge.8
+++ b/man/unexpunge.8
@@ -108,6 +108,14 @@ file unless specified otherwise by \fB-C\fR.
 .BI \-C " config-file"
 Read configuration options from \fIconfig-file\fR.
 .TP
+.B \-r
+Recursively process mailboxes. Mutually exclusive to -u.
+.TP
+.B \-c
+Print a count of the messages that are available to be unexpunged for
+a mailbox.  Nothing is printed for a mailbox that has no cyrus.expunge
+file.
+.TP
 .B \-l
 List the expunged messages in the specified mailbox which are available
 for restoration.
-- 
1.7.0.3

From 09cac5951e2e2a0bd8f8ebe3b12427e28fd3bf97 Mon Sep 17 00:00:00 2001
From: Brian Awood <bawood@xxxxxxxxx>
Date: Wed, 21 Oct 2009 16:37:01 -0400
Subject: [PATCH 2/3] Make the default output on unexpunge -l human readable

Unexpunge -l previously printed out the cache data
in the format where it is striped of caps and white
space.  This is a modification to the original patch
from Wes Craig for human readable output so it
applies to 2.3.15 and is the default output.

diff --git a/imap/Makefile.in b/imap/Makefile.in
index 0123719..ac3b11f 100644
--- a/imap/Makefile.in
+++ b/imap/Makefile.in
@@ -338,8 +338,8 @@ tls_prune: tls_prune.o tls.o $(CLIOBJS) libimap.a $(DEPLIBS)
 	$(CC) $(LDFLAGS) -o \
 	 $@ tls_prune.o tls.o $(CLIOBJS) libimap.a $(DEPLIBS) $(LIBS)
 
-unexpunge: unexpunge.o $(CLIOBJS) libimap.a $(DEPLIBS)
-	$(CC) $(LDFLAGS) -o $@ unexpunge.o $(CLIOBJS) \
+unexpunge: unexpunge.o index.o $(CLIOBJS) libimap.a $(DEPLIBS)
+	$(CC) $(LDFLAGS) -o $@ unexpunge.o index.o $(CLIOBJS) \
 	libimap.a $(DEPLIBS) $(LIBS)
 
 make_md5: make_md5.o libimap.a mutex_fake.o $(DEPLIBS)
diff --git a/imap/index.c b/imap/index.c
index 284eaf7..dec8425 100644
--- a/imap/index.c
+++ b/imap/index.c
@@ -167,7 +167,6 @@ static int _index_search(unsigned **msgno_list, struct mailbox *mailbox,
 			 struct searchargs *searchargs,
 			 modseq_t *highestmodseq);
 
-static void parse_cached_envelope(char *env, char *tokens[], int tokens_size);
 static char *find_msgid(char *str, char **rem);
 static char *get_localpart_addr(const char *header);
 static char *index_extract_subject(const char *subj, size_t len, int *is_refwd);
@@ -3737,7 +3736,7 @@ static MsgData *index_msgdata_load(unsigned *msgno_list, int n,
  * When inside a list (ncom > 0), we parse the individual tokens but don't
  * isolate them -- we return the entire list as a single token.
  */
-static void parse_cached_envelope(char *env, char *tokens[], int tokens_size)
+void parse_cached_envelope(char *env, char *tokens[], int tokens_size)
 {
     char *c;
     int i = 0, ncom = 0, len;
@@ -5166,7 +5165,7 @@ static char *parse_nstring(char **str)
     return val;
 }
 
-static void parse_env_address(char *str, struct address *addr)
+void parse_env_address(char *str, struct address *addr)
 {
     str++; /* skip ( */
     addr->name = parse_nstring(&str);
diff --git a/imap/index.h b/imap/index.h
index 0410b0f..0de66d9 100644
--- a/imap/index.h
+++ b/imap/index.h
@@ -180,6 +180,7 @@ extern unsigned long index_getsize(struct mailbox *mailbox, unsigned msgno);
 extern unsigned long index_getlines(struct mailbox *mailbox, unsigned msgno);
 extern int index_copy_remote(struct mailbox *mailbox, char *sequence, 
 			     int usinguid, struct protstream *pout);
+void parse_cached_envelope(char *env, char *tokens[], int tokens_size);
 
 void appendsequencelist(struct seq_set **l, char *sequence, int usinguid);
 void freesequencelist(struct seq_set *l);
diff --git a/imap/unexpunge.c b/imap/unexpunge.c
index e9dd19e..95824b5 100644
--- a/imap/unexpunge.c
+++ b/imap/unexpunge.c
@@ -64,6 +64,7 @@
 #include "global.h"
 #include "hash.h"
 #include "imap_err.h"
+#include "parseaddr.h"
 #include "index.h"
 #include "libcyr_cfg.h"
 #include "lock.h"
@@ -76,12 +77,31 @@
 #include "xstrlcat.h"
 #include "sync_log.h"
 
+/*
+ * Junk, to help index.c link.
+ */
+int imapd_exists;
+struct protstream *imapd_out = NULL;
+struct auth_state *imapd_authstate = NULL;
+char *imapd_userid = NULL;
+int imapd_condstore_client = 0;
+
+extern void parse_env_address(char *str, struct address *addr);
+
 /* global state */
 const int config_need_data = 0;
-
 int  unsetdeleted = 0;
 time_t time_since;
 
+enum {
+    MODE_UNKNOWN = -1,
+    MODE_LIST,
+    MODE_ALL,
+    MODE_TIME,
+    MODE_UID,
+    MODE_COUNT
+} mode = MODE_UNKNOWN;
+
 /* current namespace */
 static struct namespace unex_namespace;
 
@@ -101,15 +121,6 @@ void usage(void)
     exit(-1);
 }
 
-enum {
-    MODE_UNKNOWN = -1,
-    MODE_LIST,
-    MODE_ALL,
-    MODE_TIME,
-    MODE_UID,
-    MODE_COUNT
-} mode = MODE_UNKNOWN;
-
 struct msg {
     unsigned recno;
     unsigned long uid;
@@ -135,6 +146,10 @@ void list_expunged(struct mailbox *mailbox,
     unsigned long uid, size, cache_offset;
     time_t internaldate, sentdate, last_updated;
     cacherecord crec;
+    int cachelen;
+    char *env;
+    char *envtokens[NUMENVTOKENS];
+    struct address addr;
 
     for (msgno = 0; msgno < exists; msgno++) {
 	/* Jump to index record for this message */
@@ -154,16 +169,49 @@ void list_expunged(struct mailbox *mailbox,
 	printf("\tRecv: %s", ctime(&internaldate));
 	printf("\tExpg: %s", ctime(&last_updated));
 
-	if (!mailbox_cacherecord_offset(mailbox, cache_offset, &crec)) {
+	if ((cachelen = mailbox_cacherecord_offset(mailbox,
+			cache_offset, &crec)) == 0) {
 	    printf("\tERROR: cache record missing or corrupt, not printing cache details\n\n");
 	    continue;
 	}
+	env = xstrndup(crec[CACHE_ENVELOPE].s + 1, crec[CACHE_ENVELOPE].l - 2);
+	parse_cached_envelope(env, envtokens, VECTOR_SIZE(envtokens));
+
+	if ( envtokens[ ENV_FROM ] ) {
+	    parse_env_address( envtokens[ ENV_FROM ], &addr );
+	    printf("\tFrom: " );
+	    if ( addr.name ) printf("\"%s\" ", addr.name );
+	    printf( "<%s@%s>\n", addr.mailbox, addr.domain );
+	} else {
+	    printf("\tFrom: \n" );
+	}
+	if ( envtokens[ ENV_TO ] ) {
+	    parse_env_address( envtokens[ ENV_TO ], &addr );
+	    printf("\tTo  : " );
+	    if ( addr.name ) printf("\"%s\" ", addr.name );
+	    printf( "<%s@%s>\n", addr.mailbox, addr.domain );
+	} else {
+	    printf("\tTo  : \n" );
+	}
+	if ( envtokens[ ENV_CC ] ) {
+	    parse_env_address( envtokens[ ENV_CC ], &addr );
+	    printf("\tCc  : " );
+	    if ( addr.name ) printf("\"%s\" ", addr.name );
+	    printf( "<%s@%s>\n", addr.mailbox, addr.domain );
+	} else {
+	    printf("\tCc  : \n" );
+	}
+	if ( envtokens[ ENV_BCC ] ) {
+	    parse_env_address( envtokens[ ENV_BCC ], &addr );
+	    printf("\tBcc : " );
+	    if ( addr.name ) printf("\"%s\" ", addr.name );
+		printf( "<%s@%s>\n", addr.mailbox, addr.domain );
+	} else {
+	    printf("\tBcc : \n" );
+	}
+	printf("\tSubj: %s\n\n", envtokens[ENV_SUBJECT]);
+	free (env);
 
-	printf("\tFrom: %.*s\n", crec[CACHE_FROM].l, crec[CACHE_FROM].s);
-	printf("\tTo  : %.*s\n", crec[CACHE_TO].l, crec[CACHE_TO].s);
-	printf("\tCc  : %.*s\n", crec[CACHE_CC].l, crec[CACHE_CC].s);
-	printf("\tBcc : %.*s\n", crec[CACHE_BCC].l, crec[CACHE_BCC].s);
-	printf("\tSubj: %.*s\n\n", crec[CACHE_SUBJECT].l, crec[CACHE_SUBJECT].s);
     }
 }
 
@@ -688,7 +736,7 @@ int main(int argc, char *argv[])
 	    if (mode == MODE_UID) usage();
 	    recursive = 1;
 	    break;
-	
+
 	default:
 	    usage();
 	    break;
@@ -752,3 +800,11 @@ int main(int argc, char *argv[])
 
     exit(r);
 }
+
+/*
+ * Junk, to help index.c link.
+ */
+void printastring(const char *s __attribute__((unused)))
+{
+    fatal("printastring not implemented in unexpunge", EC_SOFTWARE);
+}
-- 
1.7.0.3

From 9c7ec4077a406c9e390bedb04ed04bebe1decf62 Mon Sep 17 00:00:00 2001
From: Brian Awood <bawood@xxxxxxxxx>
Date: Mon, 2 Nov 2009 12:53:22 -0500
Subject: [PATCH 3/3] Add "machine readable" -m option to unexpunge command

This patch adds the -m flag to be combined with -l
option of the unexpunge command so the list is more
easily parsed with perl or other similar tools.
Output for each message is on one line and fields
separated with % since that char should only be
possible in the Subject field.

diff --git a/imap/unexpunge.c b/imap/unexpunge.c
index 95824b5..8494930 100644
--- a/imap/unexpunge.c
+++ b/imap/unexpunge.c
@@ -92,6 +92,7 @@ extern void parse_env_address(char *str, struct address *addr);
 const int config_need_data = 0;
 int  unsetdeleted = 0;
 time_t time_since;
+char sepchar = '\n';
 
 enum {
     MODE_UNKNOWN = -1,
@@ -137,6 +138,23 @@ int compare_msg(const void *a, const void *b)
     return ((struct msg *) a)->uid - ((struct msg *) b)->uid;
 }
 
+int escape_string(char *inbuf, int len, char *outbuf, const char c)
+{
+    int x;
+    char *ret = outbuf;
+
+    for ( x = 0; x < len; x++ ) {
+	if ( *inbuf != c ) {
+	    *outbuf++ = *inbuf++;
+	} else {
+	    *outbuf++ = '\\';
+	    *outbuf++ = *inbuf++;
+	}
+    }
+    *outbuf = '\0';
+    return strlen(ret);
+}
+
 void list_expunged(struct mailbox *mailbox,
 		   struct msg *msgs, unsigned long exists,
 		   const char *expunge_index_base)
@@ -146,10 +164,13 @@ void list_expunged(struct mailbox *mailbox,
     unsigned long uid, size, cache_offset;
     time_t internaldate, sentdate, last_updated;
     cacherecord crec;
-    int cachelen;
+    int cachelen, datelen;
     char *env;
     char *envtokens[NUMENVTOKENS];
     struct address addr;
+    char date[200];
+    char *format = "%c";
+    char *escstr;
 
     for (msgno = 0; msgno < exists; msgno++) {
 	/* Jump to index record for this message */
@@ -162,12 +183,22 @@ void list_expunged(struct mailbox *mailbox,
 	size = ntohl(*((bit32 *)(rec+OFFSET_SIZE)));
 	cache_offset = ntohl(*((bit32 *)(rec+OFFSET_CACHE_OFFSET)));
 	last_updated = ntohl(*((bit32 *)(rec+OFFSET_LAST_UPDATED)));
-
-	printf("UID: %lu\n", uid);
-	printf("\tSize: %lu\n", size);
-	printf("\tSent: %s", ctime(&sentdate));
-	printf("\tRecv: %s", ctime(&internaldate));
-	printf("\tExpg: %s", ctime(&last_updated));
+	datelen = strlen(ctime(&internaldate));
+
+	if (sepchar != '\n') printf("%s%c", mailbox->name, sepchar);
+	if (sepchar == '\n') printf("UID: ");
+	printf("%lu%c", uid, sepchar);
+	if (sepchar == '\n') printf("\tSize: ");
+	printf("%lu%c", size, sepchar);
+	if (sepchar == '\n') printf("\tSent: ");
+	strftime( date, sizeof(date), format, localtime(&sentdate));
+	printf("%s%c", date, sepchar);
+	if (sepchar == '\n') printf("\tRecv: ");
+	strftime( date, sizeof(date), format, localtime(&internaldate));
+	printf("%s%c", date, sepchar);
+	if (sepchar == '\n') printf("\tExpg: ");
+	strftime( date, sizeof(date), format, localtime(&last_updated));
+	printf("%s%c", date, sepchar);
 
 	if ((cachelen = mailbox_cacherecord_offset(mailbox,
 			cache_offset, &crec)) == 0) {
@@ -179,39 +210,57 @@ void list_expunged(struct mailbox *mailbox,
 
 	if ( envtokens[ ENV_FROM ] ) {
 	    parse_env_address( envtokens[ ENV_FROM ], &addr );
-	    printf("\tFrom: " );
+	    if (sepchar == '\n') printf("\tFrom: " );
 	    if ( addr.name ) printf("\"%s\" ", addr.name );
-	    printf( "<%s@%s>\n", addr.mailbox, addr.domain );
+	    printf( "<%s@%s>%c", addr.mailbox, addr.domain, sepchar );
 	} else {
-	    printf("\tFrom: \n" );
+	    if (sepchar == '\n') printf("\tFrom: \n" );
+	    else printf( "%c", sepchar );
 	}
 	if ( envtokens[ ENV_TO ] ) {
 	    parse_env_address( envtokens[ ENV_TO ], &addr );
-	    printf("\tTo  : " );
+	    if (sepchar == '\n') printf("\tTo  : " );
 	    if ( addr.name ) printf("\"%s\" ", addr.name );
-	    printf( "<%s@%s>\n", addr.mailbox, addr.domain );
+	    printf( "<%s@%s>%c", addr.mailbox, addr.domain, sepchar );
 	} else {
-	    printf("\tTo  : \n" );
+	    if (sepchar == '\n') printf("\tTo  : \n" );
+	    else printf( "%c", sepchar );
 	}
 	if ( envtokens[ ENV_CC ] ) {
 	    parse_env_address( envtokens[ ENV_CC ], &addr );
-	    printf("\tCc  : " );
+	    if (sepchar == '\n') printf("\tCc  : " );
 	    if ( addr.name ) printf("\"%s\" ", addr.name );
-	    printf( "<%s@%s>\n", addr.mailbox, addr.domain );
+	    printf( "<%s@%s>%c", addr.mailbox, addr.domain, sepchar );
 	} else {
-	    printf("\tCc  : \n" );
+	    if (sepchar == '\n') printf("\tCc  : \n" );
+	    else printf( "%c", sepchar );
 	}
 	if ( envtokens[ ENV_BCC ] ) {
 	    parse_env_address( envtokens[ ENV_BCC ], &addr );
-	    printf("\tBcc : " );
+	    if (sepchar == '\n') printf("\tBcc : " );
 	    if ( addr.name ) printf("\"%s\" ", addr.name );
-		printf( "<%s@%s>\n", addr.mailbox, addr.domain );
+		printf( "<%s@%s>%c", addr.mailbox, addr.domain, sepchar );
+	} else {
+	    if (sepchar == '\n') printf("\tBcc : \n" );
+	    else printf( "%c", sepchar );
+	}
+	if ( envtokens[ ENV_SUBJECT ] ) {
+	    if ( sepchar != '\n' ) {
+		cachelen = strlen(envtokens[ENV_SUBJECT]);
+		escstr = xmalloc( 2 * cachelen + 1 );
+		if ( escape_string( envtokens[ENV_SUBJECT], cachelen,
+				escstr, '%' ) != 0 ) {
+		    printf("%s", escstr);
+		}
+		free (escstr);
+	    } else {
+		printf("\tSubj: %s\n", envtokens[ ENV_SUBJECT ]);
+	    }
 	} else {
-	    printf("\tBcc : \n" );
+	    if (sepchar == '\n') printf("\tSubj:\n");
 	}
-	printf("\tSubj: %s\n\n", envtokens[ENV_SUBJECT]);
+	printf("\n");
 	free (env);
-
     }
 }
 
@@ -514,9 +563,13 @@ unexpunge(char *mboxname, unsigned long *uids, unsigned nuids)
 
     if (r) {
 	/* mailbox corrupt/nonexistent -- skip it */
-	syslog(LOG_WARNING, "unable to open/lock mailbox %s", mboxname);
-	if (doclose) mailbox_close(&mailbox);
-	return 0;
+	if (doclose) {
+	    syslog(LOG_WARNING, "unable to open/lock mailbox index %s", mboxname);
+	    mailbox_close(&mailbox);
+	} else {
+	    syslog(LOG_WARNING, "unable to open mailbox header %s", mboxname);
+	}
+	return r;
     }
 
     /* Open expunge index */
@@ -540,7 +593,7 @@ unexpunge(char *mboxname, unsigned long *uids, unsigned nuids)
 			     mailbox.name, NULL, buf);
 	    printf("%s: 0\n", buf);
 	}
-	/* mailbox corrupt/nonexistent -- skip it */
+	/* mailbox expunge file nonexistent -- skip it */
 	syslog(LOG_WARNING, "unable to open %s: %m", expunge_fname);
 	if (doclose) mailbox_close(&mailbox);
 	return 0;
@@ -564,23 +617,25 @@ unexpunge(char *mboxname, unsigned long *uids, unsigned nuids)
 	if (sbuf.st_size < INDEX_HEADER_SIZE) {
 	    syslog(LOG_ERR, "ERROR: %s cyrus.expunge invalid",
 		    mailbox.name);
-	    goto done;
+	    r = IMAP_IOERROR;
 	}
 
-	exists = ntohl(*((bit32 *)(expunge_index_base+OFFSET_EXISTS)));
-	if (sbuf.st_size < ( INDEX_HEADER_SIZE + (exists * INDEX_RECORD_SIZE))) {
-	    syslog(LOG_ERR,
-		    "ERROR: %s cyrus.expunge too short for %lu messages",
+	if (!r) exists = ntohl(*((bit32 *)(expunge_index_base+OFFSET_EXISTS)));
+	if (!r && sbuf.st_size < ( INDEX_HEADER_SIZE + (exists * INDEX_RECORD_SIZE))) {
+	    syslog(LOG_ERR, "ERROR: %s cyrus.expunge too short for %lu messages",
 		    mailbox.name, exists);
-	    goto done;
+	    r = IMAP_IOERROR;
 	}
 
     if (mode == MODE_COUNT) {
 	/* Translate mailboxname */
 	char buf[MAX_MAILBOX_PATH+1];
 	(*unex_namespace.mboxname_toexternal)(&unex_namespace, mailbox.name, NULL, buf);
-	printf("%s: %lu\n", buf, exists);
-    } else {
+	/* in count mode, if there's a problem, print an error and continue */
+	if (!r) printf("%s: %lu\n", buf, exists);
+	else printf("%s: -1\n", buf);
+	r = 0;
+    } else if (!r) {
 	msgs = (struct msg *) xmalloc(exists * sizeof(struct msg));
 
 	/* Get UIDs of expunged messages */
@@ -672,7 +727,7 @@ int main(int argc, char *argv[])
 	fatal("must run as the Cyrus user", EC_USAGE);
     }
 
-    while ((opt = getopt(argc, argv, "C:laudt:vcr")) != EOF) {
+    while ((opt = getopt(argc, argv, "C:laudt:vcrm")) != EOF) {
 	switch (opt) {
 	case 'C': /* alt config file */
 	    alt_config = optarg;
@@ -737,6 +792,11 @@ int main(int argc, char *argv[])
 	    recursive = 1;
 	    break;
 
+	case 'm':
+	    if (mode != MODE_LIST) usage();
+	    sepchar = '%';
+	    break;
+	
 	default:
 	    usage();
 	    break;
@@ -788,7 +848,6 @@ int main(int argc, char *argv[])
 	strlcat(buf, "*", sizeof(buf));
 	r = (*unex_namespace.mboxlist_findall)(&unex_namespace, buf, 1, 0, 0,
 					   do_unexpunge, NULL);
-/* XXX call unexpunge appropriately XXX */
     }
 
     if (uids) free(uids);
diff --git a/man/unexpunge.8 b/man/unexpunge.8
index 3152af0..955f67b 100644
--- a/man/unexpunge.8
+++ b/man/unexpunge.8
@@ -50,6 +50,9 @@ unexpunge \- restore expunged (but yet unremoved) messages
 .I config-file
 ]
 .B \-l
+[
+.B \-m
+]
 .I mailbox
 .br
 .B unexpunge
@@ -120,6 +123,12 @@ file.
 List the expunged messages in the specified mailbox which are available
 for restoration.
 .TP
+.B \-m
+When listing with -l, print data fields for each message on one
+line separated by '%'.  Occurances of '%' in a Subject field are escaped
+with a '\\'.  Data is in the same order as plain -l, with the addition of
+the mailbox name in the first field, however field titles are not included.
+.TP
 .BI \-t " time-interval"
 Unexpunge messages which were expunged in the last time-interval seconds.
 (modifiers: m: minutes, h: hours, d: days, w: weeks).
-- 
1.7.0.3

----
Cyrus Home Page: http://cyrusimap.web.cmu.edu/
Cyrus Wiki/FAQ: http://cyrusimap.web.cmu.edu/twiki
List Archives/Info: http://asg.web.cmu.edu/cyrus/mailing-list.html

[Index of Archives]     [Cyrus SASL]     [Squirrel Mail]     [Asterisk PBX]     [Video For Linux]     [Photo]     [Yosemite News]     [gtk]     [KDE]     [Gimp on Windows]     [Steve's Art]

  Powered by Linux