Hi,

It would be useful for this destructive command to have a dry run
mode displaying what would be deleted.  The patch attached adds
a -n option for this purpose.  The info displayed is similar to
unexpunge output but without the cached info (not sure if it was
useful and safe to call cache here).  Let me know what you think.

https://lists.andrew.cmu.edu/pipermail/info-cyrus/2014-August/037528.html

-- 
Valentin
diff --git a/docsrc/imap/admin/systemcommands/ipurge.rst b/docsrc/imap/admin/systemcommands/ipurge.rst
index 6ac5fde..84ccf80 100644
--- a/docsrc/imap/admin/systemcommands/ipurge.rst
+++ b/docsrc/imap/admin/systemcommands/ipurge.rst
@@ -13,7 +13,7 @@ Synopsis
 
 .. parsed-literal::
 
-    **ipurge** [ **-f** ] [ **-C** *config-file* ] [ **-x** ] [ **-X** ] [ **-i** ] [ **-s** ] [ **-o** ]
+    **ipurge** [ **-f** ] [ **-C** *config-file* ] [ **-x** ] [ **-X** ] [ **-i** ] [ **-s** ] [ **-o** ] [ **-n** ]
             [ **-d** *days* | **-b** *bytes* | **-k** *Kbytes* | **-m** *Mbytes* ]
             [ *mailbox-pattern*... ]
 
@@ -85,6 +85,10 @@ Options
 
     Only purge messages that have the \\Deleted flag set.
 
+.. option:: -n
+
+    Only print messages that would be deleted (dry run).
+
 Examples
 ========
 
diff --git a/imap/ipurge.c b/imap/ipurge.c
index 07b6382..bbddb8d 100644
--- a/imap/ipurge.c
+++ b/imap/ipurge.c
@@ -91,6 +91,7 @@ typedef struct mbox_stats_s {
 
 } mbox_stats_t;
 
+static int dryrun = 0;
 static int verbose = 1;
 static int forceall = 0;
 
@@ -99,6 +100,7 @@ static unsigned purge_check(struct mailbox *mailbox,
                             const struct index_record *record,
                             void *rock);
 static int usage(const char *name);
+static void print_record(const struct index_record *record);
 static void print_stats(mbox_stats_t *stats);
 
 int main (int argc, char *argv[]) {
@@ -109,7 +111,7 @@ int main (int argc, char *argv[]) {
       fatal("must run as the Cyrus user", EC_USAGE);
   }
 
-  while ((option = getopt(argc, argv, "C:hxd:b:k:m:fsXio")) != EOF) {
+  while ((option = getopt(argc, argv, "C:hxd:b:k:m:fsXion")) != EOF) {
     switch (option) {
     case 'C': /* alt config file */
       alt_config = optarg;
@@ -138,6 +140,9 @@ int main (int argc, char *argv[]) {
       }
       size = atoi(optarg) * 1048576; /* 1024 * 1024 */
     } break;
+    case 'n' : {
+      dryrun = 1;
+    } break;
     case 'x' : {
       exact = 1;
     } break;
@@ -207,7 +212,7 @@ int main (int argc, char *argv[]) {
 
 static int usage(const char *name)
 {
-  printf("usage: %s [-f] [-s] [-C <alt_config>] [-x] [-X] [-i] [-o] {-d days | -b bytes|-k Kbytes|-m Mbytes}\n\t[mboxpattern1 ... [mboxpatternN]]\n", name);
+  printf("usage: %s [-f] [-s] [-C <alt_config>] [-x] [-X] [-i] [-o] [-n] {-d days | -b bytes|-k Kbytes|-m Mbytes}\n\t[mboxpattern1 ... [mboxpatternN]]\n", name);
   printf("\tthere are no defaults and at least one of -d, -b, -k, -m\n\tmust be specified\n");
   printf("\tif no mboxpattern is given %s works on all mailboxes\n", name);
   printf("\t -x specifies an exact match for days or size\n");
@@ -216,6 +221,7 @@ static int usage(const char *name)
   printf("\t -X use delivery time instead of date header for date matches.\n");
   printf("\t -i invert match logic: -x means not equal, date is for newer, size is for smaller.\n");
   printf("\t -o only purge messages that are deleted.\n");
+  printf("\t -n only print messages that would be deleted (dry run).\n");
   exit(0);
 }
 
@@ -289,11 +295,11 @@ static unsigned purge_check(struct mailbox *mailbox __attribute__((unused)),
       if (((my_time - (time_t) senttime)/86400) == (days/86400)) {
           if (invertmatch) return 0;
           deleteit(record->size, stats);
-          return 1;
+          return dryrun ? print_record(record), 0 : 1;
       } else {
           if (!invertmatch) return 0;
           deleteit(record->size, stats);
-          return 1;
+          return dryrun ? print_record(record), 0 : 1;
       }
     }
     if (size >= 0) {
@@ -301,11 +307,11 @@ static unsigned purge_check(struct mailbox *mailbox __attribute__((unused)),
         if (record->size == (unsigned)size) {
           if (invertmatch) return 0;
           deleteit(record->size, stats);
-          return 1;
+          return dryrun ? print_record(record), 0 : 1;
       } else {
           if (!invertmatch) return 0;
           deleteit(record->size, stats);
-          return 1;
+          return dryrun ? print_record(record), 0 : 1;
       }
     }
     return 0;
@@ -314,28 +320,37 @@ static unsigned purge_check(struct mailbox *mailbox __attribute__((unused)),
       /*    printf("comparing %ld :: %ld\n", my_time, the_record->sentdate); */
       if (!invertmatch && ((my_time - (time_t) senttime) > days)) {
           deleteit(record->size, stats);
-          return 1;
+          return dryrun ? print_record(record), 0 : 1;
       }
       if (invertmatch && ((my_time - (time_t) senttime) < days)) {
           deleteit(record->size, stats);
-          return 1;
+          return dryrun ? print_record(record), 0 : 1;
       }
     }
     if (size >= 0) {
       /* check size */
         if (!invertmatch && ((int) record->size > size)) {
           deleteit(record->size, stats);
-          return 1;
+          return dryrun ? print_record(record), 0 : 1;
       }
         if (invertmatch && ((int) record->size < size)) {
           deleteit(record->size, stats);
-          return 1;
+          return dryrun ? print_record(record), 0 : 1;
       }
     }
     return 0;
   }
 }
 
+static void print_record(const struct index_record *record)
+{
+    printf("UID: %u\n", record->uid);
+    printf("\tSize: %u\n", record->size);
+    printf("\tSent: %s", ctime(&record->sentdate));
+    printf("\tRecv: %s", ctime(&record->internaldate));
+    printf("\n");
+}
+
 static void print_stats(mbox_stats_t *stats)
 {
     printf("total messages    \t\t %d\n",stats->total);

Reply via email to