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 ...@@ -5,6 +5,9 @@ base64
5 decode2047 5 decode2047
6 echosrv 6 echosrv
7 encode2047 7 encode2047
8 fcopy
9 fremove
10 frename
8 header 11 header
9 http 12 http
10 iconv 13 iconv
......
...@@ -30,6 +30,9 @@ noinst_PROGRAMS = \ ...@@ -30,6 +30,9 @@ noinst_PROGRAMS = \
30 addr\ 30 addr\
31 base64\ 31 base64\
32 echosrv\ 32 echosrv\
33 fcopy\
34 fremove\
35 frename\
33 header\ 36 header\
34 http\ 37 http\
35 iconv\ 38 iconv\
...@@ -44,7 +47,6 @@ noinst_PROGRAMS = \ ...@@ -44,7 +47,6 @@ noinst_PROGRAMS = \
44 murun\ 47 murun\
45 musocio\ 48 musocio\
46 $(NNTPCLIENT)\ 49 $(NNTPCLIENT)\
47 rename\
48 sa\ 50 sa\
49 sfrom 51 sfrom
50 52
......
1 #include <mailutils/mailutils.h> 1 #include <mailutils/mailutils.h>
2 2
3 int copy_option;
4 int owner_option; 3 int owner_option;
5 int mode_option; 4 int mode_option;
5 int force_option;
6 int deref_option;
6 7
7 static struct mu_option rename_options[] = { 8 static struct mu_option copy_options[] = {
8 { "copy", 'c', NULL, MU_OPTION_DEFAULT,
9 "copy the file",
10 mu_c_bool, &copy_option },
11 { "owner", 'u', NULL, MU_OPTION_DEFAULT, 9 { "owner", 'u', NULL, MU_OPTION_DEFAULT,
12 "copy ownership", 10 "copy ownership",
13 mu_c_bool, &owner_option }, 11 mu_c_bool, &owner_option },
14 { "mode", 'm', NULL, MU_OPTION_DEFAULT, 12 { "mode", 'm', NULL, MU_OPTION_DEFAULT,
15 "copy mode", 13 "copy mode",
16 mu_c_bool, &mode_option }, 14 mu_c_bool, &mode_option },
15 { "force", 'f', NULL, MU_OPTION_DEFAULT,
16 "force overwriting the destination file if it exists",
17 mu_c_bool, &force_option },
18 { "overwrite", 0, NULL, MU_OPTION_ALIAS },
19 { "dereference", 'h', NULL, MU_OPTION_DEFAULT,
20 "dereference symbolic links",
21 mu_c_bool, &deref_option },
17 MU_OPTION_END 22 MU_OPTION_END
18 }, *options[] = { rename_options, NULL }; 23 }, *options[] = { copy_options, NULL };
19 24
20 struct mu_cli_setup cli = { 25 struct mu_cli_setup cli = {
21 options, 26 options,
22 NULL, 27 NULL,
23 "copy or rename file", 28 "copy file",
24 "SRC DST" 29 "SRC DST"
25 }; 30 };
26 31
...@@ -33,6 +38,7 @@ int ...@@ -33,6 +38,7 @@ int
33 main (int argc, char **argv) 38 main (int argc, char **argv)
34 { 39 {
35 int rc; 40 int rc;
41 int flags;
36 42
37 mu_cli (argc, argv, &cli, capa, NULL, &argc, &argv); 43 mu_cli (argc, argv, &cli, capa, NULL, &argc, &argv);
38 44
...@@ -42,17 +48,14 @@ main (int argc, char **argv) ...@@ -42,17 +48,14 @@ main (int argc, char **argv)
42 return 1; 48 return 1;
43 } 49 }
44 50
45 if (copy_option) 51 flags = (owner_option ? MU_COPY_OWNER : 0)
46 { 52 | (mode_option ? MU_COPY_MODE : 0)
47 int flags = (owner_option ? MU_COPY_OWNER : 0) 53 | (force_option ? MU_COPY_OVERWRITE : 0)
48 | (mode_option ? MU_COPY_MODE : 0); 54 | (deref_option ? MU_COPY_DEREF : 0);
49 rc = mu_copy_file (argv[0], argv[1], flags); 55 rc = mu_copy_file (argv[0], argv[1], flags);
50 }
51 else
52 rc = mu_rename_file (argv[0], argv[1]);
53 56
54 if (rc) 57 if (rc)
55 mu_diag_funcall (MU_DIAG_ERROR, "mu_rename_file", NULL, rc); 58 mu_diag_funcall (MU_DIAG_ERROR, "mu_copy_file", NULL, rc);
56 59
57 return !!rc; 60 return !!rc;
58 } 61 }
......
1 #include <mailutils/mailutils.h>
2
3 int owner_option;
4 int mode_option;
5 int force_option;
6
7 static struct mu_option *options[] = { NULL };
8
9 struct mu_cli_setup cli = {
10 options,
11 NULL,
12 "delete file",
13 "FILE"
14 };
15
16 static char *capa[] = {
17 "debug",
18 NULL
19 };
20
21 int
22 main (int argc, char **argv)
23 {
24 int rc;
25
26 mu_cli (argc, argv, &cli, capa, NULL, &argc, &argv);
27
28 if (argc != 1)
29 {
30 mu_error ("wrong number of arguments");
31 return 1;
32 }
33
34 if (!mu_file_name_is_safe (argv[0])
35 || (argv[0][0] == '/' && mu_str_count (argv[0], '/') < 2))
36 {
37 mu_error ("unsafe file name");
38 return 1;
39 }
40
41 rc = mu_remove_file (argv[0]);
42
43 if (rc)
44 mu_diag_funcall (MU_DIAG_ERROR, "mu_remove_file", NULL, rc);
45
46 return !!rc;
47 }
48
49
1 #include <mailutils/mailutils.h>
2
3 int force_option;
4
5 static struct mu_option rename_options[] = {
6 { "force", 'f', NULL, MU_OPTION_DEFAULT,
7 "force overwriting the destination file if it exists",
8 mu_c_bool, &force_option },
9 { "overwrite", 0, NULL, MU_OPTION_ALIAS },
10 MU_OPTION_END
11 }, *options[] = { rename_options, NULL };
12
13 struct mu_cli_setup cli = {
14 options,
15 NULL,
16 "rename file",
17 "SRC DST"
18 };
19
20 static char *capa[] = {
21 "debug",
22 NULL
23 };
24
25 int
26 main (int argc, char **argv)
27 {
28 int rc;
29
30 mu_cli (argc, argv, &cli, capa, NULL, &argc, &argv);
31
32 if (argc != 2)
33 {
34 mu_error ("wrong number of arguments");
35 return 1;
36 }
37
38 if (!mu_file_name_is_safe (argv[0])
39 || (argv[0][0] == '/' && mu_str_count (argv[0], '/') < 2))
40 {
41 mu_error ("%s: unsafe file name", argv[0]);
42 return 1;
43 }
44 if (!mu_file_name_is_safe (argv[1])
45 || (argv[1][0] == '/' && mu_str_count (argv[1], '/') < 2))
46 {
47 mu_error ("%sunsafe file name", argv[0]);
48 return 1;
49 }
50
51 rc = mu_rename_file (argv[0], argv[1], force_option ? MU_COPY_OVERWRITE : 0);
52
53 if (rc)
54 mu_diag_funcall (MU_DIAG_ERROR, "mu_rename_file", NULL, rc);
55
56 return !!rc;
57 }
58
59
...@@ -125,8 +125,8 @@ imap4d_rename (struct imap4d_session *session, ...@@ -125,8 +125,8 @@ imap4d_rename (struct imap4d_session *session,
125 if (!newname) 125 if (!newname)
126 return io_completion_response (command, RESP_NO, "Permission denied"); 126 return io_completion_response (command, RESP_NO, "Permission denied");
127 127
128 /* It is an error to attempt to rename from a mailbox name that already 128 /* It is an error to attempt to rename from a mailbox name that does not
129 exist. */ 129 exist or to a mailbox name that already exists. */
130 if (stat (newname, &newst) == 0) 130 if (stat (newname, &newst) == 0)
131 { 131 {
132 /* FIXME: What if it's a maildir?!? */ 132 /* FIXME: What if it's a maildir?!? */
...@@ -216,9 +216,26 @@ imap4d_rename (struct imap4d_session *session, ...@@ -216,9 +216,26 @@ imap4d_rename (struct imap4d_session *session,
216 } 216 }
217 else 217 else
218 { 218 {
219 if (rename (oldname, newname) != 0) 219 rc = mu_rename_file (oldname, newname, 0);
220 if (rc)
220 { 221 {
221 mu_diag_funcall (MU_DIAG_ERROR, "rename", oldname, errno); 222 switch (rc)
223 {
224 case MU_ERR_REMOVE_SOURCE:
225 mu_error (_("failed to remove source mailbox after moving %s to %s"),
226 oldname, newname);
227 break;
228
229 case MU_ERR_RESTORE_META:
230 mu_error (_("failed to restore mailbox ownership/modes after moving %s to %s"),
231 oldname, newname);
232 break;
233
234 default:
235 mu_error (_("error renaming mailbox %s to %s: %s"),
236 oldname, newname, mu_strerror (rc));
237 }
238
222 rc = RESP_NO; 239 rc = RESP_NO;
223 msg = "Failed"; 240 msg = "Failed";
224 } 241 }
......
...@@ -45,6 +45,8 @@ char *mu_str_skip_cset_comp (const char *str, const char *cset); ...@@ -45,6 +45,8 @@ char *mu_str_skip_cset_comp (const char *str, const char *cset);
45 char *mu_str_stripws (char *string); 45 char *mu_str_stripws (char *string);
46 46
47 int mu_string_split (const char *string, char *delim, mu_list_t list); 47 int mu_string_split (const char *string, char *delim, mu_list_t list);
48
49 size_t mu_str_count (char const *str, int chr);
48 50
49 #ifdef __cplusplus 51 #ifdef __cplusplus
50 } 52 }
......
...@@ -140,8 +140,6 @@ struct mu_param ...@@ -140,8 +140,6 @@ struct mu_param
140 char *name; 140 char *name;
141 char *value; 141 char *value;
142 }; 142 };
143
144
145 143
146 int mu_content_type_parse (const char *input, mu_content_type_t *retct); 144 int mu_content_type_parse (const char *input, mu_content_type_t *retct);
147 void mu_content_type_destroy (mu_content_type_t *pptr); 145 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, ...@@ -207,17 +205,23 @@ int mu_str_to_c (char const *string, mu_c_type_t type, void *tgt,
207 /* -------------------------- */ 205 /* -------------------------- */
208 /* Safe file copy and rename */ 206 /* Safe file copy and rename */
209 /* -------------------------- */ 207 /* -------------------------- */
210 #define MU_COPY_MODE 0x01 208 /* Bits for the flags argument of mu_copy_file and mu_rename_file. The
211 #define MU_COPY_OWNER 0x02 209 MU_COPY_OVERWRITE is valid for both calls. The rest is for mu_copy_file
212 #define MU_COPY_SYMLINK 0x04 210 only */
213 #define MU_COPY_FORCE 0x08 211 #define MU_COPY_OVERWRITE 0x01 /* Overwrite destination file, if it exists */
212 #define MU_COPY_MODE 0x02 /* Preserve file mode */
213 #define MU_COPY_OWNER 0x04 /* Preserve file ownership */
214 #define MU_COPY_DEREF 0x08 /* Dereference the source file */
214 215
215 int mu_copy_file (const char *srcpath, const char *dstpath, int flags); 216 int mu_copy_file (const char *srcpath, const char *dstpath, int flags);
216 int mu_rename_file (const char *oldpath, const char *newpath); 217 int mu_rename_file (const char *oldpath, const char *newpath, int flags);
218 int mu_remove_file (const char *path);
217 219
218 /* ----------------------- */ 220 /* ----------------------- */
219 /* Assorted functions. */ 221 /* Assorted functions. */
220 /* ----------------------- */ 222 /* ----------------------- */
223 int mu_file_name_is_safe (char const *str);
224
221 int mu_getmaxfd (void); 225 int mu_getmaxfd (void);
222 /* Get the host name, doing a gethostbyname() if possible. */ 226 /* Get the host name, doing a gethostbyname() if possible. */
223 int mu_get_host_name (char **host); 227 int mu_get_host_name (char **host);
......
...@@ -59,6 +59,7 @@ libbase_la_SOURCES = \ ...@@ -59,6 +59,7 @@ libbase_la_SOURCES = \
59 registrar.c\ 59 registrar.c\
60 refcount.c\ 60 refcount.c\
61 renamefile.c\ 61 renamefile.c\
62 removefile.c\
62 rfc2047.c\ 63 rfc2047.c\
63 schemeauto.c\ 64 schemeauto.c\
64 sha1.c\ 65 sha1.c\
......
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2016 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Mailutils is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
16
1 #include <config.h> 17 #include <config.h>
2 #include <sys/types.h> 18 #include <sys/types.h>
3 #include <sys/stat.h> 19 #include <sys/stat.h>
...@@ -16,13 +32,24 @@ static int copy_regular_file (const char *srcpath, const char *dstpath, ...@@ -16,13 +32,24 @@ static int copy_regular_file (const char *srcpath, const char *dstpath,
16 static int copy_symlink (const char *srcpath, const char *dstpath); 32 static int copy_symlink (const char *srcpath, const char *dstpath);
17 static int copy_dir (const char *srcpath, const char *dstpath, int flags); 33 static int copy_dir (const char *srcpath, const char *dstpath, int flags);
18 34
35 /* Copy SRCPATH to DSTPATH. SRCPATH can be any kind of file. If it is
36 a directory, its content will be copied recursively.
37
38 FLAGS:
39
40 MU_COPY_OVERWRITE Overwrite destination file, if it exists.
41 MU_COPY_MODE Preserve file mode
42 MU_COPY_OWNER Preserve file ownership
43 MU_COPY_DEREF Dereference symbolic links: operate on files they
44 refer to.
45 */
19 int 46 int
20 mu_copy_file (const char *srcpath, const char *dstpath, int flags) 47 mu_copy_file (const char *srcpath, const char *dstpath, int flags)
21 { 48 {
22 int rc = 0; 49 int rc = 0;
23 struct stat st; 50 struct stat st;
24 51
25 if (((flags & MU_COPY_SYMLINK) ? lstat : stat) (srcpath, &st)) 52 if (((flags & MU_COPY_DEREF) ? stat : lstat) (srcpath, &st))
26 { 53 {
27 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, 54 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
28 (_("can't stat file %s: %s"), 55 (_("can't stat file %s: %s"),
...@@ -30,6 +57,23 @@ mu_copy_file (const char *srcpath, const char *dstpath, int flags) ...@@ -30,6 +57,23 @@ mu_copy_file (const char *srcpath, const char *dstpath, int flags)
30 return errno; 57 return errno;
31 } 58 }
32 59
60 if (access (dstpath, F_OK) == 0)
61 {
62 if (flags & MU_COPY_OVERWRITE)
63 {
64 rc = mu_remove_file (dstpath);
65 if (rc)
66 {
67 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
68 (_("can't remove destination %s: %s"),
69 dstpath, mu_strerror (rc)));
70 return rc;
71 }
72 }
73 else
74 return EEXIST;
75 }
76
33 switch (st.st_mode & S_IFMT) 77 switch (st.st_mode & S_IFMT)
34 { 78 {
35 case S_IFREG: 79 case S_IFREG:
...@@ -37,11 +81,9 @@ mu_copy_file (const char *srcpath, const char *dstpath, int flags) ...@@ -37,11 +81,9 @@ mu_copy_file (const char *srcpath, const char *dstpath, int flags)
37 81
38 case S_IFLNK: 82 case S_IFLNK:
39 return copy_symlink (srcpath, dstpath); 83 return copy_symlink (srcpath, dstpath);
40 break;
41 84
42 case S_IFDIR: 85 case S_IFDIR:
43 return copy_dir (srcpath, dstpath, flags); 86 return copy_dir (srcpath, dstpath, flags);
44 break;
45 87
46 case S_IFBLK: 88 case S_IFBLK:
47 case S_IFCHR: 89 case S_IFCHR:
...@@ -123,16 +165,16 @@ copy_regular_file (const char *srcpath, const char *dstpath, int flags, ...@@ -123,16 +165,16 @@ copy_regular_file (const char *srcpath, const char *dstpath, int flags,
123 { 165 {
124 if (fchmod ((int) trans[0], mode)) 166 if (fchmod ((int) trans[0], mode))
125 { 167 {
126 rc = errno;
127 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, 168 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
128 (_("%s: cannot chmod: %s"), 169 (_("%s: cannot chmod: %s"),
129 dstpath, mu_strerror (rc))); 170 dstpath, mu_strerror (errno)));
171 rc = MU_ERR_RESTORE_META;
130 } 172 }
131 else if (flags & MU_COPY_OWNER) 173 else if (flags & MU_COPY_OWNER)
132 { 174 {
133 uid_t uid; 175 uid_t uid;
134 gid_t gid; 176 gid_t gid;
135 177
136 if (getuid () == 0) 178 if (getuid () == 0)
137 { 179 {
138 uid = st->st_uid; 180 uid = st->st_uid;
...@@ -153,13 +195,13 @@ copy_regular_file (const char *srcpath, const char *dstpath, int flags, ...@@ -153,13 +195,13 @@ copy_regular_file (const char *srcpath, const char *dstpath, int flags,
153 { 195 {
154 if (fchown ((int) trans[0], uid, gid)) 196 if (fchown ((int) trans[0], uid, gid))
155 { 197 {
156 rc = errno;
157 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, 198 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
158 (_("%s: cannot chown to %lu.%lu: %s"), 199 (_("%s: cannot chown to %lu.%lu: %s"),
159 dstpath, 200 dstpath,
160 (unsigned long) uid, 201 (unsigned long) uid,
161 (unsigned long) gid, 202 (unsigned long) gid,
162 mu_strerror (rc))); 203 mu_strerror (errno)));
204 rc = MU_ERR_RESTORE_META;
163 } 205 }
164 } 206 }
165 } 207 }
...@@ -212,9 +254,8 @@ copy_dir (const char *srcpath, const char *dstpath, int flags) ...@@ -212,9 +254,8 @@ copy_dir (const char *srcpath, const char *dstpath, int flags)
212 { 254 {
213 DIR *dirp; 255 DIR *dirp;
214 struct dirent *dp; 256 struct dirent *dp;
215 struct stat st, st1; 257 struct stat st;
216 int rc; 258 int rc;
217 int create = 0;
218 mode_t mode, mask; 259 mode_t mode, mask;
219 260
220 if (stat (srcpath, &st)) 261 if (stat (srcpath, &st))
...@@ -225,67 +266,28 @@ copy_dir (const char *srcpath, const char *dstpath, int flags) ...@@ -225,67 +266,28 @@ copy_dir (const char *srcpath, const char *dstpath, int flags)
225 return errno; 266 return errno;
226 } 267 }
227 268
228 if (stat (dstpath, &st1))
229 {
230 if (errno == ENOENT)
231 create = 1;
232 else
233 {
234 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
235 (_("can't stat directory %s: %s"),
236 dstpath, mu_strerror (errno)));
237 return errno;
238 }
239 }
240 else if (!S_ISDIR (st1.st_mode))
241 {
242 if (flags & MU_COPY_FORCE)
243 {
244 if (unlink (dstpath))
245 {
246 rc = errno;
247 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
248 (_("%s is not a directory and cannot be unlinked: %s"),
249 dstpath, mu_strerror (rc)));
250 return rc;
251 }
252 create = 1;
253 }
254 else
255 {
256 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
257 (_("%s is not a directory"),
258 dstpath));
259 return EEXIST;
260 }
261 }
262
263 mask = umask (077); 269 mask = umask (077);
264 mode = ((flags & MU_COPY_MODE) ? st.st_mode : (0777 & ~mask)) & 0777; 270 mode = ((flags & MU_COPY_MODE) ? st.st_mode : (0777 & ~mask)) & 0777;
265 271
266 if (create) 272 rc = mkdir (dstpath, 0700);
267 { 273 umask (mask);
268 rc = mkdir (dstpath, 0700);
269 umask (mask);
270 274
271 if (rc) 275 if (rc)
272 { 276 {
273 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, 277 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
274 (_("can't create directory %s: %s"), 278 (_("can't create directory %s: %s"),
275 dstpath, mu_strerror (errno))); 279 dstpath, mu_strerror (errno)));
276 return errno; 280 return errno;
277 }
278 } 281 }
279 else
280 umask (mask);
281 282
282 dirp = opendir (srcpath); 283 dirp = opendir (srcpath);
283 if (dirp == NULL) 284 if (dirp == NULL)
284 { 285 {
285 mu_debug (MU_DEBCAT_FOLDER, MU_DEBUG_ERROR, 286 rc = errno;
287 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
286 ("cannot open directory %s: %s", 288 ("cannot open directory %s: %s",
287 srcpath, mu_strerror (errno))); 289 srcpath, mu_strerror (errno)));
288 return 1; 290 return rc;
289 } 291 }
290 292
291 while ((dp = readdir (dirp))) 293 while ((dp = readdir (dirp)))
......
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2016 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Mailutils is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <dirent.h>
23 #include <mailutils/stream.h>
24 #include <mailutils/util.h>
25 #include <mailutils/diag.h>
26 #include <mailutils/error.h>
27 #include <mailutils/errno.h>
28 #include <mailutils/list.h>
29 #include <mailutils/iterator.h>
30 #include <mailutils/nls.h>
31
32 static int removedir (const char *path);
33
34 int
35 mu_remove_file (const char *path)
36 {
37 int rc = 0;
38 struct stat st;
39
40 if (stat (path, &st))
41 {
42 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
43 (_("can't stat file %s: %s"),
44 path, mu_strerror (errno)));
45 return errno;
46 }
47
48 if (S_ISDIR (st.st_mode))
49 rc = removedir (path);
50 else if (unlink (path))
51 {
52 rc = errno;
53 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
54 (_("can't unlink file %s: %s"),
55 path, mu_strerror (rc)));
56 }
57
58 return rc;
59 }
60
61 struct nameent
62 {
63 int isdir;
64 char name[1];
65 };
66
67 static int
68 name_add (mu_list_t list, char const *name)
69 {
70 int rc;
71 size_t len = strlen (name);
72 struct nameent *ent = malloc (sizeof (*ent) + len);
73 if (!ent)
74 {
75 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
76 ("%s", mu_strerror (errno)));
77 return 1;
78 }
79 ent->isdir = -1;
80 strcpy (ent->name, name);
81 rc = mu_list_append (list, ent);
82 if (rc)
83 {
84 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
85 ("mu_list_append: %s", mu_strerror (rc)));
86 free (ent);
87 }
88
89 return rc;
90 }
91
92 static int
93 lsdir (const char *path, mu_list_t list)
94 {
95 DIR *dirp;
96 struct dirent *dp;
97 int rc = 0;
98
99 dirp = opendir (path);
100 if (dirp == NULL)
101 {
102 rc = errno;
103 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
104 ("cannot open directory %s: %s",
105 path, mu_strerror (errno)));
106 return rc;
107 }
108
109 while ((dp = readdir (dirp)))
110 {
111 char const *ename = dp->d_name;
112 char *filename;
113
114 if (ename[ename[0] != '.' ? 0 : ename[1] != '.' ? 1 : 2] == 0)
115 continue;
116
117 filename = mu_make_file_name (path, ename);
118 if (!filename)
119 {
120 rc = errno;
121 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
122 ("%s: can't create file name: %s",
123 path, mu_strerror (errno)));
124 break;
125 }
126
127 rc = name_add (list, filename);
128 free (filename);
129 if (rc)
130 break;
131 }
132
133 closedir (dirp);
134
135 return rc;
136 }
137
138 static int
139 namecmp (const void *a, const void *b)
140 {
141 struct nameent const *enta = a;
142 struct nameent const *entb = b;
143 int d = enta->isdir - entb->isdir;
144 if (d)
145 return d;
146 return strcmp (entb->name, enta->name);
147 }
148
149 static int
150 check_parent_access (const char *path)
151 {
152 int rc;
153 char *name, *p;
154
155 name = strdup (path);
156 if (!name)
157 return errno;
158 p = strrchr (name, '/');
159 if (p)
160 *p = 0;
161 else
162 strcpy (name, ".");
163 rc = access (name, R_OK|W_OK|X_OK);
164 free (name);
165 if (rc)
166 {
167 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
168 (_("not enough privileges to remove files from %s"),
169 name));
170 return EACCES;
171 }
172
173 return 0;
174 }
175
176 static int
177 removedir (const char *path)
178 {
179 int rc;
180 struct stat st;
181 mu_list_t namelist;
182 mu_iterator_t itr;
183 struct nameent *ent;
184
185 rc = check_parent_access (path);
186 if (rc)
187 return rc;
188
189 rc = mu_list_create (&namelist);
190 if (rc)
191 {
192 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
193 ("mu_list_create: %s", mu_strerror (rc)));
194 return rc;
195 }
196 mu_list_set_destroy_item (namelist, mu_list_free_item);
197 mu_list_set_comparator (namelist, namecmp);
198
199 rc = name_add (namelist, path);
200 if (rc)
201 {
202 mu_list_destroy (&namelist);
203 return rc;
204 }
205
206 rc = mu_list_get_iterator (namelist, &itr);
207 if (rc)
208 {
209 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
210 ("mu_list_get_iterator: %s", mu_strerror (rc)));
211 mu_list_destroy (&namelist);
212 return rc;
213 }
214
215 for (mu_iterator_first (itr);
216 !mu_iterator_is_done (itr);
217 mu_iterator_next (itr))
218 {
219 mu_iterator_current (itr, (void **)&ent);
220
221 if (lstat (ent->name, &st))
222 {
223 rc = errno;
224 if (rc == ENOENT)
225 continue;
226 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
227 (_("can't lstat file %s: %s"),
228 ent->name, mu_strerror (rc)));
229 break;
230 }
231
232 if (S_ISDIR (st.st_mode))
233 {
234 ent->isdir = 1;
235 if (access (ent->name, R_OK|W_OK|X_OK))
236 {
237 rc = EACCES;
238 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
239 (_("not enough privileges to remove files from %s"),
240 ent->name));
241 }
242 else
243 rc = lsdir (ent->name, namelist);
244 if (rc)
245 break;
246 }
247 else
248 ent->isdir = 0;
249 }
250
251 if (rc == 0)
252 {
253 mu_list_sort (namelist, namecmp);
254
255 for (mu_iterator_first (itr);
256 !mu_iterator_is_done (itr);
257 mu_iterator_next (itr))
258 {
259 mu_iterator_current (itr, (void **)&ent);
260 rc = (ent->isdir ? rmdir : unlink) (ent->name);
261 if (rc)
262 {
263 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
264 (_("can't remove %s: %s"),
265 ent->name, mu_strerror (rc)));
266 }
267 }
268 }
269 mu_iterator_destroy (&itr);
270 mu_list_destroy (&namelist);
271
272 return rc;
273 }
274
275
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2016 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Mailutils is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
16
1 #include <config.h> 17 #include <config.h>
2 #include <stdio.h> 18 #include <stdio.h>
3 #include <unistd.h> 19 #include <unistd.h>
20 #include <sys/stat.h>
4 #include <mailutils/stream.h> 21 #include <mailutils/stream.h>
5 #include <mailutils/util.h> 22 #include <mailutils/util.h>
6 #include <mailutils/diag.h> 23 #include <mailutils/diag.h>
...@@ -8,10 +25,36 @@ ...@@ -8,10 +25,36 @@
8 #include <mailutils/errno.h> 25 #include <mailutils/errno.h>
9 #include <mailutils/nls.h> 26 #include <mailutils/nls.h>
10 27
28 /* Rename file OLDPATH to NEWPATH. Prefer rename(2), unless OLDPATH
29 and NEWPATH reside on different devices. In the latter case, fall
30 back to recursive copying.
31
32 FLAGS controls what to do if NEWPATH exists. If it has MU_COPY_OVERWRITE
33 bit set, the NEWPATH will be overwritten (if possible, atomically).
34 Otherwise EEXIST will be returned.
35 */
11 int 36 int
12 mu_rename_file (const char *oldpath, const char *newpath) 37 mu_rename_file (const char *oldpath, const char *newpath, int flags)
13 { 38 {
14 int rc; 39 int rc;
40 struct stat st;
41
42 if (access (oldpath, F_OK))
43 return errno;
44
45 if (stat (newpath, &st) == 0)
46 {
47 if (flags & MU_COPY_OVERWRITE)
48 {
49 if (S_ISDIR (st.st_mode))
50 {
51 if (mu_remove_file (newpath))
52 return MU_ERR_REMOVE_DEST;
53 }
54 }
55 else
56 return EEXIST;
57 }
15 58
16 if (rename (oldpath, newpath) == 0) 59 if (rename (oldpath, newpath) == 0)
17 return 0; 60 return 0;
...@@ -24,16 +67,20 @@ mu_rename_file (const char *oldpath, const char *newpath) ...@@ -24,16 +67,20 @@ mu_rename_file (const char *oldpath, const char *newpath)
24 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_TRACE1, 67 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_TRACE1,
25 (_("attempting copy"))); 68 (_("attempting copy")));
26 69
27 rc = mu_copy_file (oldpath, newpath, MU_COPY_MODE|MU_COPY_OWNER); 70 rc = mu_copy_file (oldpath, newpath, flags|MU_COPY_MODE|MU_COPY_OWNER);
28 if (rc == 0) 71 if (rc == 0)
29 { 72 {
30 if (unlink (oldpath)) 73 rc = mu_remove_file (oldpath);
74 if (rc)
31 { 75 {
32 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR, 76 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
33 (_("copied %s to %s, but failed to remove the source: %s"), 77 (_("copied %s to %s, but failed to remove the source: %s"),
34 oldpath, newpath, mu_strerror (errno))); 78 oldpath, newpath, mu_strerror (rc)));
79 rc = MU_ERR_REMOVE_SOURCE;
35 } 80 }
36 } 81 }
37 } 82 }
83 else
84 rc = errno;
38 return rc; 85 return rc;
39 } 86 }
......
...@@ -125,3 +125,7 @@ MU_ERR_PERM_DIR_IWOTH _("File in world writable directory") ...@@ -125,3 +125,7 @@ MU_ERR_PERM_DIR_IWOTH _("File in world writable directory")
125 MU_ERR_DISABLED _("Requested feature disabled in configuration") 125 MU_ERR_DISABLED _("Requested feature disabled in configuration")
126 126
127 MU_ERR_FORMAT _("Error in format string") 127 MU_ERR_FORMAT _("Error in format string")
128
129 MU_ERR_REMOVE_SOURCE _("Failed to remove source file")
130 MU_ERR_REMOVE_DEST _("Failed to remove destination file")
131 MU_ERR_RESTORE_META _("Failed to restore ownership or mode")
......
...@@ -24,8 +24,10 @@ libstring_la_SOURCES = \ ...@@ -24,8 +24,10 @@ libstring_la_SOURCES = \
24 cstrlower.c\ 24 cstrlower.c\
25 cstrupper.c\ 25 cstrupper.c\
26 hexstr.c\ 26 hexstr.c\
27 safefilename.c\
27 stpcpy.c\ 28 stpcpy.c\
28 str_to_c.c\ 29 str_to_c.c\
30 strcount.c\
29 strltrim.c\ 31 strltrim.c\
30 strskip.c\ 32 strskip.c\
31 stripws.c\ 33 stripws.c\
......
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2016 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Mailutils is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18 #include <mailutils/util.h>
19
20 /* Return 1 if file name STR is safe, or 0 otherwise.
21 The name is not safe if it begins with .. or contains /../
22 */
23 int
24 mu_file_name_is_safe (char const *str)
25 {
26 enum { st_init, st_slash, st_dot, st_dotdot } state;
27 unsigned char c;
28 int consume = 0;
29
30 if (!str)
31 return 0;
32
33 state = (*str == '.') ? st_dot : st_init;
34
35 while ((c = *str++) != 0)
36 {
37 if (consume)
38 consume--;
39 else if (c < 0xc0)
40 {
41 switch (state)
42 {
43 case st_init:
44 if (c == '/')
45 state = st_slash;
46 break;
47
48 case st_slash:
49 if (c == '.')
50 state = st_dot;
51 else if (c != '/')
52 state = st_init;
53 break;
54
55 case st_dot:
56 if (c == '.')
57 state = st_dotdot;
58 else if (c == '/')
59 state = st_slash;
60 else
61 state = st_init;
62 break;
63
64 case st_dotdot:
65 if (c == '/')
66 return 0;
67 else
68 state = st_init;
69 break;
70 }
71 }
72 else if (c & 0xc0)
73 consume = 1;
74 else if (c & 0xe0)
75 consume = 2;
76 else if (c & 0xf0)
77 consume = 3;
78 }
79
80 if (state == st_dotdot)
81 return 0;
82
83 return 1;
84 }
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2016 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Mailutils is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18 #include <limits.h>
19 #include <mailutils/util.h>
20
21 /* Return the number of occurrences of the ASCII character CHR in the
22 UTF-8 string STR. */
23 size_t
24 mu_str_count (char const *str, int chr)
25 {
26 unsigned char c;
27 size_t count = 0;
28 int consume = 0;
29
30 if (!str || chr < 0 || chr > UCHAR_MAX)
31 return 0;
32
33 while ((c = *str++) != 0)
34 {
35 if (consume)
36 consume--;
37 else if (c < 0xc0)
38 {
39 if (c == chr)
40 count++;
41 }
42 else if (c & 0xc0)
43 consume = 1;
44 else if (c & 0xe0)
45 consume = 2;
46 else if (c & 0xf0)
47 consume = 3;
48 }
49 return count;
50 }
...@@ -436,7 +436,15 @@ main (int argc, char **argv) ...@@ -436,7 +436,15 @@ main (int argc, char **argv)
436 if (build_only || wh_env.nowhatnowproc) 436 if (build_only || wh_env.nowhatnowproc)
437 { 437 {
438 if (strcmp (wh_env.file, wh_env.draftfile)) 438 if (strcmp (wh_env.file, wh_env.draftfile))
439 rename (wh_env.file, wh_env.draftfile); 439 {
440 rc = mu_rename_file (wh_env.file, wh_env.draftfile, MU_COPY_OVERWRITE);
441 if (rc)
442 {
443 mu_error (_("can't rename %s to %s: %s"),
444 wh_env.file, wh_env.draftfile, mu_strerror (rc));
445 return 1;
446 }
447 }
440 return 0; 448 return 0;
441 } 449 }
442 450
......
...@@ -425,7 +425,13 @@ quit (struct mh_whatnow_env *wh, int argc, char **argv, int *status) ...@@ -425,7 +425,13 @@ quit (struct mh_whatnow_env *wh, int argc, char **argv, int *status)
425 { 425 {
426 mu_printf (_("draft left on \"%s\"."), wh->draftfile); 426 mu_printf (_("draft left on \"%s\"."), wh->draftfile);
427 if (strcmp (wh->file, wh->draftfile)) 427 if (strcmp (wh->file, wh->draftfile))
428 rename (wh->file, wh->draftfile); 428 {
429 int rc;
430 rc = mu_rename_file (wh->file, wh->draftfile, MU_COPY_OVERWRITE);
431 if (rc)
432 mu_error (_("can't rename %s to %s: %s"),
433 wh->file, wh->draftfile, mu_strerror (rc));
434 }
429 } 435 }
430 } 436 }
431 mu_printf ("\n"); 437 mu_printf ("\n");
......
...@@ -1204,7 +1204,7 @@ list_iterator (size_t num, mu_message_t msg, void *data) ...@@ -1204,7 +1204,7 @@ list_iterator (size_t num, mu_message_t msg, void *data)
1204 } 1204 }
1205 1205
1206 int 1206 int
1207 mhn_list () 1207 mhn_list (void)
1208 { 1208 {
1209 int rc; 1209 int rc;
1210 1210
...@@ -1329,7 +1329,7 @@ sigint (int sig) ...@@ -1329,7 +1329,7 @@ sigint (int sig)
1329 } 1329 }
1330 1330
1331 static int 1331 static int
1332 mhn_pause () 1332 mhn_pause (void)
1333 { 1333 {
1334 char c; 1334 char c;
1335 int rc = 0; 1335 int rc = 0;
...@@ -1438,7 +1438,7 @@ show_iterator (size_t num, mu_message_t msg, void *data) ...@@ -1438,7 +1438,7 @@ show_iterator (size_t num, mu_message_t msg, void *data)
1438 } 1438 }
1439 1439
1440 int 1440 int
1441 mhn_show () 1441 mhn_show (void)
1442 { 1442 {
1443 1443
1444 int rc; 1444 int rc;
...@@ -1680,7 +1680,7 @@ store_iterator (size_t num, mu_message_t msg, void *data) ...@@ -1680,7 +1680,7 @@ store_iterator (size_t num, mu_message_t msg, void *data)
1680 } 1680 }
1681 1681
1682 int 1682 int
1683 mhn_store () 1683 mhn_store (void)
1684 { 1684 {
1685 int rc = 0; 1685 int rc = 0;
1686 1686
...@@ -2697,7 +2697,7 @@ mhn_header (mu_message_t msg, mu_message_t omsg) ...@@ -2697,7 +2697,7 @@ mhn_header (mu_message_t msg, mu_message_t omsg)
2697 } 2697 }
2698 2698
2699 int 2699 int
2700 mhn_compose () 2700 mhn_compose (void)
2701 { 2701 {
2702 int rc; 2702 int rc;
2703 mu_mime_t mime = NULL; 2703 mu_mime_t mime = NULL;
...@@ -2760,12 +2760,22 @@ mhn_compose () ...@@ -2760,12 +2760,22 @@ mhn_compose ()
2760 2760
2761 /* Preserve the backup copy and replace the draft */ 2761 /* Preserve the backup copy and replace the draft */
2762 unlink (backup); 2762 unlink (backup);
2763 rename (input_file, backup); 2763
2764 rename (name, input_file); 2764 rc = mu_rename_file (input_file, backup, MU_COPY_OVERWRITE);
2765 if (rc)
2766 mu_error (_("can't rename %s to backup file %s: %s"),
2767 input_file, backup, mu_strerror (rc));
2768 else
2769 {
2770 rc = mu_rename_file (name, input_file, 0);
2771 if (rc)
2772 mu_error (_("can't rename %s to %s: %s"),
2773 name, input_file, mu_strerror (rc));
2774 }
2765 2775
2766 free (name); 2776 free (name);
2767 mu_mime_unref (mime); 2777 mu_mime_unref (mime);
2768 return 0; 2778 return rc;
2769 } 2779 }
2770 2780
2771 2781
......
...@@ -562,9 +562,13 @@ backup_file (const char *file_name) ...@@ -562,9 +562,13 @@ backup_file (const char *file_name)
562 562
563 if (unlink (new_name) && errno != ENOENT) 563 if (unlink (new_name) && errno != ENOENT)
564 mu_diag_funcall (MU_DIAG_ERROR, "unlink", new_name, errno); 564 mu_diag_funcall (MU_DIAG_ERROR, "unlink", new_name, errno);
565 else if (rename (file_name, new_name)) 565 else
566 mu_error (_("cannot rename `%s' to `%s': %s"), 566 {
567 file_name, new_name, mu_strerror (errno)); 567 int rc = mu_rename_file (file_name, new_name, MU_COPY_OVERWRITE);
568 if (rc)
569 mu_error (_("cannot rename %s to %s: %s"),
570 file_name, new_name, mu_strerror (errno));
571 }
568 free (new_name); 572 free (new_name);
569 } 573 }
570 574
......