commit-mailutils
[Top][All Lists]
Advanced

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

[SCM] GNU Mailutils branch, master, updated. release-3.1.1-3-g3f1e6e4


From: Sergey Poznyakoff
Subject: [SCM] GNU Mailutils branch, master, updated. release-3.1.1-3-g3f1e6e4
Date: Sat, 17 Dec 2016 09:39:38 +0000 (UTC)

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU Mailutils".

http://git.savannah.gnu.org/cgit/mailutils.git/commit/?id=3f1e6e4206f0d8306b650490704a46209d3c1e70

The branch, master has been updated
       via  3f1e6e4206f0d8306b650490704a46209d3c1e70 (commit)
       via  7324f9691545126806bbe219607248c7ad854852 (commit)
       via  e87f373e24cd64b77e9b66348fc8aa0c53de5d52 (commit)
      from  64ef4cea7afa19ac83476fc80d1228b9333efdbf (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 3f1e6e4206f0d8306b650490704a46209d3c1e70
Author: Sergey Poznyakoff <address@hidden>
Date:   Sat Dec 17 11:29:06 2016 +0200

    Version 3.1.90

commit 7324f9691545126806bbe219607248c7ad854852
Author: Sergey Poznyakoff <address@hidden>
Date:   Sat Dec 17 09:06:15 2016 +0200

    Improve rename/copy/reomove API.
    
    * examples/rename.c: Remove.
    * examples/fcopy.c: New file.
    * examples/fremove.c: New file.
    * examples/frename.c: New file.
    * examples/Makefile.am: Update.
    
    * include/mailutils/util.h (mu_rename_file): Add flags.
    (mu_remove_file): New function.
    (MU_COPY_OVERWRITE): New flag.
    * libmailutils/base/renamefile.c: New file.
    * libmailutils/base/Makefile.am: Add newe file.
    * libmailutils/base/copyfile.c: Fix error handling.
    * libmailutils/base/renamefile.c (mu_rename_file): Refuse to proceed if
    the destination file exists and MU_COPY_OVERWRITE flag is not set
    * libmailutils/diag/errors (MU_ERR_REMOVE_SOURCE)
    (MU_ERR_RESTORE_META): New errors
    
    * imap4d/rename.c (imap4d_rename): Use mu_rename_file
    * mh/forw.c: Likewise.
    * mh/mh_whatnow.c: Likewise.
    * mh/mhn.c: Likewise.
    * mh/send.c: Likewise.
    
    * include/mailutils/cstr.h (mu_str_count): New proto.
    * include/mailutils/util.h (mu_file_name_is_safe): New proto.
    * libmailutils/string/safefilename.c: New file.
    * libmailutils/string/strcount.c: New file.
    * libmailutils/string/Makefile.am: Update.

commit e87f373e24cd64b77e9b66348fc8aa0c53de5d52
Author: Sergey Poznyakoff <address@hidden>
Date:   Thu Dec 15 17:25:40 2016 +0200

    Add functions for safe renaming and copying of files
    
    * libmailutils/base/copyfile.c: New file.
    * libmailutils/base/renamefile.c: New file.
    
    * include/mailutils/util.h (mu_copy_file)
    (mu_rename_file): New protos.
    
    * libmailutils/base/Makefile.am: Add new files.
    * examples/rename.c: New file.
    * examples/Makefile.am: Add new files.
    
    * NEWS: Update.

-----------------------------------------------------------------------

Summary of changes:
 NEWS                                               |    7 +-
 configure.ac                                       |    2 +-
 examples/.gitignore                                |    3 +
 examples/Makefile.am                               |    3 +
 examples/fcopy.c                                   |   63 ++++
 examples/fremove.c                                 |   49 +++
 examples/frename.c                                 |   59 ++++
 imap4d/rename.c                                    |   25 +-
 include/mailutils/cstr.h                           |    2 +
 include/mailutils/util.h                           |   19 +-
 libmailutils/base/Makefile.am                      |    3 +
 libmailutils/base/copyfile.c                       |  356 ++++++++++++++++++++
 libmailutils/base/removefile.c                     |  275 +++++++++++++++
 libmailutils/base/renamefile.c                     |   86 +++++
 libmailutils/diag/errors                           |    4 +
 libmailutils/string/Makefile.am                    |    2 +
 libmailutils/string/safefilename.c                 |   84 +++++
 .../glob.h => libmailutils/string/strcount.c       |   53 +--
 mh/forw.c                                          |   10 +-
 mh/mh_whatnow.c                                    |    8 +-
 mh/mhn.c                                           |   26 +-
 mh/send.c                                          |   10 +-
 22 files changed, 1108 insertions(+), 41 deletions(-)
 create mode 100644 examples/fcopy.c
 create mode 100644 examples/fremove.c
 create mode 100644 examples/frename.c
 create mode 100644 libmailutils/base/copyfile.c
 create mode 100644 libmailutils/base/removefile.c
 create mode 100644 libmailutils/base/renamefile.c
 create mode 100644 libmailutils/string/safefilename.c
 copy include/mailutils/glob.h => libmailutils/string/strcount.c (55%)

diff --git a/NEWS b/NEWS
index 96e078a..3c438cb 100644
--- a/NEWS
+++ b/NEWS
@@ -1,11 +1,14 @@
-GNU mailutils NEWS -- history of user-visible changes. 2016-12-16
+GNU mailutils NEWS -- history of user-visible changes. 2016-12-17
 Copyright (C) 2002-2016 Free Software Foundation, Inc.
 See the end of file for copying conditions.
 
 Please send mailutils bug reports to <address@hidden>.
 
 
-Version 3.1.1 - 2016-12-16
+Version 3.1.90 (Git)
+
+
+Version 3.1.1 - 2016-12-15
 
 Bugfixes
 
diff --git a/configure.ac b/configure.ac
index 7b9aba4..2327ca0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -16,7 +16,7 @@ dnl You should have received a copy of the GNU General Public 
License along
 dnl with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>.
 
 AC_PREREQ(2.63)
-AC_INIT([GNU Mailutils], [3.1.1], address@hidden, [mailutils],
+AC_INIT([GNU Mailutils], [3.1.90], address@hidden, [mailutils],
         [http://mailutils.org])
 AC_CONFIG_SRCDIR([libmailutils/mailbox/mailbox.c])
 AC_CONFIG_AUX_DIR([build-aux])
diff --git a/examples/.gitignore b/examples/.gitignore
index e75f226..d4792a3 100644
--- a/examples/.gitignore
+++ b/examples/.gitignore
@@ -5,6 +5,9 @@ base64
 decode2047
 echosrv
 encode2047
+fcopy
+fremove
+frename
 header
 http
 iconv
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 55b53df..fa63b66 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -30,6 +30,9 @@ noinst_PROGRAMS = \
  addr\
  base64\
  echosrv\
+ fcopy\
+ fremove\
+ frename\
  header\
  http\
  iconv\
diff --git a/examples/fcopy.c b/examples/fcopy.c
new file mode 100644
index 0000000..e4aa868
--- /dev/null
+++ b/examples/fcopy.c
@@ -0,0 +1,63 @@
+#include <mailutils/mailutils.h>
+
+int owner_option;
+int mode_option;
+int force_option;
+int deref_option;
+
+static struct mu_option copy_options[] = {
+  { "owner", 'u', NULL, MU_OPTION_DEFAULT,
+    "copy ownership",
+    mu_c_bool, &owner_option },
+  { "mode", 'm', NULL, MU_OPTION_DEFAULT,
+    "copy mode",
+    mu_c_bool, &mode_option },
+  { "force", 'f', NULL, MU_OPTION_DEFAULT,
+    "force overwriting the destination file if it exists",
+    mu_c_bool, &force_option },
+  { "overwrite", 0, NULL, MU_OPTION_ALIAS },
+  { "dereference", 'h', NULL, MU_OPTION_DEFAULT,
+    "dereference symbolic links",
+    mu_c_bool, &deref_option },
+  MU_OPTION_END
+}, *options[] = { copy_options, NULL };
+  
+struct mu_cli_setup cli = {
+  options,
+  NULL,
+  "copy file",
+  "SRC DST"
+};
+
+static char *capa[] = {
+  "debug",
+  NULL
+};
+
+int
+main (int argc, char **argv)
+{
+  int rc;
+  int flags;
+  
+  mu_cli (argc, argv, &cli, capa, NULL, &argc, &argv);
+
+  if (argc != 2)
+    {
+      mu_error ("wrong number of arguments");
+      return 1;
+    }
+
+  flags = (owner_option ? MU_COPY_OWNER : 0)
+        | (mode_option ? MU_COPY_MODE : 0)
+        | (force_option ? MU_COPY_OVERWRITE : 0)
+        | (deref_option ? MU_COPY_DEREF : 0);
+  rc = mu_copy_file (argv[0], argv[1], flags);
+
+  if (rc)
+    mu_diag_funcall (MU_DIAG_ERROR, "mu_copy_file", NULL, rc);
+
+  return !!rc;
+}
+
+      
diff --git a/examples/fremove.c b/examples/fremove.c
new file mode 100644
index 0000000..e7c2d91
--- /dev/null
+++ b/examples/fremove.c
@@ -0,0 +1,49 @@
+#include <mailutils/mailutils.h>
+
+int owner_option;
+int mode_option;
+int force_option;
+
+static struct mu_option *options[] = { NULL };
+  
+struct mu_cli_setup cli = {
+  options,
+  NULL,
+  "delete file",
+  "FILE"
+};
+
+static char *capa[] = {
+  "debug",
+  NULL
+};
+
+int
+main (int argc, char **argv)
+{
+  int rc;
+  
+  mu_cli (argc, argv, &cli, capa, NULL, &argc, &argv);
+
+  if (argc != 1)
+    {
+      mu_error ("wrong number of arguments");
+      return 1;
+    }
+
+  if (!mu_file_name_is_safe (argv[0])
+      || (argv[0][0] == '/' && mu_str_count (argv[0], '/') < 2))
+    {
+      mu_error ("unsafe file name");
+      return 1;
+    }
+  
+  rc = mu_remove_file (argv[0]);
+
+  if (rc)
+    mu_diag_funcall (MU_DIAG_ERROR, "mu_remove_file", NULL, rc);
+
+  return !!rc;
+}
+
+      
diff --git a/examples/frename.c b/examples/frename.c
new file mode 100644
index 0000000..1bf6556
--- /dev/null
+++ b/examples/frename.c
@@ -0,0 +1,59 @@
+#include <mailutils/mailutils.h>
+
+int force_option;
+
+static struct mu_option rename_options[] = {
+  { "force", 'f', NULL, MU_OPTION_DEFAULT,
+    "force overwriting the destination file if it exists",
+    mu_c_bool, &force_option },
+  { "overwrite", 0, NULL, MU_OPTION_ALIAS },
+  MU_OPTION_END
+}, *options[] = { rename_options, NULL };
+  
+struct mu_cli_setup cli = {
+  options,
+  NULL,
+  "rename file",
+  "SRC DST"
+};
+
+static char *capa[] = {
+  "debug",
+  NULL
+};
+
+int
+main (int argc, char **argv)
+{
+  int rc;
+  
+  mu_cli (argc, argv, &cli, capa, NULL, &argc, &argv);
+
+  if (argc != 2)
+    {
+      mu_error ("wrong number of arguments");
+      return 1;
+    }
+
+  if (!mu_file_name_is_safe (argv[0])
+      || (argv[0][0] == '/' && mu_str_count (argv[0], '/') < 2))
+    {
+      mu_error ("%s: unsafe file name", argv[0]);
+      return 1;
+    }
+  if (!mu_file_name_is_safe (argv[1])
+      || (argv[1][0] == '/' && mu_str_count (argv[1], '/') < 2))
+    {
+      mu_error ("%sunsafe file name", argv[0]);
+      return 1;
+    }
+  
+  rc = mu_rename_file (argv[0], argv[1], force_option ? MU_COPY_OVERWRITE : 0);
+
+  if (rc)
+    mu_diag_funcall (MU_DIAG_ERROR, "mu_rename_file", NULL, rc);
+
+  return !!rc;
+}
+
+      
diff --git a/imap4d/rename.c b/imap4d/rename.c
index bca5023..c551d25 100644
--- a/imap4d/rename.c
+++ b/imap4d/rename.c
@@ -125,8 +125,8 @@ imap4d_rename (struct imap4d_session *session,
   if (!newname)
     return io_completion_response (command, RESP_NO, "Permission denied");
 
-  /* It is an error to attempt to rename from a mailbox name that already
-     exist.  */
+  /* It is an error to attempt to rename from a mailbox name that does not
+     exist or to a mailbox name that already exists. */
   if (stat (newname, &newst) == 0)
     {
       /* FIXME: What if it's a maildir?!? */
@@ -216,9 +216,26 @@ imap4d_rename (struct imap4d_session *session,
     }
   else
     {
-      if (rename (oldname, newname) != 0)
+      rc = mu_rename_file (oldname, newname, 0);
+      if (rc)
        {
-         mu_diag_funcall (MU_DIAG_ERROR, "rename", oldname, errno);
+         switch (rc)
+           {
+           case MU_ERR_REMOVE_SOURCE:
+             mu_error (_("failed to remove source mailbox after moving %s to 
%s"),
+                       oldname, newname);
+             break;
+         
+           case MU_ERR_RESTORE_META:
+             mu_error (_("failed to restore mailbox ownership/modes after 
moving %s to %s"),
+                       oldname, newname);
+             break;
+             
+           default:
+             mu_error (_("error renaming mailbox %s to %s: %s"),
+                       oldname, newname, mu_strerror (rc));
+           }
+
           rc = RESP_NO;
           msg = "Failed";
        }
diff --git a/include/mailutils/cstr.h b/include/mailutils/cstr.h
index f98439f..579a496 100644
--- a/include/mailutils/cstr.h
+++ b/include/mailutils/cstr.h
@@ -45,6 +45,8 @@ char *mu_str_skip_cset_comp (const char *str, const char 
*cset);
 char *mu_str_stripws (char *string);  
 
 int mu_string_split (const char *string, char *delim, mu_list_t list);
+
+size_t mu_str_count (char const *str, int chr);
   
 #ifdef __cplusplus
 }
diff --git a/include/mailutils/util.h b/include/mailutils/util.h
index 60bc99c..d539099 100644
--- a/include/mailutils/util.h
+++ b/include/mailutils/util.h
@@ -140,8 +140,6 @@ struct mu_param
   char *name;
   char *value;
 };
-
-  
   
 int mu_content_type_parse (const char *input, mu_content_type_t *retct);
 void mu_content_type_destroy (mu_content_type_t *pptr);
@@ -203,10 +201,27 @@ typedef enum mu_c_type mu_c_type_t;
 extern char const *mu_c_type_str[];
 int mu_str_to_c (char const *string, mu_c_type_t type, void *tgt,
                 char **errmsg);
+
+  /* -------------------------- */
+  /* Safe file copy and rename  */
+  /* -------------------------- */
+/* Bits for the flags argument of mu_copy_file and mu_rename_file.  The
+   MU_COPY_OVERWRITE is valid for both calls.  The rest is for mu_copy_file
+   only */
+#define MU_COPY_OVERWRITE 0x01 /* Overwrite destination file, if it exists */
+#define MU_COPY_MODE      0x02 /* Preserve file mode */
+#define MU_COPY_OWNER     0x04 /* Preserve file ownership */
+#define MU_COPY_DEREF     0x08 /* Dereference the source file */
   
+int mu_copy_file (const char *srcpath, const char *dstpath, int flags);
+int mu_rename_file (const char *oldpath, const char *newpath, int flags);
+int mu_remove_file (const char *path);
+
   /* ----------------------- */
   /* Assorted functions.     */
   /* ----------------------- */
+int mu_file_name_is_safe (char const *str);
+  
 int mu_getmaxfd (void);
 /* Get the host name, doing a gethostbyname() if possible. */
 int mu_get_host_name (char **host);
diff --git a/libmailutils/base/Makefile.am b/libmailutils/base/Makefile.am
index f47705d..24f08ba 100644
--- a/libmailutils/base/Makefile.am
+++ b/libmailutils/base/Makefile.am
@@ -24,6 +24,7 @@ libbase_la_SOURCES = \
  argcvjoin.c\
  argcvrem.c\
  assoc.c\
+ copyfile.c\
  ctparse.c\
  daemon.c\
  filesafety.c\
@@ -57,6 +58,8 @@ libbase_la_SOURCES = \
  permstr.c\
  registrar.c\
  refcount.c\
+ renamefile.c\
+ removefile.c\
  rfc2047.c\
  schemeauto.c\
  sha1.c\
diff --git a/libmailutils/base/copyfile.c b/libmailutils/base/copyfile.c
new file mode 100644
index 0000000..ff03a4b
--- /dev/null
+++ b/libmailutils/base/copyfile.c
@@ -0,0 +1,356 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+   Copyright (C) 2016 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/>. */
+
+#include <config.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <mailutils/stream.h>
+#include <mailutils/util.h>
+#include <mailutils/diag.h>
+#include <mailutils/error.h>
+#include <mailutils/errno.h>
+#include <mailutils/nls.h>
+
+static int copy_regular_file (const char *srcpath, const char *dstpath,
+                             int flags, struct stat *st);
+static int copy_symlink (const char *srcpath, const char *dstpath);
+static int copy_dir (const char *srcpath, const char *dstpath, int flags);
+
+/* Copy SRCPATH to DSTPATH.  SRCPATH can be any kind of file.  If it is
+   a directory, its content will be copied recursively.
+
+   FLAGS:
+
+   MU_COPY_OVERWRITE      Overwrite destination file, if it exists.
+   MU_COPY_MODE           Preserve file mode
+   MU_COPY_OWNER          Preserve file ownership 
+   MU_COPY_DEREF          Dereference symbolic links: operate on files they
+                          refer to.
+*/
+int
+mu_copy_file (const char *srcpath, const char *dstpath, int flags)
+{
+  int rc = 0;
+  struct stat st;
+
+  if (((flags & MU_COPY_DEREF) ? stat : lstat) (srcpath, &st))
+    {
+      mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+               (_("can't stat file %s: %s"),
+                srcpath, mu_strerror (errno)));
+      return errno;
+    }
+
+  if (access (dstpath, F_OK) == 0)
+    {
+      if (flags & MU_COPY_OVERWRITE)
+       {
+         rc = mu_remove_file (dstpath);
+         if (rc)
+           {
+             mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+                       (_("can't remove destination %s: %s"),
+                        dstpath, mu_strerror (rc)));
+             return rc;
+           }
+       }
+      else
+       return EEXIST;
+    }
+  
+  switch (st.st_mode & S_IFMT)
+    {
+    case S_IFREG:
+      return copy_regular_file (srcpath, dstpath, flags, &st);
+
+    case S_IFLNK:
+      return copy_symlink (srcpath, dstpath);
+
+    case S_IFDIR:
+      return copy_dir (srcpath, dstpath, flags);
+
+    case S_IFBLK:
+    case S_IFCHR:
+      if (mknod (dstpath, st.st_mode & 0777, st.st_dev))
+       {
+         rc = errno;
+         mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+                   (_("%s: cannot create node: %s"),
+                    dstpath,
+                    mu_strerror (rc)));
+       }
+      break;
+      
+    case S_IFIFO:
+      if (mkfifo (dstpath, st.st_mode & 0777))
+       {
+         rc = errno;
+         mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+                   (_("%s: cannot create node: %s"),
+                    dstpath,
+                    mu_strerror (rc)));
+       }
+      break;
+
+    default:
+      mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+               (_("%s: don't know how to copy file of that type"),
+                srcpath));
+      return ENOTSUP;
+    }
+
+  return rc;
+}
+      
+static int
+copy_regular_file (const char *srcpath, const char *dstpath, int flags,
+                  struct stat *st)
+{
+  int rc;
+  mu_stream_t src, dst;
+  mode_t mask, mode;
+
+  rc = mu_file_stream_create (&src, srcpath, MU_STREAM_READ);
+  if (rc)
+    {
+      mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+               (_("cannot open source file %s: %s"),
+                srcpath, mu_strerror (rc)));
+      return rc;
+    }
+
+  mask = umask (077);
+  mode = ((flags & MU_COPY_MODE) ? st->st_mode : (0666 & ~mask)) & 0777;
+
+  rc = mu_file_stream_create (&dst, dstpath, MU_STREAM_CREAT|MU_STREAM_WRITE);
+  umask (mask);
+  if (rc)
+    {
+      mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+               (_("cannot open destination file %s: %s"),
+                dstpath, mu_strerror (rc)));
+      mu_stream_destroy (&src);
+      return rc;
+    }
+
+  rc = mu_stream_copy (dst, src, 0, NULL);
+  if (rc)
+    {
+      mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+               (_("failed to copy %s to %s: %s"),
+                srcpath, dstpath, mu_strerror (rc)));
+    }
+  else 
+    {
+      mu_transport_t trans[2];
+
+      rc = mu_stream_ioctl (dst, MU_IOCTL_TRANSPORT, MU_IOCTL_OP_GET, trans);
+      if (rc == 0)
+       {           
+         if (fchmod ((int) trans[0], mode))
+           {
+             mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+                       (_("%s: cannot chmod: %s"),
+                        dstpath, mu_strerror (errno)));
+             rc = MU_ERR_RESTORE_META;
+           }
+         else if (flags & MU_COPY_OWNER)
+           {
+             uid_t uid;
+             gid_t gid;
+
+             if (getuid () == 0)
+               {
+                 uid = st->st_uid;
+                 gid = st->st_gid;
+               }
+             else if (getuid () == st->st_uid)
+               {
+                 uid = -1;
+                 gid = st->st_gid;
+               }
+             else
+               {
+                 uid = -1;
+                 gid = -1;
+               }
+
+             if (gid != -1)
+               {
+                 if (fchown ((int) trans[0], uid, gid))
+                   {
+                     mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+                               (_("%s: cannot chown to %lu.%lu: %s"),
+                                dstpath,
+                                (unsigned long) uid,
+                                (unsigned long) gid,
+                                mu_strerror (errno)));
+                     rc = MU_ERR_RESTORE_META;
+                   }
+               }
+           }
+       }
+      else
+       {
+         mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+                   (_("can't change file mode and ownership after copying %s 
to %s;"
+                      " cannot get file handle: %s"),
+                    srcpath, dstpath,
+                    mu_strerror (rc)));
+       }      
+    }
+  
+  mu_stream_destroy (&src);
+  mu_stream_destroy (&dst);
+  
+  return rc;
+}
+
+static int
+copy_symlink (const char *srcpath, const char *dstpath)
+{
+  int rc;
+  char *buf = NULL;
+  size_t size = 0;
+  
+  rc = mu_readlink (srcpath, &buf, &size, NULL);
+  if (rc)
+    {
+      mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+               (_("%s: cannot read link: %s"),
+                srcpath, mu_strerror (rc)));
+      return rc;
+    }
+
+  if (symlink (buf, dstpath))
+    {
+      rc = errno;
+      mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+               (_("%s: can't link %s to %s: %s"),
+                srcpath, buf, dstpath, mu_strerror (rc)));
+    }
+  free (buf);
+  return rc;
+}
+  
+static int
+copy_dir (const char *srcpath, const char *dstpath, int flags)
+{
+  DIR *dirp;
+  struct dirent *dp;
+  struct stat st;
+  int rc;
+  mode_t mode, mask;
+  
+  if (stat (srcpath, &st))
+    {
+      mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+               (_("can't stat file %s: %s"),
+                srcpath, mu_strerror (errno)));
+      return errno;
+    }
+
+  mask = umask (077);
+  mode = ((flags & MU_COPY_MODE) ? st.st_mode : (0777 & ~mask)) & 0777;
+  
+  rc = mkdir (dstpath, 0700);
+  umask (mask);
+         
+  if (rc)
+    {
+      mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+               (_("can't create directory %s: %s"),
+                dstpath, mu_strerror (errno)));
+      return errno;
+    }
+  
+  dirp = opendir (srcpath);
+  if (dirp == NULL)
+    {
+      rc = errno;
+      mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+               ("cannot open directory %s: %s",
+                srcpath, mu_strerror (errno)));
+      return rc;
+    }
+
+  while ((dp = readdir (dirp)))
+    {
+      char const *ename = dp->d_name;
+      char *src, *dst;
+      
+      if (ename[ename[0] != '.' ? 0 : ename[1] != '.' ? 1 : 2] == 0)
+       continue;
+
+      src = mu_make_file_name (srcpath, ename);
+      dst = mu_make_file_name (dstpath, ename);
+      rc = mu_copy_file (src, dst, flags);
+      free (dst);
+      free (src);
+
+      if (rc)
+       break;
+    }
+  closedir (dirp);
+
+  if (chmod (dstpath, mode))
+    {
+      rc = errno;
+      mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+               (_("%s: cannot chmod: %s"),
+                dstpath, mu_strerror (rc)));
+    }
+  else if (flags & MU_COPY_OWNER)
+    {
+      uid_t uid;
+      gid_t gid;
+             
+      if (getuid () == 0)
+       {
+         uid = st.st_uid;
+         gid = st.st_gid;
+       }
+      else if (getuid () == st.st_uid)
+       {
+         uid = -1;
+         gid = st.st_gid;
+       }
+      else
+       {
+         uid = -1;
+         gid = -1;
+       }
+      
+      if (gid != -1)
+       {
+         if (chown (dstpath, uid, gid))
+           {
+             rc = errno;
+             mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+                       (_("%s: cannot chown to %lu.%lu: %s"),
+                        dstpath,
+                        (unsigned long) uid,
+                        (unsigned long) gid,
+                        mu_strerror (rc)));
+           }
+       }
+    }
+  return rc;
+}
+
diff --git a/libmailutils/base/removefile.c b/libmailutils/base/removefile.c
new file mode 100644
index 0000000..405eb7b
--- /dev/null
+++ b/libmailutils/base/removefile.c
@@ -0,0 +1,275 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+   Copyright (C) 2016 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/>. */
+
+#include <config.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <mailutils/stream.h>
+#include <mailutils/util.h>
+#include <mailutils/diag.h>
+#include <mailutils/error.h>
+#include <mailutils/errno.h>
+#include <mailutils/list.h>
+#include <mailutils/iterator.h>
+#include <mailutils/nls.h>
+
+static int removedir (const char *path);
+
+int
+mu_remove_file (const char *path)
+{
+  int rc = 0;
+  struct stat st;
+
+  if (stat (path, &st))
+    {
+      mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+               (_("can't stat file %s: %s"),
+                path, mu_strerror (errno)));
+      return errno;
+    }
+
+  if (S_ISDIR (st.st_mode))
+    rc = removedir (path);
+  else if (unlink (path))
+    {
+      rc = errno;
+      mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+               (_("can't unlink file %s: %s"),
+                path, mu_strerror (rc)));
+    }
+
+  return rc;
+}
+
+struct nameent
+{
+  int isdir;
+  char name[1];
+};
+
+static int
+name_add (mu_list_t list, char const *name)
+{
+  int rc;
+  size_t len = strlen (name);
+  struct nameent *ent = malloc (sizeof (*ent) + len);
+  if (!ent)
+    {
+      mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+               ("%s", mu_strerror (errno)));
+      return 1;
+    }
+  ent->isdir = -1;
+  strcpy (ent->name, name);
+  rc = mu_list_append (list, ent);
+  if (rc)
+    {
+      mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+               ("mu_list_append: %s", mu_strerror (rc)));
+      free (ent);
+    }
+    
+  return rc;
+}
+
+static int
+lsdir (const char *path, mu_list_t list)
+{
+  DIR *dirp;
+  struct dirent *dp;
+  int rc = 0;
+
+  dirp = opendir (path);
+  if (dirp == NULL)
+    {
+      rc = errno;
+      mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+               ("cannot open directory %s: %s",
+                path, mu_strerror (errno)));
+      return rc;
+    }
+
+  while ((dp = readdir (dirp)))
+    {
+      char const *ename = dp->d_name;
+      char *filename;
+      
+      if (ename[ename[0] != '.' ? 0 : ename[1] != '.' ? 1 : 2] == 0)
+       continue;
+
+      filename = mu_make_file_name (path, ename);
+      if (!filename)
+       {
+         rc = errno;
+         mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+                   ("%s: can't create file name: %s",
+                    path, mu_strerror (errno)));
+         break;
+       }
+
+      rc = name_add (list, filename);
+      free (filename);
+      if (rc)
+       break;
+    }
+
+  closedir (dirp);
+
+  return rc;
+}
+
+static int
+namecmp (const void *a, const void *b)
+{
+  struct nameent const *enta = a;
+  struct nameent const *entb = b;
+  int d = enta->isdir - entb->isdir;
+  if (d)
+    return d;
+  return strcmp (entb->name, enta->name);
+}
+
+static int
+check_parent_access (const char *path)
+{
+  int rc;
+  char *name, *p;
+
+  name = strdup (path);
+  if (!name)
+    return errno;
+  p = strrchr (name, '/');
+  if (p)
+    *p = 0;
+  else
+    strcpy (name, ".");
+  rc = access (name, R_OK|W_OK|X_OK);
+  free (name);
+  if (rc)
+    {
+      mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+               (_("not enough privileges to remove files from %s"),
+                name));
+      return EACCES;
+    }
+
+  return 0;
+}
+
+static int
+removedir (const char *path)
+{
+  int rc;
+  struct stat st;
+  mu_list_t namelist;
+  mu_iterator_t itr;
+  struct nameent *ent;
+
+  rc = check_parent_access (path);
+  if (rc)
+    return rc;
+  
+  rc = mu_list_create (&namelist);
+  if (rc)
+    {
+      mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+               ("mu_list_create: %s", mu_strerror (rc)));
+      return rc;
+    }
+  mu_list_set_destroy_item (namelist, mu_list_free_item);
+  mu_list_set_comparator (namelist, namecmp);
+
+  rc = name_add (namelist, path);
+  if (rc)
+    {
+      mu_list_destroy (&namelist);
+      return rc;
+    }
+  
+  rc = mu_list_get_iterator (namelist, &itr);
+  if (rc)
+    {
+      mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+               ("mu_list_get_iterator: %s", mu_strerror (rc)));
+      mu_list_destroy (&namelist);
+      return rc;
+    }
+
+  for (mu_iterator_first (itr);
+       !mu_iterator_is_done (itr);
+       mu_iterator_next (itr)) 
+    {
+      mu_iterator_current (itr, (void **)&ent);
+      
+      if (lstat (ent->name, &st))
+       {
+         rc = errno;
+         if (rc == ENOENT)
+           continue;
+         mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+                   (_("can't lstat file %s: %s"),
+                    ent->name, mu_strerror (rc)));
+         break;
+       }
+
+      if (S_ISDIR (st.st_mode))
+       {
+         ent->isdir = 1;
+         if (access (ent->name, R_OK|W_OK|X_OK))
+           {
+             rc = EACCES;
+             mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+                       (_("not enough privileges to remove files from %s"),
+                        ent->name));
+           }
+         else 
+           rc = lsdir (ent->name, namelist);
+         if (rc)
+           break;
+       }
+      else
+       ent->isdir = 0;
+    }
+
+  if (rc == 0)
+    {
+      mu_list_sort (namelist, namecmp);
+       
+      for (mu_iterator_first (itr);
+          !mu_iterator_is_done (itr);
+          mu_iterator_next (itr))
+       {
+         mu_iterator_current (itr, (void **)&ent);
+         rc = (ent->isdir ? rmdir : unlink) (ent->name);
+         if (rc)
+           {
+             mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+                       (_("can't remove %s: %s"),
+                        ent->name, mu_strerror (rc)));
+           }
+       }
+    }
+  mu_iterator_destroy (&itr);
+  mu_list_destroy (&namelist);
+
+  return rc;
+}
+  
+  
diff --git a/libmailutils/base/renamefile.c b/libmailutils/base/renamefile.c
new file mode 100644
index 0000000..a791619
--- /dev/null
+++ b/libmailutils/base/renamefile.c
@@ -0,0 +1,86 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+   Copyright (C) 2016 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/>. */
+
+#include <config.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <mailutils/stream.h>
+#include <mailutils/util.h>
+#include <mailutils/diag.h>
+#include <mailutils/error.h>
+#include <mailutils/errno.h>
+#include <mailutils/nls.h>
+
+/* Rename file OLDPATH to NEWPATH.  Prefer rename(2), unless OLDPATH
+   and NEWPATH reside on different devices.  In the latter case, fall
+   back to recursive copying.
+
+   FLAGS controls what to do if NEWPATH exists.  If it has MU_COPY_OVERWRITE
+   bit set, the NEWPATH will be overwritten (if possible, atomically).
+   Otherwise EEXIST will be returned.
+*/
+int
+mu_rename_file (const char *oldpath, const char *newpath, int flags)
+{
+  int rc;
+  struct stat st;
+  
+  if (access (oldpath, F_OK))
+    return errno;
+  
+  if (stat (newpath, &st) == 0)
+    {
+      if (flags & MU_COPY_OVERWRITE)
+       {
+         if (S_ISDIR (st.st_mode))
+           {
+             if (mu_remove_file (newpath))
+               return MU_ERR_REMOVE_DEST;
+           }
+       }
+      else
+       return EEXIST;
+    }
+
+  if (rename (oldpath, newpath) == 0)
+    return 0;
+
+  if (errno == EXDEV)
+    {
+      mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+               (_("cannot rename %s to %s: %s"),
+                oldpath, newpath, mu_strerror (errno)));
+      mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_TRACE1,
+               (_("attempting copy")));
+      
+      rc = mu_copy_file (oldpath, newpath, flags|MU_COPY_MODE|MU_COPY_OWNER);
+      if (rc == 0)
+       {
+         rc = mu_remove_file (oldpath);
+         if (rc)
+           {
+             mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+                       (_("copied %s to %s, but failed to remove the source: 
%s"),
+                        oldpath, newpath, mu_strerror (rc)));
+             rc = MU_ERR_REMOVE_SOURCE;
+           }
+       }
+    }
+  else
+    rc = errno;
+  return rc;
+}
diff --git a/libmailutils/diag/errors b/libmailutils/diag/errors
index 4b394f4..c4fec20 100644
--- a/libmailutils/diag/errors
+++ b/libmailutils/diag/errors
@@ -125,3 +125,7 @@ MU_ERR_PERM_DIR_IWOTH       _("File in world writable 
directory")
 MU_ERR_DISABLED             _("Requested feature disabled in configuration")
 
 MU_ERR_FORMAT              _("Error in format string")
+
+MU_ERR_REMOVE_SOURCE       _("Failed to remove source file")
+MU_ERR_REMOVE_DEST         _("Failed to remove destination file")
+MU_ERR_RESTORE_META         _("Failed to restore ownership or mode")
diff --git a/libmailutils/string/Makefile.am b/libmailutils/string/Makefile.am
index 47e578e..4bcea5b 100644
--- a/libmailutils/string/Makefile.am
+++ b/libmailutils/string/Makefile.am
@@ -24,8 +24,10 @@ libstring_la_SOURCES = \
  cstrlower.c\
  cstrupper.c\
  hexstr.c\
+ safefilename.c\
  stpcpy.c\
  str_to_c.c\
+ strcount.c\
  strltrim.c\
  strskip.c\
  stripws.c\
diff --git a/libmailutils/string/safefilename.c 
b/libmailutils/string/safefilename.c
new file mode 100644
index 0000000..60c9104
--- /dev/null
+++ b/libmailutils/string/safefilename.c
@@ -0,0 +1,84 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+   Copyright (C) 2016 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/>. */
+
+#include <config.h>
+#include <mailutils/util.h>
+
+/* Return 1 if file name STR is safe, or 0 otherwise.
+   The name is not safe if it begins with .. or contains /../
+*/
+int
+mu_file_name_is_safe (char const *str)
+{
+  enum { st_init, st_slash, st_dot, st_dotdot } state;
+  unsigned char c;
+  int consume = 0;
+
+  if (!str)
+    return 0;
+  
+  state = (*str == '.') ? st_dot : st_init;
+
+  while ((c = *str++) != 0)
+    {
+      if (consume)
+       consume--;
+      else if (c < 0xc0)
+       {
+         switch (state)
+           {
+           case st_init:
+             if (c == '/')
+               state = st_slash;
+             break;
+             
+           case st_slash:
+             if (c == '.')
+               state = st_dot;
+             else if (c != '/')
+               state = st_init;
+             break;
+             
+           case st_dot:
+             if (c == '.')
+               state = st_dotdot;
+             else if (c == '/')
+               state = st_slash;
+             else
+               state = st_init;
+             break;
+             
+           case st_dotdot:
+             if (c == '/')
+               return 0;
+             else
+               state = st_init;
+             break;
+           }
+       }
+      else if (c & 0xc0)
+       consume = 1;
+      else if (c & 0xe0)
+       consume = 2;
+      else if (c & 0xf0)
+       consume = 3;
+    }
+
+  if (state == st_dotdot)
+    return 0;
+
+  return 1;
+}
diff --git a/include/mailutils/glob.h b/libmailutils/string/strcount.c
similarity index 55%
copy from include/mailutils/glob.h
copy to libmailutils/string/strcount.c
index 57992f6..4316c0a 100644
--- a/include/mailutils/glob.h
+++ b/libmailutils/string/strcount.c
@@ -14,22 +14,37 @@
    You should have received a copy of the GNU General Public License
    along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
 
-#ifndef _MAILUTILS_GLOB_H
-#define _MAILUTILS_GLOB_H
-
-#include <mailutils/types.h>
-#include <regex.h>
-
-/* Produce case-insensitive regex */
-#define MU_GLOBF_ICASE    0x01
-/* Treat each wildcard as regexp parenthesized group */
-#define MU_GLOBF_SUB      0x02
-/* When used with MU_GLOBF_SUB - collapse contiguous runs of * to single
-   asterisk */
-#define MU_GLOBF_COLLAPSE 0x04
-
-int mu_glob_to_regex_opool (char const *pattern, mu_opool_t pool, int flags);
-int mu_glob_to_regex (char **rxstr, char const *pattern, int flags);
-int mu_glob_compile (regex_t *rx, char const *pattern, int flags);
-
-#endif
+#include <config.h>
+#include <limits.h>
+#include <mailutils/util.h>
+
+/* Return the number of occurrences of the ASCII character CHR in the
+   UTF-8 string STR. */
+size_t
+mu_str_count (char const *str, int chr)
+{
+  unsigned char c;
+  size_t count = 0;
+  int consume = 0;
+  
+  if (!str || chr < 0 || chr > UCHAR_MAX)
+    return 0;
+  
+  while ((c = *str++) != 0)
+    {
+      if (consume)
+       consume--;
+      else if (c < 0xc0)
+       {
+         if (c == chr)
+           count++;
+       }
+      else if (c & 0xc0)
+       consume = 1;
+      else if (c & 0xe0)
+       consume = 2;
+      else if (c & 0xf0)
+       consume = 3;      
+    }
+  return count;
+}
diff --git a/mh/forw.c b/mh/forw.c
index 33d120f..cd053c0 100644
--- a/mh/forw.c
+++ b/mh/forw.c
@@ -436,7 +436,15 @@ main (int argc, char **argv)
   if (build_only || wh_env.nowhatnowproc)
     {
       if (strcmp (wh_env.file, wh_env.draftfile))
-       rename (wh_env.file, wh_env.draftfile);
+       {
+         rc = mu_rename_file (wh_env.file, wh_env.draftfile, 
MU_COPY_OVERWRITE);
+         if (rc)
+           {
+             mu_error (_("can't rename %s to %s: %s"),
+                       wh_env.file, wh_env.draftfile, mu_strerror (rc));
+             return 1;
+           }
+       }
       return 0;
     }
   
diff --git a/mh/mh_whatnow.c b/mh/mh_whatnow.c
index 95a403a..6b86648 100644
--- a/mh/mh_whatnow.c
+++ b/mh/mh_whatnow.c
@@ -425,7 +425,13 @@ quit (struct mh_whatnow_env *wh, int argc, char **argv, 
int *status)
        {
          mu_printf (_("draft left on \"%s\"."), wh->draftfile);
          if (strcmp (wh->file, wh->draftfile))
-           rename (wh->file, wh->draftfile);
+           {
+             int rc;
+             rc = mu_rename_file (wh->file, wh->draftfile, MU_COPY_OVERWRITE);
+             if (rc)
+               mu_error (_("can't rename %s to %s: %s"),
+                         wh->file, wh->draftfile, mu_strerror (rc));
+           }
        }
     }
   mu_printf ("\n");
