Commit 7324f969 7324f9691545126806bbe219607248c7ad854852 by Sergey Poznyakoff

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.
1 parent e87f373e
......@@ -5,6 +5,9 @@ base64
decode2047
echosrv
encode2047
fcopy
fremove
frename
header
http
iconv
......
......@@ -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
......
#include <mailutils/mailutils.h>
int copy_option;
int owner_option;
int mode_option;
int force_option;
int deref_option;
static struct mu_option rename_options[] = {
{ "copy", 'c', NULL, MU_OPTION_DEFAULT,
"copy the file",
mu_c_bool, &copy_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[] = { rename_options, NULL };
}, *options[] = { copy_options, NULL };
struct mu_cli_setup cli = {
options,
NULL,
"copy or rename file",
"copy file",
"SRC DST"
};
......@@ -33,6 +38,7 @@ int
main (int argc, char **argv)
{
int rc;
int flags;
mu_cli (argc, argv, &cli, capa, NULL, &argc, &argv);
......@@ -42,17 +48,14 @@ main (int argc, char **argv)
return 1;
}
if (copy_option)
{
int flags = (owner_option ? MU_COPY_OWNER : 0)
| (mode_option ? MU_COPY_MODE : 0);
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);
}
else
rc = mu_rename_file (argv[0], argv[1]);
if (rc)
mu_diag_funcall (MU_DIAG_ERROR, "mu_rename_file", NULL, rc);
mu_diag_funcall (MU_DIAG_ERROR, "mu_copy_file", NULL, rc);
return !!rc;
}
......
#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;
}
#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;
}
......@@ -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";
}
......
......@@ -46,6 +46,8 @@ 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
}
#endif
......
......@@ -141,8 +141,6 @@ struct mu_param
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);
......
......@@ -59,6 +59,7 @@ libbase_la_SOURCES = \
registrar.c\
refcount.c\
renamefile.c\
removefile.c\
rfc2047.c\
schemeauto.c\
sha1.c\
......
/* 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,10 +165,10 @@ 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)
{
......@@ -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,46 +266,9 @@ 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);
......@@ -275,17 +279,15 @@ copy_dir (const char *srcpath, const char *dstpath, int flags)
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)))
......
/* 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;
}
/* 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;
}
......
......@@ -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")
......
......@@ -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\
......
/* 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;
}
/* 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 <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;
}
......@@ -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;
}
......
......@@ -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");
......
......@@ -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;
}
......
......@@ -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"),
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);
}
......