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.
38 void mutt_nocurses_error (const char *fmt, ...)
43 vfprintf (stderr, fmt, ap);
48 int safe_fclose (FILE ** f)
59 void mutt_unlink (const char *s)
67 /* Defend against symlink attacks */
70 flags = O_RDWR | O_NOFOLLOW;
75 if (lstat (s, &sb) == 0 && S_ISREG (sb.st_mode)) {
76 if ((fd = open (s, flags)) < 0)
79 if ((fstat (fd, &sb2) != 0) || !S_ISREG (sb2.st_mode)
80 || (sb.st_dev != sb2.st_dev) || (sb.st_ino != sb2.st_ino)) {
85 if ((f = fdopen (fd, "r+"))) {
87 memset (buf, 0, sizeof (buf));
88 while (sb.st_size > 0) {
89 fwrite (buf, 1, MIN (sizeof (buf), sb.st_size), f);
90 sb.st_size -= MIN (sizeof (buf), sb.st_size);
97 int mutt_copy_bytes (FILE * in, FILE * out, size_t size)
103 chunk = (size > sizeof (buf)) ? sizeof (buf) : size;
104 if ((chunk = fread (buf, 1, chunk, in)) < 1)
106 if (fwrite (buf, 1, chunk, out) != chunk) {
107 /* dprint (1, (debugfile, "mutt_copy_bytes(): fwrite() returned short byte count\n")); */
116 int mutt_copy_stream (FILE * fin, FILE * fout)
119 char buf[LONG_STRING];
121 while ((l = fread (buf, 1, sizeof (buf), fin)) > 0) {
122 if (fwrite (buf, 1, l, fout) != l)
129 static int compare_stat (struct stat *osb, struct stat *nsb)
131 if (osb->st_dev != nsb->st_dev || osb->st_ino != nsb->st_ino ||
132 osb->st_rdev != nsb->st_rdev) {
139 int safe_symlink (const char *oldpath, const char *newpath)
141 struct stat osb, nsb;
143 if (!oldpath || !newpath)
146 if (unlink (newpath) == -1 && errno != ENOENT)
149 if (oldpath[0] == '/') {
150 if (symlink (oldpath, newpath) == -1)
154 char abs_oldpath[_POSIX_PATH_MAX];
156 if ((getcwd (abs_oldpath, sizeof abs_oldpath) == NULL) ||
157 (safe_strlen (abs_oldpath) + 1 + safe_strlen (oldpath) + 1 >
161 strcat (abs_oldpath, "/"); /* __STRCAT_CHECKED__ */
162 strcat (abs_oldpath, oldpath); /* __STRCAT_CHECKED__ */
163 if (symlink (abs_oldpath, newpath) == -1)
167 if (stat (oldpath, &osb) == -1 || stat (newpath, &nsb) == -1
168 || compare_stat (&osb, &nsb) == -1) {
177 * This function is supposed to do nfs-safe renaming of files.
179 * Warning: We don't check whether src and target are equal.
182 int safe_rename (const char *src, const char *target)
184 struct stat ssb, tsb;
189 if (link (src, target) != 0) {
192 * Coda does not allow cross-directory links, but tells
193 * us it's a cross-filesystem linking attempt.
195 * However, the Coda rename call is allegedly safe to use.
197 * With other file systems, rename should just fail when
198 * the files reside on different file systems, so it's safe
204 return rename (src, target);
210 * Stat both links and check if they are equal.
213 if (stat (src, &ssb) == -1) {
217 if (stat (target, &tsb) == -1) {
222 * pretend that the link failed because the target file
226 if (compare_stat (&ssb, &tsb) == -1) {
232 * Unlink the original link. Should we really ignore the return
241 int safe_open (const char *path, int flags)
243 struct stat osb, nsb;
247 if ((fd = open (path, flags, 0666)) < 0)
250 /* make sure the file is not symlink */
251 if (lstat (path, &osb) < 0 || fstat (fd, &nsb) < 0 ||
252 compare_stat (&osb, &nsb) == -1) {
253 /* dprint (1, (debugfile, "safe_open(): %s is a symlink!\n", path)); */
261 /* when opening files for writing, make sure the file doesn't already exist
262 * to avoid race conditions.
264 FILE *safe_fopen (const char *path, const char *mode)
266 /* first set the current umask */
268 if (mode[0] == 'w') {
270 int flags = O_CREAT | O_EXCL;
281 if ((fd = safe_open (path, flags)) < 0)
284 return (fdopen (fd, mode));
287 return (fopen (path, mode));
290 static char safe_chars[] =
291 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%/";
293 void mutt_sanitize_filename (char *f, short slash)
299 if ((slash && *f == '/') || !strchr (safe_chars, *f))
304 /* these characters must be escaped in regular expressions */
306 static char rx_special_chars[] = "^.[$()|*+?{\\";
308 int mutt_rx_sanitize_string (char *dest, size_t destlen, const char *src)
310 while (*src && --destlen > 2) {
311 if (strchr (rx_special_chars, *src)) {
326 /* Read a line from ``fp'' into the dynamically allocated ``s'',
327 * increasing ``s'' if necessary. The ending "\n" or "\r\n" is removed.
328 * If a line ends with "\", this char and the linefeed is removed,
329 * and the next line is read too.
331 char *mutt_read_line (char *s, size_t * size, FILE * fp, int *line)
337 s = safe_malloc (STRING);
342 if (fgets (s + offset, *size - offset, fp) == NULL) {
346 if ((ch = strchr (s + offset, '\n')) != NULL) {
349 if (ch > s && *(ch - 1) == '\r')
351 if (ch == s || *(ch - 1) != '\\')
358 c = getc (fp); /* This is kind of a hack. We want to know if the
359 char at the current point in the input stream is EOF.
360 feof() will only tell us if we've already hit EOF, not
361 if the next character is EOF. So, we need to read in
362 the next character and manually check if it is EOF. */
364 /* The last line of fp isn't \n terminated */
369 ungetc (c, fp); /* undo our dammage */
370 /* There wasn't room for the line -- increase ``s'' */
371 offset = *size - 1; /* overwrite the terminating 0 */
373 safe_realloc (&s, *size);
379 /* prepare a file name to survive the shell's quoting rules.
380 * From the Unix programming FAQ by way of Liviu.
383 size_t mutt_quote_filename (char *d, size_t l, const char *f)
392 /* leave some space for the trailing characters. */
397 for (i = 0; j < l && f[i]; i++) {
398 if (f[i] == '\'' || f[i] == '`') {
414 char *mutt_concat_path (char *d, const char *dir, const char *fname, size_t l)
416 const char *fmt = "%s/%s";
418 if (!*fname || (*dir && dir[safe_strlen (dir) - 1] == '/'))
421 snprintf (d, l, fmt, dir, fname);
425 const char *mutt_basename (const char *f)
427 const char *p = strrchr (f, '/');