diff --git a/mh/mhn.c b/mh/mhn.c
index 0d24011..79131b5 100644
--- a/mh/mhn.c
+++ b/mh/mhn.c
@@ -1204,7 +1204,7 @@ list_iterator (size_t num, mu_message_t msg, void *data)
 }
 
 int
-mhn_list ()
+mhn_list (void)
 {
   int rc; 
 
@@ -1329,7 +1329,7 @@ sigint (int sig)
 }
 
 static int
-mhn_pause ()
+mhn_pause (void)
 {
   char c;
   int rc = 0;
@@ -1438,7 +1438,7 @@ show_iterator (size_t num, mu_message_t msg, void *data)
 }
 
 int
-mhn_show ()
+mhn_show (void)
 {
   
   int rc;
@@ -1680,7 +1680,7 @@ store_iterator (size_t num, mu_message_t msg, void *data)
 }
 
 int
-mhn_store ()
+mhn_store (void)
 {
   int rc = 0;
   
@@ -2697,7 +2697,7 @@ mhn_header (mu_message_t msg, mu_message_t omsg)
 }
 
 int
-mhn_compose ()
+mhn_compose (void)
 {
   int rc;
   mu_mime_t mime = NULL;
@@ -2760,12 +2760,22 @@ mhn_compose ()
   
   /* Preserve the backup copy and replace the draft */
   unlink (backup);
-  rename (input_file, backup);
-  rename (name, input_file);
+
+  rc = mu_rename_file (input_file, backup, MU_COPY_OVERWRITE);
+  if (rc)
+    mu_error (_("can't rename %s to backup file %s: %s"),
+             input_file, backup, mu_strerror (rc));
+  else
+    {
+      rc = mu_rename_file (name, input_file, 0);
+      if (rc)
+       mu_error (_("can't rename %s to %s: %s"),
+                 name, input_file, mu_strerror (rc));
+    }
 
   free (name);
   mu_mime_unref (mime); 
-  return 0;
+  return rc;
 }
 
 
diff --git a/mh/send.c b/mh/send.c
index 99dd570..5bf5b4a 100644
--- a/mh/send.c
+++ b/mh/send.c
@@ -562,9 +562,13 @@ backup_file (const char *file_name)
 
   if (unlink (new_name) && errno != ENOENT)
     mu_diag_funcall (MU_DIAG_ERROR, "unlink", new_name, errno);
-  else if (rename (file_name, new_name))
-    mu_error (_("cannot rename `%s' to `%s': %s"),
-             file_name, new_name, mu_strerror (errno));
+  else
+    {
+      int rc = mu_rename_file (file_name, new_name, MU_COPY_OVERWRITE);
+      if (rc)
+       mu_error (_("cannot rename %s to %s: %s"),
+                 file_name, new_name, mu_strerror (errno));
+    }
   free (new_name);
 }
 


hooks/post-receive
-- 
GNU Mailutils



reply via email to

[Prev in Thread] Current Thread [Next in Thread]