commit-mailutils
[Top][All Lists]
Advanced

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

[SCM] GNU Mailutils branch, safe-rename, updated. release-3.1-9-g9ab204f


From: Sergey Poznyakoff
Subject: [SCM] GNU Mailutils branch, safe-rename, updated. release-3.1-9-g9ab204f
Date: Sat, 17 Dec 2016 09:30:06 +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=9ab204f57de0883f1f876a2b58f3359ab606fd0a

The branch, safe-rename has been updated
       via  9ab204f57de0883f1f876a2b58f3359ab606fd0a (commit)
      from  f0461be42ccc00ea751e93abc6eec51e455e925b (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 9ab204f57de0883f1f876a2b58f3359ab606fd0a
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.

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

Summary of changes:
 examples/.gitignore                                |    3 +
 examples/Makefile.am                               |    4 +-
 examples/fcopy.c                                   |   63 +++++
 examples/fremove.c                                 |   49 ++++
 examples/frename.c                                 |   59 +++++
 examples/rename.c                                  |   60 -----
 imap4d/rename.c                                    |   25 +-
 include/mailutils/cstr.h                           |    2 +
 include/mailutils/util.h                           |   18 +-
 libmailutils/base/Makefile.am                      |    1 +
 libmailutils/base/copyfile.c                       |  122 ++++-----
 libmailutils/base/removefile.c                     |  275 ++++++++++++++++++++
 libmailutils/base/renamefile.c                     |   55 +++-
 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 +-
 21 files changed, 765 insertions(+), 168 deletions(-)
 create mode 100644 examples/fcopy.c
 create mode 100644 examples/fremove.c
 create mode 100644 examples/frename.c
 delete mode 100644 examples/rename.c
 create mode 100644 libmailutils/base/removefile.c
 create mode 100644 libmailutils/string/safefilename.c
 copy include/mailutils/glob.h => libmailutils/string/strcount.c (55%)

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 ca5e094..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\
@@ -44,7 +47,6 @@ noinst_PROGRAMS = \
  murun\
  musocio\
  $(NNTPCLIENT)\
- rename\
  sa\
  sfrom
 
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/examples/rename.c b/examples/rename.c
deleted file mode 100644
index f12264a..0000000
--- a/examples/rename.c
+++ /dev/null
@@ -1,60 +0,0 @@
-#include <mailutils/mailutils.h>
-
-int copy_option;
-int owner_option;
-int mode_option;
-
-static struct mu_option rename_options[] = {
-  { "copy", 'c', NULL, MU_OPTION_DEFAULT,
-    "copy the file",
-    mu_c_bool, &copy_option },
-  { "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 },
-  MU_OPTION_END
-}, *options[] = { rename_options, NULL };
-  
-struct mu_cli_setup cli = {
-  options,
-  NULL,
-  "copy or 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 (copy_option)
-    {
-      int flags = (owner_option ? MU_COPY_OWNER : 0)
-               | (mode_option ? MU_COPY_MODE : 0);
-      rc = mu_copy_file (argv[0], argv[1], flags);
-    }
-  else
-    rc = mu_rename_file (argv[0], argv[1]);
-
-  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 23792e2..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);
@@ -207,17 +205,23 @@ int mu_str_to_c (char const *string, mu_c_type_t type, 
void *tgt,
   /* -------------------------- */
   /* Safe file copy and rename  */
   /* -------------------------- */
-#define MU_COPY_MODE    0x01
-#define MU_COPY_OWNER   0x02
-#define MU_COPY_SYMLINK 0x04
-#define MU_COPY_FORCE   0x08
+/* 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 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 e16fb44..24f08ba 100644
--- a/libmailutils/base/Makefile.am
+++ b/libmailutils/base/Makefile.am
@@ -59,6 +59,7 @@ libbase_la_SOURCES = \
  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
index f65d59c..ff03a4b 100644
--- a/libmailutils/base/copyfile.c
+++ b/libmailutils/base/copyfile.c
@@ -1,3 +1,19 @@
+/* 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>
@@ -16,13 +32,24 @@ static int copy_regular_file (const char *srcpath, const 
char *dstpath,
 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_SYMLINK) ? lstat : stat) (srcpath, &st))
+  if (((flags & MU_COPY_DEREF) ? stat : lstat) (srcpath, &st))
     {
       mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
                (_("can't stat file %s: %s"),
@@ -30,6 +57,23 @@ mu_copy_file (const char *srcpath, const char *dstpath, int 
flags)
       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:
@@ -37,11 +81,9 @@ mu_copy_file (const char *srcpath, const char *dstpath, int 
flags)
 
     case S_IFLNK:
       return copy_symlink (srcpath, dstpath);
-      break;
 
     case S_IFDIR:
       return copy_dir (srcpath, dstpath, flags);
-      break;
 
     case S_IFBLK:
     case S_IFCHR:
@@ -123,16 +165,16 @@ copy_regular_file (const char *srcpath, const char 
*dstpath, int flags,
        {           
          if (fchmod ((int) trans[0], mode))
            {
-             rc = errno;
              mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
                        (_("%s: cannot chmod: %s"),
-                        dstpath, mu_strerror (rc)));
+                        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;
@@ -153,13 +195,13 @@ copy_regular_file (const char *srcpath, const char 
*dstpath, int flags,
                {
                  if (fchown ((int) trans[0], 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)));
+                                mu_strerror (errno)));
+                     rc = MU_ERR_RESTORE_META;
                    }
                }
            }
@@ -212,9 +254,8 @@ copy_dir (const char *srcpath, const char *dstpath, int 
flags)
 {
   DIR *dirp;
   struct dirent *dp;
-  struct stat st, st1;
+  struct stat st;
   int rc;
-  int create = 0;
   mode_t mode, mask;
   
   if (stat (srcpath, &st))
@@ -225,67 +266,28 @@ copy_dir (const char *srcpath, const char *dstpath, int 
flags)
       return errno;
     }
 
-  if (stat (dstpath, &st1))
-    {
-      if (errno == ENOENT)
-       create = 1;
-      else
-       {
-         mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
-                   (_("can't stat directory %s: %s"),
-                    dstpath, mu_strerror (errno)));
-         return errno;
-       }
-    }
-  else if (!S_ISDIR (st1.st_mode))
-    {
-      if (flags & MU_COPY_FORCE)
-       {
-         if (unlink (dstpath))
-           {
-             rc = errno;
-             mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
-                       (_("%s is not a directory and cannot be unlinked: %s"),
-                        dstpath, mu_strerror (rc)));
-             return rc;
-           }
-         create = 1;
-       }
-      else
-       {
-         mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
-                   (_("%s is not a directory"),
-                    dstpath));
-         return EEXIST;
-       }
-    }      
-
   mask = umask (077);
   mode = ((flags & MU_COPY_MODE) ? st.st_mode : (0777 & ~mask)) & 0777;
   
-  if (create)
-    {
-      rc = mkdir (dstpath, 0700);
-      umask (mask);
+  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;
-       }
+  if (rc)
+    {
+      mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
+               (_("can't create directory %s: %s"),
+                dstpath, mu_strerror (errno)));
+      return errno;
     }
-  else
-    umask (mask);
   
   dirp = opendir (srcpath);
   if (dirp == NULL)
     {
-      mu_debug (MU_DEBCAT_FOLDER, MU_DEBUG_ERROR,
+      rc = errno;
+      mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
                ("cannot open directory %s: %s",
                 srcpath, mu_strerror (errno)));
-      return 1;
+      return rc;
     }
 
   while ((dp = readdir (dirp)))
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
index 5bc0bef..a791619 100644
--- a/libmailutils/base/renamefile.c
+++ b/libmailutils/base/renamefile.c
@@ -1,6 +1,23 @@
+/* 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>
@@ -8,10 +25,36 @@
 #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)
+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;
@@ -24,16 +67,20 @@ mu_rename_file (const char *oldpath, const char *newpath)
       mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_TRACE1,
                (_("attempting copy")));
       
-      rc = mu_copy_file (oldpath, newpath, MU_COPY_MODE|MU_COPY_OWNER);
+      rc = mu_copy_file (oldpath, newpath, flags|MU_COPY_MODE|MU_COPY_OWNER);
       if (rc == 0)
        {
-         if (unlink (oldpath))
+         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 (errno)));
+                        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]