/* GNU Mailutils -- a suite of utilities for electronic mail Copyright (C) 2003-2021 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 . */ #if defined(HAVE_CONFIG_H) # include #endif #include #include #include #include #include #include #include #include #include "mailutils/cli.h" #include #ifdef HAVE_TERMIOS_H # include #endif #include static int reverse_order; static int preserve_mail; static int emacs_mode; static int uidl_option; static int verbose_option; static int ignore_errors; static char *program_id_option; static size_t max_messages_option; static int notify; static int progress_meter_option; /* These bits tell what to do when an error occurs: */ #define ONERROR_SKIP 0x01 /* Skip to the next message */ #define ONERROR_DELETE 0x02 /* Delete the source message */ #define ONERROR_COUNT 0x04 /* Count it as processed */ static int onerror_flags; size_t msg_count = 0; /* Number of processed messages */ size_t get_err_count = 0; /* Number of message retrieval errors */ size_t app_err_count = 0; /* Number of message appending errors */ enum set_ownership_mode { copy_owner_id, copy_owner_name, set_owner_id, set_owner_name }; #define SET_OWNERSHIP_MAX 4 struct user_id { uid_t uid; gid_t gid; }; struct set_ownership_method { enum set_ownership_mode mode; union { char *name; struct user_id id; } owner; }; static struct set_ownership_method so_methods[SET_OWNERSHIP_MAX]; static int so_method_num; struct set_ownership_method * get_next_so_method () { if (so_method_num == MU_ARRAY_SIZE (so_methods)) { mu_error (_("ownership method table overflow")); exit (1); } return so_methods + so_method_num++; } mu_kwd_t method_kwd[] = { { "copy-id", copy_owner_id }, { "copy-name", copy_owner_name }, { "set-name", set_owner_name }, { "user", set_owner_name }, { "set-id", set_owner_id }, { NULL } }; static int set_mailbox_ownership (const char *str) { if (strcmp (str, "clear") == 0) so_method_num = 0; else { int code; char *p; size_t len = strcspn (str, "="); struct set_ownership_method *meth; if (mu_kwd_xlat_name_len (method_kwd, str, len, &code)) { mu_error (_("invalid ownership method: %s"), str); return 1; } meth = get_next_so_method (); meth->mode = code; switch (meth->mode) { case copy_owner_id: case copy_owner_name: break; case set_owner_id: if (!str[len]) { mu_error (_("ownership method %s requires value"), str); return 1; } str += len + 1; meth->owner.id.uid = strtoul (str, &p, 0); if (*p) { if (*p == ':') { str = p + 1; meth->owner.id.gid = strtoul (str, &p, 0); if (*p) { mu_error (_("expected gid number, but found %s"), str); return 1; } } else { mu_error (_("expected uid number, but found %s"), str); return 1; } } else meth->owner.id.gid = (gid_t) -1; break; case set_owner_name: if (!str[len]) { mu_error (_("ownership method %s requires value"), str); return 1; } meth->owner.name = mu_strdup (str + len + 1); } } return 0; } static int set_mailbox_ownership_list (char const *str) { if (!strchr (str, ',')) return set_mailbox_ownership (str); else { struct mu_wordsplit ws; size_t i; ws.ws_delim = ","; if (mu_wordsplit (str, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_DELIM)) { mu_error (_("cannot parse %s: %s"), str, mu_wordsplit_strerror (&ws)); return 1; } for (i = 0; i < ws.ws_wordc; i++) if (set_mailbox_ownership (ws.ws_wordv[i])) return 1; mu_wordsplit_free (&ws); return 0; } } static int set_onerror_action (void *item, void *data) { char *str = item; if (strcmp (str, "abort") == 0) onerror_flags = 0; else { static struct mu_kwd onerror_kw[] = { { "skip", ONERROR_SKIP }, { "delete", ONERROR_DELETE }, { "count", ONERROR_COUNT }, { NULL } }; int flag, clr = 0; if (strncmp (str, "no", 2) == 0) { clr = 1; str += 2; } if (mu_kwd_xlat_name (onerror_kw, str, &flag)) { mu_error (_("unknown keyword: %s"), str); return MU_ERR_FAILURE; } if (clr) onerror_flags &= ~flag; else onerror_flags |= flag; } return 0; } static int set_onerror_actions (char const *str) { mu_list_t list; int rc; mu_list_create (&list); mu_list_set_destroy_item (list, mu_list_free_item); mu_string_split (str, ",", list); rc = mu_list_foreach (list, set_onerror_action, NULL); mu_list_destroy (&list); return rc; } static void cli_mailbox_ownership (struct mu_parseopt *po, struct mu_option *opt, char const *arg) { if (set_mailbox_ownership_list (arg)) exit (po->po_exit_error); } static void cli_onerror (struct mu_parseopt *po, struct mu_option *opt, char const *arg) { if (set_onerror_actions (arg)) exit (po->po_exit_error); } static struct mu_option movemail_options[] = { { "preserve", 'p', NULL, MU_OPTION_DEFAULT, N_("preserve the source mailbox"), mu_c_bool, &preserve_mail }, { "keep-messages", 0, NULL, MU_OPTION_ALIAS }, { "reverse", 'r', NULL, MU_OPTION_DEFAULT, N_("reverse the sorting order"), mu_c_bool, &reverse_order }, { "emacs", 0, NULL, MU_OPTION_DEFAULT, N_("output information used by Emacs rmail interface"), mu_c_bool, &emacs_mode }, { "uidl", 'u', NULL, MU_OPTION_DEFAULT, N_("use UIDLs to avoid downloading the same message twice"), mu_c_bool, &uidl_option }, { "verbose", 'v', NULL, MU_OPTION_DEFAULT, N_("increase verbosity level"), mu_c_incr, &verbose_option }, { "owner", 'P', N_("MODELIST"), MU_OPTION_DEFAULT, N_("control mailbox ownership"), mu_c_string, cli_mailbox_ownership }, { "ignore-errors", 0, NULL, MU_OPTION_DEFAULT, N_("try to continue after errors"), mu_c_bool, &ignore_errors }, { "onerror", 0, N_("KW[,KW...]"), MU_OPTION_DEFAULT, N_("what to do on errors"), mu_c_string, NULL, cli_onerror }, { "program-id", 0, N_("FMT"), MU_OPTION_DEFAULT, N_("set program identifier for diagnostics (default: program name)"), mu_c_string, &program_id_option }, { "max-messages", 0, N_("NUMBER"), MU_OPTION_DEFAULT, N_("process at most NUMBER messages"), mu_c_size, &max_messages_option }, { "notify", 0, NULL, MU_OPTION_DEFAULT, N_("enable biff notification"), mu_c_bool, ¬ify }, { "progress-meter", 'm', NULL, MU_OPTION_DEFAULT, N_("enable progress meter"), mu_c_bool, &progress_meter_option }, MU_OPTION_END }, *options[] = { movemail_options, NULL }; static int cb_mailbox_ownership (void *data, mu_config_value_t *val) { int i; if (val->type == MU_CFG_STRING) set_mailbox_ownership_list (val->v.string); if (mu_cfg_assert_value_type (val, MU_CFG_LIST)) return 1; for (i = 0; i < val->v.arg.c; i++) { if (mu_cfg_assert_value_type (&val->v.arg.v[i], MU_CFG_STRING)) return 1; if (set_mailbox_ownership (val->v.arg.v[i].v.string)) return 1; } return 0; } static int cb_onerror (void *data, mu_config_value_t *val) { switch (val->type) { case MU_CFG_LIST: mu_list_foreach (val->v.list, set_onerror_action, NULL); break; case MU_CFG_STRING: set_onerror_actions (val->v.string); break; default: mu_error ("%s", _("too many arguments")); } return 0; } struct mu_cfg_param movemail_cfg_param[] = { { "preserve", mu_c_bool, &preserve_mail, 0, NULL, N_("Do not remove messages from the source mailbox.") }, { "reverse", mu_c_bool, &reverse_order, 0, NULL, N_("Reverse message sorting order.") }, { "emacs", mu_c_bool, &emacs_mode, 0, NULL, N_("Output information used by Emacs rmail interface.") }, { "uidl", mu_c_bool, &uidl_option, 0, NULL, N_("Use UIDLs to avoid downloading the same message twice.") }, { "verbose", mu_c_int, &verbose_option, 0, NULL, N_("Set verbosity level.") }, { "program-id", mu_c_string, &program_id_option, 0, NULL, N_("Set program identifier string (default: program name)") }, { "mailbox-ownership", mu_cfg_callback, NULL, 0, cb_mailbox_ownership, N_("Define a list of methods for setting mailbox ownership. Valid " "methods are:\n" " copy-id get owner UID and GID from the source mailbox\n" " copy-name get owner name from the source mailbox URL\n" " set-id=UID[:GID] set supplied UID and GID\n" " set-name=USER make destination mailbox owned by USER"), N_("methods: list") }, { "max-messages", mu_c_size, &max_messages_option, 0, NULL, N_("Copy at most messages."), N_("count") }, { "ignore-errors", mu_c_bool, &ignore_errors, 0, NULL, N_("Continue after an error.") }, { "onerror", mu_cfg_callback, NULL, 0, cb_onerror, N_("What to do after an error. Argument is a list of:\n" " abort - terminate the program (the default)\n" " skip - skip to the next message\n" " delete - delete this one and to the next message\n" " count - count this message as processed\n" "Each keyword can be prefixed with \"no\" to reverse its meaning."), N_("arg: list") }, { NULL } }; struct mu_cli_setup cli = { options, movemail_cfg_param, N_("GNU movemail -- move messages across mailboxes."), N_("inbox-url destfile [POP-password]") }; static char *movemail_capa[] = { "debug", "locking", "mailbox", "auth", NULL }; void die (mu_mailbox_t mbox, const char *msg, int status) { mu_url_t url = NULL; mu_mailbox_get_url (mbox, &url); if (emacs_mode) mu_error (_("%s:mailbox `%s': %s: %s"), mu_errname (status), mu_url_to_string (url), msg, mu_strerror (status)); else mu_error (_("mailbox `%s': %s: %s"), mu_url_to_string (url), msg, mu_strerror (status)); exit (1); } void lock_mailbox (mu_mailbox_t mbox) { mu_locker_t lock; int status; status = mu_mailbox_get_locker (mbox, &lock); if (status) die (mbox, _("cannot retrieve locker"), status); if (!lock) /* Remote mailboxes have no lockers */ return; status = mu_locker_lock (lock); if (status) die (mbox, _("cannot lock"), status); } void attach_passwd_ticket (mu_mailbox_t mbx, char *passwd) { mu_folder_t folder = NULL; mu_authority_t auth = NULL; mu_secret_t secret; mu_ticket_t t; int rc; rc = mu_secret_create (&secret, passwd, strlen (passwd)); if (rc) { mu_error ("mu_secret_create: %s", mu_strerror (rc)); exit (1); } mu_ticket_create (&t, NULL); mu_ticket_set_secret (t, secret); if ((rc = mu_mailbox_get_folder (mbx, &folder))) die (mbx, _("mu_mailbox_get_folder failed"), rc); if ((rc = mu_folder_get_authority (folder, &auth))) die (mbx, _("mu_folder_get_authority failed"), rc); if (auth && (rc = mu_authority_set_ticket (auth, t))) die (mbx, _("mu_authority_set_ticket failed"), rc); } /* Create and open a mailbox associated with the given URL, flags and (optionally) password */ void open_mailbox (mu_mailbox_t *mbx, char *name, int flags, char *passwd) { int status = mu_mailbox_create_default (mbx, name); if (status) { if (name) mu_error (_("could not create mailbox `%s': %s"), name, mu_strerror (status)); else mu_error (_("could not create default mailbox: %s"), mu_strerror (status)); exit (1); } if (passwd) attach_passwd_ticket (*mbx, passwd); status = mu_mailbox_open (*mbx, flags); if (status) die (*mbx, _("cannot open"), status); lock_mailbox (*mbx); } int move_message (mu_mailbox_t dst, mu_message_t msg, size_t msgno) { int rc; if ((rc = mu_mailbox_append_message (dst, msg)) != 0) { mu_error (_("cannot append message %lu: %s"), (unsigned long) msgno, mu_strerror (rc)); if (!(onerror_flags & ONERROR_DELETE)) return rc; } if (!preserve_mail) { mu_attribute_t attr; mu_message_get_attribute (msg, &attr); mu_attribute_set_deleted (attr); } return rc; } int movemail (mu_mailbox_t dst, mu_message_t msg, size_t msgno) { int rc = move_message (dst, msg, msgno); if (rc == 0) ++msg_count; else { app_err_count++; if (onerror_flags) { if (onerror_flags & ONERROR_COUNT) ++msg_count; } else return 1; } return max_messages_option && msg_count >= max_messages_option; } /* Open source mailbox using compatibility syntax. Source_name is of the form: po:USERNAME[:POP-SERVER] if POP-SERVER part is omitted, the MAILHOST environment variable will be consulted. */ void compatibility_mode (mu_mailbox_t *mbx, char *source_name, char *password, int flags) { char *tmp; char *user_name = strtok (source_name+3, ":"); char *host = strtok (NULL, ":"); if (!host) host = getenv ("MAILHOST"); if (!host) { mu_error (_("hostname of the POP3 server is unknown")); exit (1); } mu_asprintf (&tmp, "pop://%s@%s", user_name, host); open_mailbox (mbx, tmp, flags, password); free (tmp); } static mu_mailbox_t source, dest; static void close_mailboxes (void) { mu_mailbox_close (dest); mu_mailbox_close (source); } static int get_mbox_owner_id (mu_mailbox_t mbox, mu_url_t url, struct user_id *id) { const char *s; int rc = mu_url_sget_scheme (url, &s); if (rc) die (mbox, _("cannot get scheme"), rc); if ((strcmp (s, "/") == 0 || strcmp (s, "mbox") == 0 || strcmp (s, "mh") == 0 || strcmp (s, "maildir") == 0)) { struct stat st; rc = mu_url_sget_path (url, &s); if (rc) die (mbox, _("cannot get path"), rc); if (stat (s, &st)) { mu_diag_funcall (MU_DIAG_ERROR, "stat", s, errno); exit (1); } id->uid = st.st_uid; id->gid = st.st_gid; return 0; } else if (verbose_option) mu_diag_output (MU_DIAG_WARNING, _("ignoring copy-name: not a local mailbox")); return 1; } static int get_user_id (const char *name, struct user_id *id) { struct mu_auth_data *auth = mu_get_auth_by_name (name); if (!auth) { if (verbose_option) mu_diag_output (MU_DIAG_WARNING, _("no such user: %s"), name); return 1; } id->uid = auth->uid; id->gid = auth->gid; mu_auth_data_free (auth); return 0; } static int get_mbox_owner_name (mu_mailbox_t mbox, mu_url_t url, struct user_id *id) { const char *s; int rc = mu_url_sget_user (url, &s); if (rc) /* FIXME */ die (mbox, _("cannot get mailbox owner name"), rc); return get_user_id (s, id); } static int guess_mbox_owner (mu_mailbox_t mbox, struct user_id *id) { mu_url_t url = NULL; int rc; struct set_ownership_method *meth; rc = mu_mailbox_get_url (mbox, &url); if (rc) { mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_get_url", NULL, rc); exit (1); } rc = 1; for (meth = so_methods; rc == 1 && meth < so_methods + so_method_num; meth++) { switch (meth->mode) { case copy_owner_id: rc = get_mbox_owner_id (mbox, url, id); break; case copy_owner_name: rc = get_mbox_owner_name (mbox, url, id); break; case set_owner_id: id->uid = meth->owner.id.uid; rc = 0; if (meth->owner.id.gid == (gid_t)-1) { struct passwd *pw = getpwuid (id->uid); if (pw) id->gid = pw->pw_gid; else { if (verbose_option) mu_diag_output (MU_DIAG_WARNING, _("no user with uid %lu found"), (unsigned long) id->uid); rc = 1; } } else id->gid = meth->owner.id.gid; break; case set_owner_name: rc = get_user_id (meth->owner.name, id); break; } } return rc; } static void switch_owner (mu_mailbox_t mbox) { struct user_id user_id; if (so_method_num == 0) return; if (getuid ()) { if (verbose_option) mu_diag_output (MU_DIAG_WARNING, _("ignoring mailbox-ownership statement")); return; } if (guess_mbox_owner (mbox, &user_id) == 0) { if (mu_switch_to_privs (user_id.uid, user_id.gid, NULL)) exit (1); } else { mu_error (_("no suitable method for setting mailbox ownership")); exit (1); } } static int _compare_uidls (const void *item, const void *value) { const struct mu_uidl *a = item; const struct mu_uidl *b = value; return strcmp (a->uidl, b->uidl); } struct movemail_getvar_closure { const char *source_name; const char *dest_name; mu_url_t source_url; mu_url_t dest_url; }; #define SEQ(s, n, l) \ (((l) == (sizeof(s) - 1)) && memcmp (s, n, l) == 0) static int get_url_part (mu_url_t url, const char *name, size_t nlen, char **ret) { int rc; if (!url) return MU_WRDSE_UNDEF; if (SEQ ("user", name, nlen)) rc = mu_url_aget_user (url, ret); else if (SEQ ("host", name, nlen)) rc = mu_url_aget_host (url, ret); else if (SEQ ("port", name, nlen)) rc = mu_url_aget_portstr (url, ret); else if (SEQ ("path", name, nlen)) rc = mu_url_aget_path (url, ret); else return MU_WRDSE_UNDEF; switch (rc) { case 0: break; case MU_ERR_NOENT: return MU_WRDSE_UNDEF; default: if (mu_asprintf (ret, "%s", mu_strerror (rc))) return MU_WRDSE_NOSPACE; return MU_WRDSE_USERERR; } return MU_WRDSE_OK; } static int movemail_getvar (char **ret, const char *name, size_t nlen, void *data) { struct movemail_getvar_closure *pc = data; const char *s; if (nlen > 7 && memcmp ("source_", name, 7) == 0) return get_url_part (pc->source_url, name + 7, nlen - 7, ret); if (nlen > 5 && memcmp ("dest_", name, 5) == 0) return get_url_part (pc->dest_url, name + 5, nlen - 5, ret); if (SEQ ("progname", name, nlen)) s = mu_program_name; else if (SEQ ("source", name, nlen)) s = pc->source_name; else if (SEQ ("dest", name, nlen)) s = pc->dest_name; else return MU_WRDSE_UNDEF; *ret = strdup (s); if (!*ret) return MU_WRDSE_NOSPACE; return MU_WRDSE_OK; } static void set_program_id (const char *source_name, const char *dest_name) { int rc; struct mu_wordsplit ws; struct movemail_getvar_closure clos; clos.source_name = source_name; clos.dest_name = dest_name; rc = mu_mailbox_get_url (source, &clos.source_url); if (rc) mu_diag_output (MU_DIAG_INFO, _("cannot obtain source mailbox URL: %s"), mu_strerror (rc)); rc = mu_mailbox_get_url (dest, &clos.dest_url); if (rc) mu_diag_output (MU_DIAG_INFO, _("cannot obtain destination mailbox URL: %s"), mu_strerror (rc)); ws.ws_getvar = movemail_getvar; ws.ws_closure = &clos; if (mu_wordsplit (program_id_option, &ws, MU_WRDSF_NOSPLIT | MU_WRDSF_NOCMD | MU_WRDSF_GETVAR | MU_WRDSF_CLOSURE)) { mu_error (_("cannot expand line `%s': %s"), program_id_option, mu_wordsplit_strerror (&ws)); return; } /* FIXME: Don't use mu_set_program_name here, because it plays wise with its argument. We need a mu_set_diag_prefix function. */ mu_program_name = ws.ws_wordv[0]; ws.ws_wordc = 0; mu_wordsplit_free (&ws); mu_stdstream_strerr_setup (MU_STRERR_STDERR); } static int screen_width (void) { struct winsize ws; ws.ws_col = 0; if (ioctl(1, TIOCGWINSZ, (char *) &ws) < 0) { char *p = getenv ("COLUMNS"); if (p) ws.ws_col = atol (p); } if (ws.ws_col == 0) return 80; return ws.ws_col; } static void progress_format (size_t pos, size_t count) { int n; fputc ('\r', stdout); n = printf ("message %zu/%zu", pos, count); n = screen_width () - n; while (n--) fputc (' ', stdout); fflush (stdout); } void progress_start (mu_iterator_t itr) { size_t count; if (!progress_meter_option) return; if (mu_iterator_ctl (itr, mu_itrctl_count, &count)) { progress_meter_option = 0; return; } progress_format (0, count); } void progress_mark (mu_iterator_t itr) { size_t count, pos; if (!progress_meter_option) return; if (mu_iterator_ctl (itr, mu_itrctl_count, &count) || mu_iterator_ctl (itr, mu_itrctl_tell, &pos)) { progress_meter_option = 0; return; } if (reverse_order) pos = count - pos + 1; progress_format (pos, count); } void progress_stop (void) { if (progress_meter_option) fputc ('\n', stdout); } int main (int argc, char **argv) { size_t total; int rc = 0; char *source_name, *dest_name; int flags; mu_list_t src_uidl_list = NULL; mu_iterator_t itr; /* Native Language Support */ MU_APP_INIT_NLS (); MU_AUTH_REGISTER_ALL_MODULES (); /* Register the desired "mailbox" formats. */ mu_register_all_formats (); /* Register authentication modules */ mu_auth_register_module (&mu_auth_tls_module); /* argument parsing */ mu_cli (argc, argv, &cli, movemail_capa, NULL, &argc, &argv); if (argc < 2 || argc > 3) { mu_error (_("wrong number of arguments")); return 1; } if (ignore_errors) onerror_flags |= ONERROR_SKIP|ONERROR_COUNT; if (!isatty (1)) progress_meter_option = 0; if (emacs_mode) { /* Undo the effect of configuration options that may affect interaction with Emacs. */ mu_registrar_set_default_record (mu_mbox_record); mu_stdstream_strerr_setup (MU_STRERR_STDERR); } atexit (close_mailboxes); source_name = argv[0]; dest_name = argv[1]; flags = preserve_mail ? MU_STREAM_READ : MU_STREAM_RDWR; if (strncmp (source_name, "po:", 3) == 0) compatibility_mode (&source, source_name, argv[2], flags); else open_mailbox (&source, source_name, flags, argv[2]); switch_owner (source); open_mailbox (&dest, dest_name, MU_STREAM_APPEND | MU_STREAM_READ | MU_STREAM_CREAT, NULL); if (program_id_option) set_program_id (source_name, dest_name); if (notify) { rc = mu_mailbox_set_notify (dest, NULL); if (rc) mu_error (_("failed to set up notification: %s"), mu_strerror (rc)); } rc = mu_mailbox_messages_count (source, &total); if (rc) { mu_error(_("cannot count messages: %s"), mu_strerror (rc)); exit (1); } if (verbose_option) { mu_diag_output (MU_DIAG_INFO, _("number of messages in source mailbox: %lu"), (unsigned long) total); if (max_messages_option) mu_diag_output (MU_DIAG_INFO, reverse_order ? ngettext ("will process last %lu message", "will process last %lu messages", max_messages_option) : ngettext ("will process first %lu message", "will process first %lu messages", max_messages_option), (unsigned long) max_messages_option); } if (uidl_option) { mu_list_t dst_uidl_list = NULL; rc = mu_mailbox_get_uidls (source, &src_uidl_list); if (rc) die (source, _("cannot get UIDLs"), rc); rc = mu_mailbox_get_uidls (dest, &dst_uidl_list); if (rc) die (dest, _("cannot get UIDLs"), rc); mu_list_set_comparator (dst_uidl_list, _compare_uidls); mu_list_get_iterator (src_uidl_list, &itr); for (mu_iterator_first (itr); !mu_iterator_is_done (itr); mu_iterator_next (itr)) { struct mu_uidl *uidl; mu_iterator_current (itr, (void **)&uidl); if (mu_list_locate (dst_uidl_list, uidl, NULL) == 0) mu_iterator_ctl (itr, mu_itrctl_delete, NULL); } mu_list_destroy (&dst_uidl_list); mu_list_set_comparator (src_uidl_list, NULL); rc = mu_iterator_ctl (itr, mu_itrctl_set_direction, &reverse_order); if (rc) { mu_error (_("cannot set iteration direction: %s"), mu_strerror (rc)); exit (1); } progress_start (itr); for (mu_iterator_first (itr); !mu_iterator_is_done (itr); mu_iterator_next (itr)) { struct mu_uidl *uidl; mu_message_t msg; mu_header_t hdr; mu_iterator_current (itr, (void **)&uidl); if ((rc = mu_mailbox_get_message (source, uidl->msgno, &msg)) != 0) { mu_error (_("cannot read message %lu: %s"), (unsigned long) uidl->msgno, mu_strerror (rc)); get_err_count++; continue; } /* Check if the downloaded message has X-UIDL header. If not, add one. This check should disappear one mailutils implements alternative storage for mailbox meta-data. */ if ((rc = mu_message_get_header (msg, &hdr))) { mu_error (_("%lu: cannot get header: %s"), (unsigned long) uidl->msgno, mu_strerror (rc)); } else { char const *suidl = NULL; if ((rc = mu_header_sget_value (hdr, MU_HEADER_X_UIDL, &suidl))) { if (rc != MU_ERR_NOENT) mu_error (_("%lu: cannot get %s: %s"), (unsigned long) uidl->msgno, MU_HEADER_X_UIDL, mu_strerror (rc)); } else if (strcmp (suidl, uidl->uidl)) { mu_error (_("%lu: stored and reported UIDL differ; fixing"), (unsigned long) uidl->msgno); suidl = NULL; } if ((rc = mu_header_set_value (hdr, MU_HEADER_X_UIDL, uidl->uidl, 1))) { mu_error (_("%lu: cannot set header: %s"), (unsigned long) uidl->msgno, mu_strerror (rc)); } } progress_mark (itr); if (movemail (dest, msg, uidl->msgno)) break; } } else { rc = mu_mailbox_get_iterator (source, &itr); if (rc) { mu_error (_("cannot obtain mailbox iterator: %s"), mu_strerror (rc)); return 1; } rc = mu_iterator_ctl (itr, mu_itrctl_set_direction, &reverse_order); if (rc) { mu_error (_("cannot set iteration direction: %s"), mu_strerror (rc)); return 1; } progress_start (itr); for (mu_iterator_first (itr); !mu_iterator_is_done (itr); mu_iterator_next (itr)) { mu_message_t msg; size_t msgno; rc = mu_iterator_ctl (itr, mu_itrctl_tell, &msgno); if (rc) { mu_error (_("cannot get iterator position: %s"), mu_strerror (rc)); return 1; } rc = mu_iterator_current (itr, (void **)&msg); if (rc) { mu_error (_("cannot read message %lu: %s"), (unsigned long) msgno, mu_strerror (rc)); get_err_count++; continue; } if (movemail (dest, msg, msgno)) break; progress_mark (itr); } } progress_stop (); mu_iterator_destroy (&itr); if (verbose_option) { mu_diag_output (MU_DIAG_INFO, _("number of processed messages: %lu"), (unsigned long) msg_count); mu_diag_output (MU_DIAG_INFO, _("number of errors: %lu / %lu"), (unsigned long) get_err_count, (unsigned long) app_err_count); } if (app_err_count && !(onerror_flags & (ONERROR_DELETE|ONERROR_COUNT))) preserve_mail = 1; if (onerror_flags & ONERROR_COUNT) app_err_count = 0; mu_mailbox_sync (dest); rc = mu_mailbox_close (dest); mu_mailbox_destroy (&dest); if (rc) mu_error (_("cannot close destination mailbox: %s"), mu_strerror (rc)); else if (!preserve_mail) mu_mailbox_expunge (source); mu_mailbox_close (source); mu_mailbox_destroy (&source); return !(rc == 0 && (app_err_count + get_err_count) == 0); }