2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4 * Copyright (C) 1999-2000 Thomas Roessler <roessler@does-not-exist.org>
6 * This file is part of mutt-ng, see http://www.muttng.org/.
7 * It's licensed under the GNU General Public License,
8 * please see the file GPL in the top level source directory.
12 * This file used to contain some more functions, namely those
13 * which are now in muttlib.c. They have been removed, so we have
14 * some of our "standard" functions in external programs, too.
35 #include "lib/debug.h"
39 void mutt_nocurses_error (const char *fmt, ...)
44 vfprintf (stderr, fmt, ap);
49 int safe_fclose (FILE ** f)
60 void mutt_unlink (const char *s)
68 /* Defend against symlink attacks */
71 flags = O_RDWR | O_NOFOLLOW;
76 if (lstat (s, &sb) == 0 && S_ISREG (sb.st_mode)) {
77 if ((fd = open (s, flags)) < 0)
80 if ((fstat (fd, &sb2) != 0) || !S_ISREG (sb2.st_mode)
81 || (sb.st_dev != sb2.st_dev) || (sb.st_ino != sb2.st_ino)) {
86 if ((f = fdopen (fd, "r+"))) {
88 memset (buf, 0, sizeof (buf));
89 while (sb.st_size > 0) {
90 fwrite (buf, 1, MIN (sizeof (buf), sb.st_size), f);
91 sb.st_size -= MIN (sizeof (buf), sb.st_size);
98 int mutt_copy_bytes (FILE * in, FILE * out, size_t size)
104 chunk = (size > sizeof (buf)) ? sizeof (buf) : size;
105 if ((chunk = fread (buf, 1, chunk, in)) < 1)
107 if (fwrite (buf, 1, chunk, out) != chunk) {
108 debug_print (1, ("fwrite() returned short byte count\n"));
117 int mutt_copy_stream (FILE * fin, FILE * fout)
120 char buf[LONG_STRING];
122 while ((l = fread (buf, 1, sizeof (buf), fin)) > 0) {
123 if (fwrite (buf, 1, l, fout) != l)
130 static int compare_stat (struct stat *osb, struct stat *nsb)
132 if (osb->st_dev != nsb->st_dev || osb->st_ino != nsb->st_ino ||
133 osb->st_rdev != nsb->st_rdev) {
140 int safe_symlink (const char *oldpath, const char *newpath)
142 struct stat osb, nsb;
144 if (!oldpath || !newpath)
147 if (unlink (newpath) == -1 && errno != ENOENT)
150 if (oldpath[0] == '/') {
151 if (symlink (oldpath, newpath) == -1)
155 char abs_oldpath[_POSIX_PATH_MAX];
157 if ((getcwd (abs_oldpath, sizeof abs_oldpath) == NULL) ||
158 (safe_strlen (abs_oldpath) + 1 + safe_strlen (oldpath) + 1 >
162 strcat (abs_oldpath, "/"); /* __STRCAT_CHECKED__ */
163 strcat (abs_oldpath, oldpath); /* __STRCAT_CHECKED__ */
164 if (symlink (abs_oldpath, newpath) == -1)
168 if (stat (oldpath, &osb) == -1 || stat (newpath, &nsb) == -1
169 || compare_stat (&osb, &nsb) == -1) {
178 * This function is supposed to do nfs-safe renaming of files.
180 * Warning: We don't check whether src and target are equal.
183 int safe_rename (const char *src, const char *target)
185 struct stat ssb, tsb;
190 if (link (src, target) != 0) {
193 * Coda does not allow cross-directory links, but tells
194 * us it's a cross-filesystem linking attempt.
196 * However, the Coda rename call is allegedly safe to use.
198 * With other file systems, rename should just fail when
199 * the files reside on different file systems, so it's safe
205 return rename (src, target);
211 * Stat both links and check if they are equal.
214 if (stat (src, &ssb) == -1) {
218 if (stat (target, &tsb) == -1) {
223 * pretend that the link failed because the target file
227 if (compare_stat (&ssb, &tsb) == -1) {
233 * Unlink the original link. Should we really ignore the return
242 int safe_open (const char *path, int flags)
244 struct stat osb, nsb;
248 if ((fd = open (path, flags, 0666)) < 0)
251 /* make sure the file is not symlink */
252 if (lstat (path, &osb) < 0 || fstat (fd, &nsb) < 0 ||
253 compare_stat (&osb, &nsb) == -1) {
254 debug_print (1, ("%s is a symlink!\n", path));
262 /* when opening files for writing, make sure the file doesn't already exist
263 * to avoid race conditions.
265 FILE *safe_fopen (const char *path, const char *mode)
267 /* first set the current umask */
269 if (mode[0] == 'w') {
271 int flags = O_CREAT | O_EXCL;
282 if ((fd = safe_open (path, flags)) < 0)
285 return (fdopen (fd, mode));
288 return (fopen (path, mode));
291 static char safe_chars[] =
292 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%/";
294 void mutt_sanitize_filename (char *f, short slash)
300 if ((slash && *f == '/') || !strchr (safe_chars, *f))
305 /* these characters must be escaped in regular expressions */
307 static char rx_special_chars[] = "^.[$()|*+?{\\";
309 int mutt_rx_sanitize_string (char *dest, size_t destlen, const char *src)
311 while (*src && --destlen > 2) {
312 if (strchr (rx_special_chars, *src)) {
327 /* Read a line from ``fp'' into the dynamically allocated ``s'',
328 * increasing ``s'' if necessary. The ending "\n" or "\r\n" is removed.
329 * If a line ends with "\", this char and the linefeed is removed,
330 * and the next line is read too.
332 char *mutt_read_line (char *s, size_t * size, FILE * fp, int *line)
338 s = safe_malloc (STRING);
343 if (fgets (s + offset, *size - offset, fp) == NULL) {
347 if ((ch = strchr (s + offset, '\n')) != NULL) {
350 if (ch > s && *(ch - 1) == '\r')
352 if (ch == s || *(ch - 1) != '\\')
359 c = getc (fp); /* This is kind of a hack. We want to know if the
360 char at the current point in the input stream is EOF.
361 feof() will only tell us if we've already hit EOF, not
362 if the next character is EOF. So, we need to read in
363 the next character and manually check if it is EOF. */
365 /* The last line of fp isn't \n terminated */
370 ungetc (c, fp); /* undo our dammage */
371 /* There wasn't room for the line -- increase ``s'' */
372 offset = *size - 1; /* overwrite the terminating 0 */
374 safe_realloc (&s, *size);
380 /* prepare a file name to survive the shell's quoting rules.
381 * From the Unix programming FAQ by way of Liviu.
384 size_t mutt_quote_filename (char *d, size_t l, const char *f)
393 /* leave some space for the trailing characters. */
398 for (i = 0; j < l && f[i]; i++) {
399 if (f[i] == '\'' || f[i] == '`') {
415 char *mutt_concat_path (char *d, const char *dir, const char *fname, size_t l)
417 const char *fmt = "%s/%s";
419 if (!*fname || (*dir && dir[safe_strlen (dir) - 1] == '/'))
422 snprintf (d, l, fmt, dir, fname);
426 const char *mutt_basename (const char *f)
428 const char *p = strrchr (f, '/');