diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2020-11-17 10:20:52 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2020-11-17 13:24:51 +0200 |
commit | 823e48a02f8952c0550bbec6bc2e3849c41cf011 (patch) | |
tree | 623ecb692a8415a5a4ebf260858a0716176ac261 | |
parent | 6e0fb32ec8557a443e7ea110bc923dfd7b79ae03 (diff) | |
download | mailutils-823e48a02f8952c0550bbec6bc2e3849c41cf011.tar.gz mailutils-823e48a02f8952c0550bbec6bc2e3849c41cf011.tar.bz2 |
New mailutils subcommand: maildir_fixup
The subcommand converts names in maildirs created by earlier versions of
mailutils to the new format.
* NEWS: Update.
* doc/texinfo/programs/mailutils.texi: Document maildir_fixup
* mu/libexec/Makefile.am: Build maildir_fixup subcommand.
* mu/libexec/maildir_fixup.c: New file.
* mu/libexec/mu.h (common_options): New extern.
* include/mailutils/cli.h (MU_CLI_OPTION_CONF_PROGNAME): New constant.
* libmailutils/cli/simple.c (MU_CLI_OPTION_PROG_NAME)
(MU_CLI_OPTION_PROG_NAME)
(MU_CLI_OPTION_PROG_DOC)
(MU_CLI_OPTION_BUG_ADDRESS)
(MU_CLI_OPTION_PACKAGE_URL)
(MU_CLI_OPTION_EXTRA_INFO)
(MU_CLI_OPTION_HELP_HOOK)
(MU_CLI_OPTION_VERSION_HOOK)
(MU_CLI_OPTION_PROG_DOC_HOOK)
(MU_CLI_OPTION_NEGATION)
(MU_CLI_OPTION_SPECIAL_ARGS): Allow for NULL argument.
(MU_CLI_OPTION_CONF_PROGNAME): New code
-rw-r--r-- | NEWS | 12 | ||||
-rw-r--r-- | doc/texinfo/programs/mailutils.texi | 57 | ||||
-rw-r--r-- | include/mailutils/cli.h | 27 | ||||
-rw-r--r-- | lib/manlock.c | 4 | ||||
-rw-r--r-- | libmailutils/cli/simple.c | 38 | ||||
-rw-r--r-- | mu/libexec/Makefile.am | 10 | ||||
-rw-r--r-- | mu/libexec/maildir_fixup.c | 202 | ||||
-rw-r--r-- | mu/libexec/mu.h | 1 |
8 files changed, 333 insertions, 18 deletions
@@ -1,4 +1,4 @@ -GNU mailutils NEWS -- history of user-visible changes. 2020-11-10 +GNU mailutils NEWS -- history of user-visible changes. 2020-11-17 See the end of file for copying conditions. Please send mailutils bug reports to <bug-mailutils@gnu.org>. @@ -18,6 +18,16 @@ incorrect flag mapping and fixes it if so. If the mailbox is opened in read-write mode, the changes are immediately propagated to the disk storage. +An auxiliary tool `maildir_fixup' is provided to fix up all maildirs +in given folders recursively. The tool can be used if you operate a +site with a large number of mailboxes in maildir format and wish to +convert all of them at once. It is implemented as a subcommand of the +`mailutils' utility. Example usage: + + mailutils maildir_fixup /var/spool/maildirs + +* Fix persistency of UIDs for all mailbox formats + * Fix message ordering in maildir. * Fix parsing of IMAP ranges diff --git a/doc/texinfo/programs/mailutils.texi b/doc/texinfo/programs/mailutils.texi index d4769b85e..176d897df 100644 --- a/doc/texinfo/programs/mailutils.texi +++ b/doc/texinfo/programs/mailutils.texi @@ -26,6 +26,7 @@ with Mailutils. * mailutils imap:: IMAP4 client shell. * mailutils send:: Send a message. * mailutils smtp:: Run a SMTP session. +* mailutils maildir_fixup:: Fix-up maildirs created by versions prior to 3.10.90 @end menu @node mailutils invocation syntax @@ -1450,3 +1451,59 @@ message: send, edit, or quit (discard) it. Internal commands are the same as in @code{pop} shell: @xref{mailutils pop, Internal commands}. +@node mailutils maildir_fixup +@subsection mailutils maildir_fixup + +This command fixes attributes and UID assignments in @samp{maildir} +mailboxes created by mailutils versions prior to 3.10.90. + +Attribute flags used in @samp{maildir} mailboxes by these versions of +mailutils were a bit different from those described in the original +description of the @samp{maildir} format@footnote{@uref{http://cr.yp.to/proto/maildir.html}} +and those used by another implementations. The discrepancy has been +reported in the Mailutils bug tracker@footnote{@uref{http://savannah.gnu.org/bugs/?56428}} +and was fixed in version 3.10.90. Along with this fix, measures has +been taken to ensure persistence of UID assignments between different +sessions. Starting from version 3.10.90, whenever @command{mailutils} +library opens a maildir mailbox, it determines the version that +created it. If the mailbox is writable and the library determines +that the mailbox is affected by the two problems described above, it +fixes the mailbox on the fly. This process is completely transparent +to the user. + +If you operate a site with a large number of mailboxes in +@samp{maildir} formats, you may choose to fix up all of them at once. +That's what the @command{maildir_fixup} command is for. It takes one +or more directory names as its arguments and recursively scans these +directories in search for @samp{maildir} mailboxes. Each mailbox +found is analyzed and a fix-up is performed, if necessary. If a +mailbox is already in the new format, it remains untouched. + +The following options modify the program's behavior: + +@table @option +@item -v +@itemx --verbose +List each maildir name before processing it. + +@item -n +@itemx --dry-run +Don't touch maildirs, just print their names, +@end table + +The @command{maildir_fixup} tool reads main mailutils configuration +file by default. It looks for program-specific settings in the +section @samp{program maildir_fixup}. If the @code{include} statement +is present that has a directory name as its argument, the file +@file{maildir_fixup} is looked up in that directory and parsed, if +present. + +The program uses the following configuration statements: + +@multitable @columnfractions 0.3 0.6 +@headitem Statement @tab Reference +@item debug @tab @xref{debug statement}. +@item locking @tab @xref{locking statement}. +@item mandatory-locking @tab @FIXME-xref{mandatory-locking statement}. +@end multitable + diff --git a/include/mailutils/cli.h b/include/mailutils/cli.h index 9d2afc806..18ce2c341 100644 --- a/include/mailutils/cli.h +++ b/include/mailutils/cli.h @@ -89,12 +89,14 @@ enum /* Argument: struct mu_cfg_param * * Description: Supplies configuration definitions. + * NULL argument is a no-op. * Ref: cfg in struct mu_cli_setup */ MU_CLI_OPTION_CONFIG, /* Argument: char ** * Description: NULL-terminated array of capability strings. + * NULL argument is a no-op. * Ref: capa argument to mu_cli and mu_cli_ext. */ MU_CLI_OPTION_CAPABILITIES, @@ -167,6 +169,7 @@ enum /* Argument: char const * * Description: Override the name of the program to use in error * messages. + * NULL argument is a no-op. * Ref: MU_PARSEOPT_PROG_NAME flag in opt.h */ MU_CLI_OPTION_PROG_NAME, @@ -174,6 +177,7 @@ enum /* Argument: char const * * Description: Documentation string for the program. It will be * displayed on the line following the usage summary. + * NULL argument is a no-op. * Ref: MU_PARSEOPT_PROG_DOC */ MU_CLI_OPTION_PROG_DOC, @@ -186,6 +190,7 @@ enum * Usage: foo A B * * Multiple instances are allowed. + * NULL argument is a no-op. * Ref: MU_PARSEOPT_PROG_ARGS flag in opt.h */ MU_CLI_OPTION_PROG_ARGS, @@ -198,12 +203,14 @@ enum /* Argument: char const * * Description: Sets the PACKAGE_NAME. + * NULL argument is a no-op. * Ref: MU_PARSEOPT_PACKAGE_NAME in opt.h */ MU_CLI_OPTION_PACKAGE_NAME, /* Argument: char const * * Description: URL of the package. + * NULL argument is a no-op. * Ref: MU_PARSEOPT_PACKAGE_URL in opt.h */ MU_CLI_OPTION_PACKAGE_URL, @@ -211,6 +218,7 @@ enum /* Argument: char const * * Description: Extra help information. This will be displayed after * the option list. + * NULL argument is a no-op. * Ref: MU_PARSEOPT_EXTRA_INFO in opt.h */ MU_CLI_OPTION_EXTRA_INFO, @@ -218,6 +226,7 @@ enum /* Argument: void (*) (struct mu_parseopt *, mu_stream_t) * Description: Pointer to a function to be called as a part of the * --help option handling, after outputting the option list. + * NULL argument is a no-op. * Ref: po_help_hook member of struct mu_parseopt and the * MU_PARSEOPT_HELP_HOOK flag in opt.h */ @@ -243,7 +252,9 @@ enum * --help option handling. This function will be called after printing * initial program description (see MU_CLI_OPTION_PROG_DOC) and before * printing the option summary. - * Ref: + * NULL argument is a no-op. + * Ref: po_prog_doc_hook member of struct mu_parseopt and the + * MU_PARSEOPT_PROG_DOC_HOOK in opt.h */ MU_CLI_OPTION_PROG_DOC_HOOK, @@ -256,6 +267,7 @@ enum /* Argument: char const * * Description: Prefix that negates the value of a boolean option. + * NULL argument is a no-op. * Ref: po_negation member of struct mu_parseopt and the * MU_PARSEOPT_NEGATION flag in opt.h */ @@ -264,6 +276,7 @@ enum /* Argument: char const * * Description: Descriptive names of special arguments. If given, this * will be printed in short usage summary after the regular options. + * NULL argument is a no-op. * Ref: The po_special_args member of struct mu_parseopt and the * MU_PARSEOPT_SPECIAL_ARGS flag in opt.h */ @@ -289,7 +302,17 @@ enum * from the command line. * Ref: MU_CFHINT_NO_CONFIG_OVERRIDE flag in cfg.h */ - MU_CLI_OPTION_CONF_NO_OVERRIDE + MU_CLI_OPTION_CONF_NO_OVERRIDE, + + /* Argument: char const * + * Description: Identifier of the per-program section in the configuration + * file or name of the per-program configuration file (used when + * processing the "include" statement with a directory name as its + * argument). If NULL or not set, program name is used. See also + * MU_CLI_OPTION_PROG_NAME. + * Ref: MU_CFHINT_PROGRAM flag in cfg.h + */ + MU_CLI_OPTION_CONF_PROGNAME, }; void mu_cli_simple (int argc, char **argv, ...); diff --git a/lib/manlock.c b/lib/manlock.c index a093a1926..f50e16343 100644 --- a/lib/manlock.c +++ b/lib/manlock.c @@ -237,10 +237,6 @@ manlock_cfg_init () mu_create_canned_section ("mandatory-locking", §ion); mu_cfg_section_add_params (section, manlock_param); } - - - - int manlock_lock (mu_mailbox_t mbox) diff --git a/libmailutils/cli/simple.c b/libmailutils/cli/simple.c index 0daef170d..20adcc11b 100644 --- a/libmailutils/cli/simple.c +++ b/libmailutils/cli/simple.c @@ -117,13 +117,15 @@ mu_cli_simple (int argc, char **argv, ...) break; case MU_CLI_OPTION_PROG_NAME: - pohint.po_flags |= MU_PARSEOPT_PROG_NAME; pohint.po_prog_name = va_arg (ap, char *); + if (pohint.po_prog_name) + pohint.po_flags |= MU_PARSEOPT_PROG_NAME; break; case MU_CLI_OPTION_PROG_DOC: - pohint.po_flags |= MU_PARSEOPT_PROG_DOC; pohint.po_prog_doc = va_arg (ap, char *); + if (pohint.po_prog_doc) + pohint.po_flags |= MU_PARSEOPT_PROG_DOC; break; case MU_CLI_OPTION_PROG_ARGS: @@ -137,51 +139,60 @@ mu_cli_simple (int argc, char **argv, ...) break; case MU_CLI_OPTION_BUG_ADDRESS: - pohint.po_flags |= MU_PARSEOPT_BUG_ADDRESS; pohint.po_bug_address = va_arg (ap, char *); + if (pohint.po_bug_address) + pohint.po_flags |= MU_PARSEOPT_BUG_ADDRESS; break; case MU_CLI_OPTION_PACKAGE_NAME: - pohint.po_flags |= MU_PARSEOPT_PACKAGE_NAME; pohint.po_package_name = va_arg (ap, char *); + if (pohint.po_package_name) + pohint.po_flags |= MU_PARSEOPT_PACKAGE_NAME; break; case MU_CLI_OPTION_PACKAGE_URL: - pohint.po_flags |= MU_PARSEOPT_PACKAGE_URL; pohint.po_package_url = va_arg (ap, char *); + if (pohint.po_package_url) + pohint.po_flags |= MU_PARSEOPT_PACKAGE_URL; break; case MU_CLI_OPTION_EXTRA_INFO: - pohint.po_flags |= MU_PARSEOPT_EXTRA_INFO; pohint.po_extra_info = va_arg (ap, char *); + if (pohint.po_extra_info) + pohint.po_flags |= MU_PARSEOPT_EXTRA_INFO; break; case MU_CLI_OPTION_HELP_HOOK: - pohint.po_flags |= MU_PARSEOPT_HELP_HOOK; pohint.po_help_hook = va_arg (ap, void (*) (struct mu_parseopt *, mu_stream_t)); + if (pohint.po_help_hook) + pohint.po_flags |= MU_PARSEOPT_HELP_HOOK; break; case MU_CLI_OPTION_VERSION_HOOK: - pohint.po_flags |= MU_PARSEOPT_VERSION_HOOK; pohint.po_version_hook = va_arg (ap, void (*) (struct mu_parseopt *, mu_stream_t)); + if (pohint.po_version_hook) + pohint.po_flags |= MU_PARSEOPT_VERSION_HOOK; break; case MU_CLI_OPTION_PROG_DOC_HOOK: - pohint.po_flags |= MU_PARSEOPT_PROG_DOC_HOOK; pohint.po_prog_doc_hook = va_arg (ap, void (*) (struct mu_parseopt *, mu_stream_t)); + if (pohint.po_prog_doc_hook) + pohint.po_flags |= MU_PARSEOPT_PROG_DOC_HOOK; break; case MU_CLI_OPTION_NEGATION: - pohint.po_flags |= MU_PARSEOPT_NEGATION; pohint.po_negation = va_arg (ap, char *); + if (pohint.po_negation) + pohint.po_flags |= MU_PARSEOPT_NEGATION; break; case MU_CLI_OPTION_SPECIAL_ARGS: - pohint.po_flags |= MU_PARSEOPT_SPECIAL_ARGS; pohint.po_special_args = va_arg (ap, char *); + if (pohint.po_special_args) + pohint.po_flags |= MU_PARSEOPT_SPECIAL_ARGS; break; case MU_CLI_OPTION_CONF_SITE_FILE: @@ -198,6 +209,11 @@ mu_cli_simple (int argc, char **argv, ...) cfhint.flags |= MU_CFHINT_NO_CONFIG_OVERRIDE; break; + case MU_CLI_OPTION_CONF_PROGNAME: + if ((cfhint.program = va_arg (ap, char *)) != NULL) + cfhint.flags |= MU_CFHINT_PROGRAM; + break; + default: mu_diag_output (MU_DIAG_CRIT, _("%s:%d: INTERNAL ERROR: unrecognized mu_cli_simple option"), diff --git a/mu/libexec/Makefile.am b/mu/libexec/Makefile.am index e94ebccc9..03bbb318d 100644 --- a/mu/libexec/Makefile.am +++ b/mu/libexec/Makefile.am @@ -83,6 +83,16 @@ if MU_COND_SUPPORT_IMAP $(MUTOOL_LIBRARIES_TAIL) endif +if MU_COND_SUPPORT_MAILDIR + pkglibexec_PROGRAMS += mailutils-maildir_fixup + mailutils_maildir_fixup_SOURCES = maildir_fixup.c + mailutils_maildir_fixup_LDADD = \ + $(MU_APP_LIBRARIES)\ + $(MU_LIB_MAILDIR)\ + $(MU_LIB_MAILUTILS)\ + $(MUTOOL_LIBRARIES_TAIL) +endif + mailutils_send_SOURCES = send.c mailutils_send_CPPFLAGS = \ $(AM_CPPFLAGS)\ diff --git a/mu/libexec/maildir_fixup.c b/mu/libexec/maildir_fixup.c new file mode 100644 index 000000000..811380691 --- /dev/null +++ b/mu/libexec/maildir_fixup.c @@ -0,0 +1,202 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 2010-2020 Free Software Foundation, Inc. + + GNU Mailutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GNU Mailutils is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */ + +#if defined(HAVE_CONFIG_H) +# include <config.h> +#endif +#include <mailutils/mailutils.h> +#include <sysexits.h> +#include "mu.h" +#include "muaux.h" + +/* Utility-specific exit codes */ +enum + { + EXIT_OK, /* Success */ + EXIT_HARDFAIL, /* None of the maildirs fixed */ + EXIT_SOFTFAIL, /* Some of the maildirs failed to fix */ + }; + +static int verbose_option; +static int dry_run_option; +static unsigned long fail_count; +static unsigned long succ_count; + +static struct mu_option options[] = +{ + { "verbose", 'v', NULL, MU_OPTION_DEFAULT, + "verbosely list what is being done", + mu_c_incr, &verbose_option }, + { "dry-run", 'n', NULL, MU_OPTION_DEFAULT, + "do nothing, print everything", + mu_c_incr, &dry_run_option }, + MU_OPTION_END +}; + +static int +mailbox_fixup (void *item, void *data) +{ + struct mu_list_response *resp = item; + mu_mailbox_t mbox = NULL; + int rc; + + if (!(resp->type & MU_FOLDER_ATTRIBUTE_FILE)) + return 0; + + if (verbose_option) + mu_diag_output (MU_DIAG_INFO, "Fixing %s", resp->name); + if (dry_run_option) + return 0; + + rc = manlock_open_mailbox (&mbox, resp->name, 1, MU_STREAM_RDWR); + if (rc == 0) + { + rc = mu_mailbox_scan (mbox, 1, NULL); + mu_mailbox_close (mbox); + manlock_unlock (mbox); + mu_mailbox_destroy (&mbox); + } + else + mu_error ("can't open mailbox: %s", mu_strerror (rc)); + + if (rc) + fail_count++; + else + succ_count++; + return rc; +} + +static inline int +filename_ok (char const *fname) +{ + return fname[0] == '/' || + fname[0] == '~' || + (fname[0] == '.' && + (fname[1] == '/' || (fname[1] == '.' && fname[2] == '/'))); +} + +static int +fix_mailboxes_in_folder (char *fname) +{ + mu_folder_t folder; + struct mu_folder_scanner scn = MU_FOLDER_SCANNER_INITIALIZER; + int rc; + + if (!filename_ok (fname)) + { + char *cwd = mu_getcwd (); + size_t prefix_len = strlen (cwd); + if (cwd[prefix_len-1] != '/') + prefix_len++; + fname = mu_make_file_name (cwd, fname); + free (cwd); + if (!fname) + mu_alloc_die (); + } + + rc = mu_folder_create (&folder, fname); + if (rc) + { + mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_create", fname, rc); + return rc; + } + rc = mu_folder_open (folder, MU_STREAM_READ); + if (rc) + { + mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_open", fname, rc); + goto err; + } + + rc = mu_list_create (&scn.result); + if (rc) + { + mu_diag_funcall (MU_DIAG_ERROR, "mu_list_create", NULL, rc); + goto err; + } + + rc = mu_folder_scan (folder, &scn); + if (rc) + mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_scan", NULL, rc); + else + mu_list_foreach (scn.result, mailbox_fixup, NULL); + + err: + mu_list_destroy (&scn.result); + mu_folder_destroy (&folder); + return rc; +} + +static char docstring[] = N_( + "recursively scans all maildirs in the folder\n" + "\nThis induces fixing of the maildir message attributes (bug #56428) " + "and attaching persistent UID numbers (commits fd9b19bac-d7110faa).\n" +); +static char argdoc[] = N_("FOLDER ..."); +static char *capa[] = { + "debug", + "locking", + NULL +}; + +static struct mu_cfg_param config_param[] = { + { "mandatory-locking", mu_cfg_section }, + { NULL } +}; + +int +main (int argc, char **argv) +{ + int i; + + mu_registrar_record (mu_maildir_record); + mu_registrar_set_default_scheme ("maildir"); + manlock_cfg_init (); + mu_cli_simple (argc, argv, + MU_CLI_OPTION_OPTIONS, options, + MU_CLI_OPTION_OPTIONS, common_options, + MU_CLI_OPTION_PROG_NAME, getenv ("MAILUTILS_PROGNAME"), + MU_CLI_OPTION_PROG_DOC, docstring, + MU_CLI_OPTION_PROG_ARGS, argdoc, + MU_CLI_OPTION_RETURN_ARGC, &argc, + MU_CLI_OPTION_RETURN_ARGV, &argv, + MU_CLI_OPTION_PACKAGE_NAME, PACKAGE_NAME, + MU_CLI_OPTION_PACKAGE_URL, PACKAGE_URL, + MU_CLI_OPTION_BUG_ADDRESS, PACKAGE_BUGREPORT, + MU_CLI_OPTION_VERSION_HOOK, mu_version_hook, + MU_CLI_OPTION_CAPABILITIES, capa, + MU_CLI_OPTION_CONFIG, config_param, + MU_CLI_OPTION_CONF_SITE_FILE, NULL, + MU_CLI_OPTION_CONF_PROGNAME, "maildir_fixup", + MU_CLI_OPTION_END); + + if (argc == 0) + { + mu_error ("required argument missing; try --help for more info"); + exit (EX_USAGE); + } + + if (dry_run_option) + verbose_option++; + + for (i = 0; i < argc; i++) + fix_mailboxes_in_folder (argv[i]); + + if (fail_count) + exit (succ_count == 0 ? EXIT_HARDFAIL : EXIT_SOFTFAIL); + + exit (EXIT_OK); +} + diff --git a/mu/libexec/mu.h b/mu/libexec/mu.h index 7631728f3..8050ce3e0 100644 --- a/mu/libexec/mu.h +++ b/mu/libexec/mu.h @@ -21,6 +21,7 @@ #include <stdarg.h> #include <mailutils/mailutils.h> +extern struct mu_option common_options[]; void mu_action_getopt (int *pargc, char ***pargv, struct mu_option *opt, char const *docstring, char const *argdoc); int mu_vgetans (const char *variants, const char *fmt, va_list ap); |