lib.c 24.2 KB
Newer Older
Thomas Roessler's avatar
Thomas Roessler committed
1
/*
2
 * Copyright (C) 1996-2000,2007,2010 Michael R. Elkins <me@mutt.org>
3
 * Copyright (C) 1999-2004,2006-2007 Thomas Roessler <roessler@does-not-exist.org>
Thomas Roessler's avatar
Thomas Roessler committed
4
 * 
5 6 7 8 9
 *     This program 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 2 of the License, or (at your option) any later
 *     version.
Thomas Roessler's avatar
Thomas Roessler committed
10
 * 
11 12 13 14 15
 *     This program 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.
Thomas Roessler's avatar
Thomas Roessler committed
16
 * 
17 18
 *     You should have received a copy of the GNU General Public
 *     License along with this program; if not, write to the Free
19
 *     Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20
 *     Boston, MA  02110-1301, USA.
Thomas Roessler's avatar
Thomas Roessler committed
21 22
 */ 

23 24 25 26 27
/*
 * This file used to contain some more functions, namely those
 * which are now in muttlib.c.  They have been removed, so we have
 * some of our "standard" functions in external programs, too.
 */
28

29 30
#define _LIB_C 1

31 32 33 34
#if HAVE_CONFIG_H
# include "config.h"
#endif

Thomas Roessler's avatar
Thomas Roessler committed
35 36 37 38 39 40 41 42
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
43
#include <pwd.h>
44
#include <sys/types.h>
45
#include <dirent.h>
Thomas Roessler's avatar
Thomas Roessler committed
46

47 48 49 50 51 52
#ifdef HAVE_SYSEXITS_H
#include <sysexits.h>
#else /* Make sure EX_OK is defined <philiph@pobox.com> */
#define EX_OK 0
#endif

53
#include "lib.h"
Thomas Roessler's avatar
Thomas Roessler committed
54

55

56
static const struct sysexits
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
{
  int v;
  const char *str;
} 
sysexits_h[] = 
{
#ifdef EX_USAGE
  { 0xff & EX_USAGE, "Bad usage." },
#endif
#ifdef EX_DATAERR
  { 0xff & EX_DATAERR, "Data format error." },
#endif
#ifdef EX_NOINPUT
  { 0xff & EX_NOINPUT, "Cannot open input." },
#endif
#ifdef EX_NOUSER
  { 0xff & EX_NOUSER, "User unknown." },
#endif
#ifdef EX_NOHOST
  { 0xff & EX_NOHOST, "Host unknown." },
#endif
#ifdef EX_UNAVAILABLE
  { 0xff & EX_UNAVAILABLE, "Service unavailable." },
#endif
#ifdef EX_SOFTWARE
  { 0xff & EX_SOFTWARE, "Internal error." },
#endif
#ifdef EX_OSERR
  { 0xff & EX_OSERR, "Operating system error." },
#endif
#ifdef EX_OSFILE
  { 0xff & EX_OSFILE, "System file missing." },
#endif
#ifdef EX_CANTCREAT
  { 0xff & EX_CANTCREAT, "Can't create output." },
#endif
#ifdef EX_IOERR
  { 0xff & EX_IOERR, "I/O error." },
#endif
#ifdef EX_TEMPFAIL
  { 0xff & EX_TEMPFAIL, "Deferred." },
#endif
#ifdef EX_PROTOCOL
  { 0xff & EX_PROTOCOL, "Remote protocol error." },
#endif
#ifdef EX_NOPERM
  { 0xff & EX_NOPERM, "Insufficient permission." },
#endif
#ifdef EX_CONFIG
  { 0xff & EX_NOPERM, "Local configuration error." },
#endif
  { S_ERR, "Exec error." },
  { -1, NULL}
};

112
void mutt_nocurses_error (const char *fmt, ...)
Thomas Roessler's avatar
Thomas Roessler committed
113
{
114
  va_list ap;
Thomas Roessler's avatar
Thomas Roessler committed
115

116 117 118 119
  va_start (ap, fmt);
  vfprintf (stderr, fmt, ap);
  va_end (ap);
  fputc ('\n', stderr);
Thomas Roessler's avatar
Thomas Roessler committed
120 121 122 123 124 125
}

void *safe_calloc (size_t nmemb, size_t size)
{
  void *p;

126 127 128
  if (!nmemb || !size)
    return NULL;

129 130 131 132 133 134 135
  if (((size_t) -1) / nmemb <= size)
  {
    mutt_error _("Integer overflow -- can't allocate memory!");
    sleep (1);
    mutt_exit (1);
  }
  
Thomas Roessler's avatar
Thomas Roessler committed
136 137
  if (!(p = calloc (nmemb, size)))
  {
138
    mutt_error _("Out of memory!");
Thomas Roessler's avatar
Thomas Roessler committed
139 140 141 142 143 144
    sleep (1);
    mutt_exit (1);
  }
  return p;
}

145
void *safe_malloc (size_t siz)
Thomas Roessler's avatar
Thomas Roessler committed
146 147 148 149 150
{
  void *p;

  if (siz == 0)
    return 0;
151
  if ((p = (void *) malloc (siz)) == 0)	/* __MEM_CHECKED__ */
Thomas Roessler's avatar
Thomas Roessler committed
152
  {
153
    mutt_error _("Out of memory!");
Thomas Roessler's avatar
Thomas Roessler committed
154 155 156 157 158 159
    sleep (1);
    mutt_exit (1);
  }
  return (p);
}

160
void safe_realloc (void *ptr, size_t siz)
Thomas Roessler's avatar
Thomas Roessler committed
161 162
{
  void *r;
163
  void **p = (void **)ptr;
Thomas Roessler's avatar
Thomas Roessler committed
164 165 166 167 168

  if (siz == 0)
  {
    if (*p)
    {
169
      free (*p);			/* __MEM_CHECKED__ */
Thomas Roessler's avatar
Thomas Roessler committed
170 171 172 173 174 175
      *p = NULL;
    }
    return;
  }

  if (*p)
176
    r = (void *) realloc (*p, siz);	/* __MEM_CHECKED__ */
Thomas Roessler's avatar
Thomas Roessler committed
177 178
  else
  {
179 180
    /* realloc(NULL, nbytes) doesn't seem to work under SunOS 4.1.x  --- __MEM_CHECKED__ */
    r = (void *) malloc (siz);		/* __MEM_CHECKED__ */
Thomas Roessler's avatar
Thomas Roessler committed
181 182 183 184
  }

  if (!r)
  {
185
    mutt_error _("Out of memory!");
Thomas Roessler's avatar
Thomas Roessler committed
186 187 188 189 190 191 192
    sleep (1);
    mutt_exit (1);
  }

  *p = r;
}

193
void safe_free (void *ptr)	/* __SAFE_FREE_CHECKED__ */
Thomas Roessler's avatar
Thomas Roessler committed
194
{
195
  void **p = (void **)ptr;
Thomas Roessler's avatar
Thomas Roessler committed
196 197
  if (*p)
  {
198
    free (*p);				/* __MEM_CHECKED__ */
Thomas Roessler's avatar
Thomas Roessler committed
199 200 201 202
    *p = 0;
  }
}

203 204 205 206 207 208 209 210 211 212 213
int safe_fclose (FILE **f)
{
  int r = 0;
  
  if (*f)
    r = fclose (*f);
      
  *f = NULL;
  return r;
}

214 215 216 217 218 219 220 221 222
int safe_fsync_close (FILE **f)
{
  int r = 0;

  if (*f)
  {
    if (fflush (*f) || fsync (fileno (*f)))
    {
      r = -1;
223
      safe_fclose (f);
224 225
    }
    else
226
      r = safe_fclose (f);
227 228 229 230 231
  }

  return r;
}

Thomas Roessler's avatar
Thomas Roessler committed
232 233 234 235 236
char *safe_strdup (const char *s)
{
  char *p;
  size_t l;

237 238 239
  if (!s || !*s)
    return 0;
  l = strlen (s) + 1;
Thomas Roessler's avatar
Thomas Roessler committed
240 241 242 243 244
  p = (char *)safe_malloc (l);
  memcpy (p, s, l);
  return (p);
}

245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
char *safe_strcat (char *d, size_t l, const char *s)
{
  char *p = d;

  if (!l) 
    return d;

  l--; /* Space for the trailing '\0'. */
  
  for (; *d && l; l--)
    d++;
  for (; *s && l; l--)
    *d++ = *s++;

  *d = '\0';
  
  return p;
}

char *safe_strncat (char *d, size_t l, const char *s, size_t sl)
{
  char *p = d;

  if (!l)
    return d;
  
  l--; /* Space for the trailing '\0'. */
  
  for (; *d && l; l--)
    d++;
  for (; *s && l && sl; l--, sl--)
    *d++ = *s++;

  *d = '\0';
  
  return p;
}


284 285
void mutt_str_replace (char **p, const char *s)
{
286
  FREE (p);		/* __FREE_CHECKED__ */
287 288 289
  *p = safe_strdup (s);
}

290 291 292
void mutt_str_adjust (char **p)
{
  if (!p || !*p) return;
293
  safe_realloc (p, strlen (*p) + 1);
294 295
}

Thomas Roessler's avatar
Thomas Roessler committed
296 297 298 299 300 301 302
/* convert all characters in the string to lowercase */
char *mutt_strlower (char *s)
{
  char *p = s;

  while (*p)
  {
303
    *p = tolower ((unsigned char) *p);
Thomas Roessler's avatar
Thomas Roessler committed
304 305 306 307 308 309 310 311
    p++;
  }

  return (s);
}

void mutt_unlink (const char *s)
{
312 313
  int fd;
  int flags;
Thomas Roessler's avatar
Thomas Roessler committed
314
  FILE *f;
315
  struct stat sb, sb2;
Thomas Roessler's avatar
Thomas Roessler committed
316
  char buf[2048];
317 318 319 320 321 322 323 324

  /* Defend against symlink attacks */
  
#ifdef O_NOFOLLOW 
  flags = O_RDWR | O_NOFOLLOW;
#else
  flags = O_RDWR;
#endif
Thomas Roessler's avatar
Thomas Roessler committed
325
  
326
  if (lstat (s, &sb) == 0 && S_ISREG(sb.st_mode))
Thomas Roessler's avatar
Thomas Roessler committed
327
  {
328 329
    if ((fd = open (s, flags)) < 0)
      return;
330 331 332 333 334 335 336 337
    
    if ((fstat (fd, &sb2) != 0) || !S_ISREG (sb2.st_mode) 
	|| (sb.st_dev != sb2.st_dev) || (sb.st_ino != sb2.st_ino))
    {
      close (fd);
      return;
    }
    
338
    if ((f = fdopen (fd, "r+")))
Thomas Roessler's avatar
Thomas Roessler committed
339 340 341 342 343
    {
      unlink (s);
      memset (buf, 0, sizeof (buf));
      while (sb.st_size > 0)
      {
Thomas Roessler's avatar
Thomas Roessler committed
344 345
	fwrite (buf, 1, MIN (sizeof (buf), sb.st_size), f);
	sb.st_size -= MIN (sizeof (buf), sb.st_size);
Thomas Roessler's avatar
Thomas Roessler committed
346
      }
347
      safe_fclose (&f);
Thomas Roessler's avatar
Thomas Roessler committed
348 349 350 351
    }
  }
}

352
int mutt_copy_bytes (FILE *in, FILE *out, size_t size)
Thomas Roessler's avatar
Thomas Roessler committed
353
{
354 355
  char buf[2048];
  size_t chunk;
Thomas Roessler's avatar
Thomas Roessler committed
356

357
  while (size > 0)
Thomas Roessler's avatar
Thomas Roessler committed
358
  {
359 360 361 362 363 364
    chunk = (size > sizeof (buf)) ? sizeof (buf) : size;
    if ((chunk = fread (buf, 1, chunk, in)) < 1)
      break;
    if (fwrite (buf, 1, chunk, out) != chunk)
    {
      /* dprint (1, (debugfile, "mutt_copy_bytes(): fwrite() returned short byte count\n")); */
Thomas Roessler's avatar
Thomas Roessler committed
365
      return (-1);
366 367
    }
    size -= chunk;
Thomas Roessler's avatar
Thomas Roessler committed
368 369 370 371 372
  }

  return 0;
}

373
int mutt_copy_stream (FILE *fin, FILE *fout)
Thomas Roessler's avatar
Thomas Roessler committed
374
{
375 376
  size_t l;
  char buf[LONG_STRING];
Thomas Roessler's avatar
Thomas Roessler committed
377

378
  while ((l = fread (buf, 1, sizeof (buf), fin)) > 0)
Thomas Roessler's avatar
Thomas Roessler committed
379
  {
380 381
    if (fwrite (buf, 1, l, fout) != l)
      return (-1);
Thomas Roessler's avatar
Thomas Roessler committed
382 383
  }

384
  return 0;
Thomas Roessler's avatar
Thomas Roessler committed
385 386
}

387
int 
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
compare_stat (struct stat *osb, struct stat *nsb)
{
  if (osb->st_dev != nsb->st_dev || osb->st_ino != nsb->st_ino ||
      osb->st_rdev != nsb->st_rdev)
  {
    return -1;
  }

  return 0;
}

int safe_symlink(const char *oldpath, const char *newpath)
{
  struct stat osb, nsb;

  if(!oldpath || !newpath)
    return -1;
  
  if(unlink(newpath) == -1 && errno != ENOENT)
    return -1;
  
409 410 411 412 413 414 415 416 417 418 419
  if (oldpath[0] == '/')
  {
    if (symlink (oldpath, newpath) == -1)
      return -1;
  }
  else
  {
    char abs_oldpath[_POSIX_PATH_MAX];

    if ((getcwd (abs_oldpath, sizeof abs_oldpath) == NULL) ||
	(strlen (abs_oldpath) + 1 + strlen (oldpath) + 1 > sizeof abs_oldpath))
420 421
    return -1;
  
422 423
    strcat (abs_oldpath, "/");		/* __STRCAT_CHECKED__ */
    strcat (abs_oldpath, oldpath);	/* __STRCAT_CHECKED__ */
424 425 426 427
    if (symlink (abs_oldpath, newpath) == -1)
      return -1;
  }

428 429 430 431 432 433 434 435 436 437
  if(stat(oldpath, &osb) == -1 || stat(newpath, &nsb) == -1
     || compare_stat(&osb, &nsb) == -1)
  {
    unlink(newpath);
    return -1;
  }
  
  return 0;
}

438 439


440 441 442 443 444 445 446 447 448
/* 
 * This function is supposed to do nfs-safe renaming of files.
 * 
 * Warning: We don't check whether src and target are equal.
 */

int safe_rename (const char *src, const char *target)
{
  struct stat ssb, tsb;
449
  int link_errno;
450 451 452 453 454 455

  if (!src || !target)
    return -1;

  if (link (src, target) != 0)
  {
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479
    link_errno = errno;

    /*
     * It is historically documented that link can return -1 if NFS
     * dies after creating the link.  In that case, we are supposed
     * to use stat to check if the link was created.
     *
     * Derek Martin notes that some implementations of link() follow a
     * source symlink.  It might be more correct to use stat() on src.
     * I am not doing so to minimize changes in behavior: the function
     * used lstat() further below for 20 years without issue, and I
     * believe was never intended to be used on a src symlink.
     */
    if ((lstat (src, &ssb) == 0) &&
        (lstat (target, &tsb) == 0) &&
        (compare_stat (&ssb, &tsb) == 0))
    {
      dprint (1, (debugfile,
                  "safe_rename: link (%s, %s) reported failure: %s (%d) but actually succeded\n",
                  src, target, strerror (errno), errno));
      goto success;
    }

    errno = link_errno;
480 481 482 483 484 485 486 487 488 489 490 491

    /*
     * Coda does not allow cross-directory links, but tells
     * us it's a cross-filesystem linking attempt.
     * 
     * However, the Coda rename call is allegedly safe to use.
     * 
     * With other file systems, rename should just fail when 
     * the files reside on different file systems, so it's safe
     * to try it here.
     *
     */
492 493
    
    dprint (1, (debugfile, "safe_rename: link (%s, %s) failed: %s (%d)\n", src, target, strerror (errno), errno));
494

495 496 497 498
    /*
     * FUSE may return ENOSYS. VFAT may return EPERM. FreeBSD's
     * msdosfs may return EOPNOTSUPP.  ENOTSUP can also appear.
     */
499 500 501 502 503 504 505 506
    if (errno == EXDEV || errno == ENOSYS || errno == EPERM
#ifdef ENOTSUP
	|| errno == ENOTSUP
#endif
#ifdef EOPNOTSUPP
	|| errno == EOPNOTSUPP
#endif
	)
507
    {
508
      dprint (1, (debugfile, "safe_rename: trying rename...\n"));
509 510 511 512 513 514
      if (rename (src, target) == -1) 
      {
	dprint (1, (debugfile, "safe_rename: rename (%s, %s) failed: %s (%d)\n", src, target, strerror (errno), errno));
	return -1;
      }
      dprint (1, (debugfile, "safe_rename: rename succeeded.\n"));
515
    
516 517 518
      return 0;
    }

519 520 521
    return -1;
  }

522 523 524 525 526 527 528 529
  /*
   * Remove the compare_stat() check, because it causes problems with maildir on
   * filesystems that don't properly support hard links, such as
   * sshfs.  The filesystem creates the link, but the resulting file
   * is given a different inode number by the sshfs layer.  This
   * results in an infinite loop creating links.
   */
#if 0
530 531 532
  /*
   * Stat both links and check if they are equal.
   */
533
  if (lstat (src, &ssb) == -1)
534
  {
535 536
    dprint (1, (debugfile, "safe_rename: can't stat %s: %s (%d)\n",
		src, strerror (errno), errno));
537 538
    return -1;
  }
539

540
  if (lstat (target, &tsb) == -1)
541
  {
542 543
    dprint (1, (debugfile, "safe_rename: can't stat %s: %s (%d)\n",
		src, strerror (errno), errno));
544 545 546 547 548 549 550 551 552
    return -1;
  }

  /* 
   * pretend that the link failed because the target file
   * did already exist.
   */
  if (compare_stat (&ssb, &tsb) == -1)
  {
553
    dprint (1, (debugfile, "safe_rename: stat blocks for %s and %s diverge; pretending EEXIST.\n", src, target));
554 555 556
    errno = EEXIST;
    return -1;
  }
557
#endif
558

559
success:
560 561 562 563
  /*
   * Unlink the original link.  Should we really ignore the return
   * value here? XXX
   */
564 565 566 567 568 569
  if (unlink (src) == -1) 
  {
    dprint (1, (debugfile, "safe_rename: unlink (%s) failed: %s (%d)\n",
		src, strerror (errno), errno));
  }
  
570 571 572 573

  return 0;
}

574

575 576
/* Create a temporary directory next to a file name */

577
static int mutt_mkwrapdir (const char *path, char *newfile, size_t nflen, 
578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
		    char *newdir, size_t ndlen)
{
  const char *basename;
  char parent[_POSIX_PATH_MAX];
  char *p;

  strfcpy (parent, NONULL (path), sizeof (parent));
  
  if ((p = strrchr (parent, '/')))
  {
    *p = '\0';
    basename = p + 1;
  }
  else
  {
    strfcpy (parent, ".", sizeof (parent));
    basename = path;
  }

597 598
  snprintf (newdir, ndlen, "%s/%s", parent, ".muttXXXXXX");
  if (mkdtemp(newdir) == NULL)
599
  {
600 601 602
      dprint(1, (debugfile, "mutt_mkwrapdir: mkdtemp() failed\n"));
      return -1;
  }
603
  
604 605 606 607 608 609
  if (snprintf (newfile, nflen, "%s/%s", newdir, NONULL(basename)) >= nflen)
  {
      rmdir(newdir);
      dprint(1, (debugfile, "mutt_mkwrapdir: string was truncated\n"));
      return -1;
  }
610 611 612
  return 0;  
}

613 614 615 616 617 618
/* remove a directory and everything under it */
int mutt_rmtree (const char* path)
{
  DIR* dirp;
  struct dirent* de;
  char cur[_POSIX_PATH_MAX];
619
  struct stat statbuf;
620 621 622 623 624 625 626 627 628 629 630 631 632 633
  int rc = 0;

  if (!(dirp = opendir (path)))
  {
    dprint (1, (debugfile, "mutt_rmtree: error opening directory %s\n", path));
    return -1;
  }
  while ((de = readdir (dirp)))
  {
    if (!strcmp (".", de->d_name) || !strcmp ("..", de->d_name))
      continue;

    snprintf (cur, sizeof (cur), "%s/%s", path, de->d_name);
    /* XXX make nonrecursive version */
634 635 636 637 638 639 640 641

    if (stat(cur, &statbuf) == -1)
    {
      rc = 1;
      continue;
    }

    if (S_ISDIR (statbuf.st_mode))
642 643 644 645 646 647 648 649 650 651 652
      rc |= mutt_rmtree (cur);
    else
      rc |= unlink (cur);
  }
  closedir (dirp);

  rc |= rmdir (path);

  return rc;
}

653
static int mutt_put_file_in_place (const char *path, const char *safe_file, const char *safe_dir)
654 655 656 657 658 659 660 661 662
{
  int rv;
  
  rv = safe_rename (safe_file, path);
  unlink (safe_file);
  rmdir (safe_dir);
  return rv;
}

Thomas Roessler's avatar
Thomas Roessler committed
663 664 665 666 667
int safe_open (const char *path, int flags)
{
  struct stat osb, nsb;
  int fd;

668 669 670 671
  if (flags & O_EXCL) 
  {
    char safe_file[_POSIX_PATH_MAX];
    char safe_dir[_POSIX_PATH_MAX];
Thomas Roessler's avatar
Thomas Roessler committed
672

673 674 675 676
    if (mutt_mkwrapdir (path, safe_file, sizeof (safe_file),
			safe_dir, sizeof (safe_dir)) == -1)
      return -1;
    
Brendan Cully's avatar
Brendan Cully committed
677
    if ((fd = open (safe_file, flags, 0600)) < 0)
678 679 680 681
    {
      rmdir (safe_dir);
      return fd;
    }
682 683 684

    /* NFS and I believe cygwin do not handle movement of open files well */
    close (fd);
685 686 687
    if (mutt_put_file_in_place (path, safe_file, safe_dir) == -1)
      return -1;
  }
688 689 690

  if ((fd = open (path, flags & ~O_EXCL, 0600)) < 0)
    return fd;
691
    
Thomas Roessler's avatar
Thomas Roessler committed
692 693
  /* make sure the file is not symlink */
  if (lstat (path, &osb) < 0 || fstat (fd, &nsb) < 0 ||
694
      compare_stat(&osb, &nsb) == -1)
Thomas Roessler's avatar
Thomas Roessler committed
695
  {
696
/*    dprint (1, (debugfile, "safe_open(): %s is a symlink!\n", path)); */
Thomas Roessler's avatar
Thomas Roessler committed
697 698 699 700 701 702 703
    close (fd);
    return (-1);
  }

  return (fd);
}

Thomas Roessler's avatar
Thomas Roessler committed
704 705 706 707 708 709 710 711 712 713
/* when opening files for writing, make sure the file doesn't already exist
 * to avoid race conditions.
 */
FILE *safe_fopen (const char *path, const char *mode)
{
  if (mode[0] == 'w')
  {
    int fd;
    int flags = O_CREAT | O_EXCL;

714 715 716 717
#ifdef O_NOFOLLOW
    flags |= O_NOFOLLOW;
#endif

Thomas Roessler's avatar
Thomas Roessler committed
718 719 720 721 722
    if (mode[1] == '+')
      flags |= O_RDWR;
    else
      flags |= O_WRONLY;

Thomas Roessler's avatar
Thomas Roessler committed
723
    if ((fd = safe_open (path, flags)) < 0)
Thomas Roessler's avatar
Thomas Roessler committed
724 725 726 727 728 729 730 731
      return (NULL);

    return (fdopen (fd, mode));
  }
  else
    return (fopen (path, mode));
}

732
static const char safe_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+@{}._-:%/";
Thomas Roessler's avatar
Thomas Roessler committed
733

734
void mutt_sanitize_filename (char *f, short slash)
Thomas Roessler's avatar
Thomas Roessler committed
735
{
736
  if (!f) return;
Thomas Roessler's avatar
Thomas Roessler committed
737

738
  for (; *f; f++)
Thomas Roessler's avatar
Thomas Roessler committed
739
  {
740
    if ((slash && *f == '/') || !strchr (safe_chars, *f))
Thomas Roessler's avatar
Thomas Roessler committed
741 742 743 744
      *f = '_';
  }
}

745 746
/* these characters must be escaped in regular expressions */

747
static const char rx_special_chars[] = "^.[$()|*+?{\\";
748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768

int mutt_rx_sanitize_string (char *dest, size_t destlen, const char *src)
{
  while (*src && --destlen > 2)
  {
    if (strchr (rx_special_chars, *src))
    {
      *dest++ = '\\';
      destlen--;
    }
    *dest++ = *src++;
  }
  
  *dest = '\0';
  
  if (*src)
    return -1;
  else
    return 0;
}

Thomas Roessler's avatar
Thomas Roessler committed
769 770 771 772 773
/* Read a line from ``fp'' into the dynamically allocated ``s'',
 * increasing ``s'' if necessary. The ending "\n" or "\r\n" is removed.
 * If a line ends with "\", this char and the linefeed is removed,
 * and the next line is read too.
 */
774
char *mutt_read_line (char *s, size_t *size, FILE *fp, int *line, int flags)
Thomas Roessler's avatar
Thomas Roessler committed
775 776 777 778 779 780 781 782 783 784 785 786 787 788
{
  size_t offset = 0;
  char *ch;

  if (!s)
  {
    s = safe_malloc (STRING);
    *size = STRING;
  }

  FOREVER
  {
    if (fgets (s + offset, *size - offset, fp) == NULL)
    {
789
      FREE (&s);
Thomas Roessler's avatar
Thomas Roessler committed
790 791 792 793
      return NULL;
    }
    if ((ch = strchr (s + offset, '\n')) != NULL)
    {
794 795
      if (line)
	(*line)++;
796
      if (flags & MUTT_EOL)
797
	return s;
Thomas Roessler's avatar
Thomas Roessler committed
798 799 800
      *ch = 0;
      if (ch > s && *(ch - 1) == '\r')
	*--ch = 0;
801
      if (!(flags & MUTT_CONT) || ch == s || *(ch - 1) != '\\')
Thomas Roessler's avatar
Thomas Roessler committed
802 803 804 805 806
	return s;
      offset = ch - s - 1;
    }
    else
    {
807 808 809 810 811 812 813 814 815
      int c;
      c = getc (fp); /* This is kind of a hack. We want to know if the
                        char at the current point in the input stream is EOF.
                        feof() will only tell us if we've already hit EOF, not
                        if the next character is EOF. So, we need to read in
                        the next character and manually check if it is EOF. */
      if (c == EOF)
      {
        /* The last line of fp isn't \n terminated */
816 817
	if (line)
	  (*line)++;
818 819 820 821
        return s;
      }
      else
      {
Ondřej Bílka's avatar
Ondřej Bílka committed
822
        ungetc (c, fp); /* undo our damage */
823 824 825
        /* There wasn't room for the line -- increase ``s'' */
        offset = *size - 1; /* overwrite the terminating 0 */
        *size += STRING;
826
        safe_realloc (&s, *size);
827
      }
Thomas Roessler's avatar
Thomas Roessler committed
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849
    }
  }
}

char *
mutt_substrcpy (char *dest, const char *beg, const char *end, size_t destlen)
{
  size_t len;

  len = end - beg;
  if (len > destlen - 1)
    len = destlen - 1;
  memcpy (dest, beg, len);
  dest[len] = 0;
  return dest;
}

char *mutt_substrdup (const char *begin, const char *end)
{
  size_t len;
  char *p;

850 851 852 853 854
  if (end)
    len = end - begin;
  else
    len = strlen (begin);

Thomas Roessler's avatar
Thomas Roessler committed
855 856 857 858 859 860
  p = safe_malloc (len + 1);
  memcpy (p, begin, len);
  p[len] = 0;
  return p;
}

861 862 863 864
/* prepare a file name to survive the shell's quoting rules.
 * From the Unix programming FAQ by way of Liviu.
 */

865
size_t mutt_quote_filename (char *d, size_t l, const char *f)
866
{
867
  size_t i, j = 0;
868

869
  if(!f) 
870
  {
871 872
    *d = '\0';
    return 0;
873
  }
874 875 876

  /* leave some space for the trailing characters. */
  l -= 6;
877
  
878
  d[j++] = '\'';
879
  
880
  for(i = 0; j < l && f[i]; i++)
881
  {
882
    if(f[i] == '\'' || f[i] == '`')
883
    {
884 885
      d[j++] = '\'';
      d[j++] = '\\';
886
      d[j++] = f[i];
887
      d[j++] = '\'';
888 889
    }
    else
890
      d[j++] = f[i];
891 892
  }
  
893 894
  d[j++] = '\'';
  d[j]   = '\0';
895
  
896
  return j;
897
}
898

899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922
/* NULL-pointer aware string comparison functions */

int mutt_strcmp(const char *a, const char *b)
{
  return strcmp(NONULL(a), NONULL(b));
}

int mutt_strcasecmp(const char *a, const char *b)
{
  return strcasecmp(NONULL(a), NONULL(b));
}

int mutt_strncmp(const char *a, const char *b, size_t l)
{
  return strncmp(NONULL(a), NONULL(b), l);
}

int mutt_strncasecmp(const char *a, const char *b, size_t l)
{
  return strncasecmp(NONULL(a), NONULL(b), l);
}

size_t mutt_strlen(const char *a)
{
923
  return a ? strlen (a) : 0;
924
}
925

926 927 928 929 930
int mutt_strcoll(const char *a, const char *b)
{
  return strcoll(NONULL(a), NONULL(b));
}

931 932 933 934 935 936 937 938 939 940 941
const char *mutt_stristr (const char *haystack, const char *needle)
{
  const char *p, *q;

  if (!haystack)
    return NULL;
  if (!needle)
    return (haystack);

  while (*(p = haystack))
  {
942 943 944 945
    for (q = needle;
         *p && *q &&
           tolower ((unsigned char) *p) == tolower ((unsigned char) *q);
         p++, q++)
946 947 948 949 950 951 952
      ;
    if (!*q)
      return (haystack);
    haystack++;
  }
  return NULL;
}
953 954 955 956 957 958 959 960 961 962 963 964 965 966

char *mutt_skip_whitespace (char *p)
{
  SKIPWS (p);
  return p;
}

void mutt_remove_trailing_ws (char *s)
{
  char *p;
  
  for (p = s + mutt_strlen (s) - 1 ; p >= s && ISSPACE (*p) ; p--)
    *p = 0;
}
967

Michael Elkins's avatar
Michael Elkins committed
968 969
/*
 * Write the concatened pathname (dir + "/" + fname) into dst.
970
 * The slash is omitted when dir or fname is of 0 length.
Michael Elkins's avatar
Michael Elkins committed
971 972 973 974 975
 * Returns NULL on error or a pointer to dst otherwise.
 */
char *mutt_concatn_path (char *dst, size_t dstlen,
    const char *dir, size_t dirlen, const char *fname, size_t fnamelen)
{
Brendan Cully's avatar
Brendan Cully committed
976 977 978
  size_t req;
  size_t offset = 0;

Michael Elkins's avatar
Michael Elkins committed
979 980 981 982
  if (dstlen == 0)
    return NULL; /* probably should not mask errors like this */

  /* size check */
Brendan Cully's avatar
Brendan Cully committed
983
  req = dirlen + fnamelen + 1; /* +1 for the trailing nul */
Michael Elkins's avatar
Michael Elkins committed
984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
  if (dirlen && fnamelen)
    req++; /* when both components are non-nul, we add a "/" in between */
  if (req > dstlen) { /* check for condition where the dst length is too short */
    /* Two options here:
     * 1) assert(0) or return NULL to signal error
     * 2) copy as much of the path as will fit
     * It doesn't appear that the return value is actually checked anywhere mutt_concat_path()
     * is called, so we should just copy set dst to nul and let the calling function fail later.
     */
    dst[0] = 0; /* safe since we bail out early if dstlen == 0 */
    return NULL;
  }

  if (dirlen) { /* when dir is not empty */
    memcpy(dst, dir, dirlen);
    offset = dirlen;
    if (fnamelen)
      dst[offset++] = '/';
  }
  if (fnamelen) { /* when fname is not empty */
    memcpy(dst + offset, fname, fnamelen);
    offset += fnamelen;
  }
  dst[offset] = 0;
  return dst;
}

1011 1012 1013 1014 1015 1016 1017
char *mutt_concat_path (char *d, const char *dir, const char *fname, size_t l)
{
  const char *fmt = "%s/%s";
  
  if (!*fname || (*dir && dir[strlen(dir)-1] == '/'))
    fmt = "%s%s";
  
1018
  snprintf (d, l, fmt, dir, fname);
1019 1020
  return d;
}
1021 1022 1023 1024

const char *mutt_basename (const char *f)
{
  const char *p = strrchr (f, '/');
1025 1026 1027 1028
  if (p)
    return p + 1;
  else
    return f;
1029
}
1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043

const char *
mutt_strsysexit(int e)
{
  int i;
  
  for(i = 0; sysexits_h[i].str; i++)
  {
    if(e == sysexits_h[i].v)
      break;
  }
  
  return sysexits_h[i].str;
}
1044

1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062
void mutt_debug (FILE *fp, const char *fmt, ...)
{
  va_list ap;
  time_t now = time (NULL);
  static char buf[23] = "";
  static time_t last = 0;

  if (now > last)
  {
    strftime (buf, sizeof (buf), "%Y-%m-%d %H:%M:%S", localtime (&now));
    last = now;
  }
  fprintf (fp, "[%s] ", buf);
  va_start (ap, fmt);
  vfprintf (fp, fmt, ap);
  va_end (ap);
}

1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111
int mutt_atos (const char *str, short *dst)
{
  int rc;
  long res;
  short tmp;
  short *t = dst ? dst : &tmp;

  *t = 0;

  if ((rc = mutt_atol (str, &res)) < 0)
    return rc;
  if ((short) res != res)
    return -2;

  *t = (short) res;
  return 0;
}

int mutt_atoi (const char *str, int *dst)
{
  int rc;
  long res;
  int tmp;
  int *t = dst ? dst : &tmp;

  *t = 0;

  if ((rc = mutt_atol (str, &res)) < 0)
    return rc;
  if ((int) res != res)
    return -2;

  *t = (int) res;
  return 0;
}

int mutt_atol (const char *str, long *dst)
{
  long r;
  long *res = dst ? dst : &r;
  char *e = NULL;

  /* no input: 0 */
  if (!str || !*str)
  {
    *res = 0;
    return 0;
  }

1112
  errno = 0;
1113
  *res = strtol (str, &e, 10);
1114
  if (e && *e != '\0')
1115
    return -1;
1116 1117
  if (errno == ERANGE)
    return -2;
1118 1119
  return 0;
}
1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175

/* NOTE: this function's return value breaks with the above three functions.
 * The imap code lexes uint values out of a stream of characters without
 * tokenization.  The above functions return -1 if there is input beyond
 * the number.
 *
 * returns: 1 - successful conversion, with trailing characters
 *          0 - successful conversion
 *         -1 - invalid input
 *         -2 - input out of range
 */
int mutt_atoui (const char *str, unsigned int *dst)
{
  int rc;
  unsigned long res;
  unsigned int tmp;
  unsigned int *t = dst ? dst : &tmp;

  *t = 0;

  if ((rc = mutt_atoul (str, &res)) < 0)
    return rc;
  if ((unsigned int) res != res)
    return -2;

  *t = (unsigned int) res;
  return rc;
}

/* NOTE: this function's return value is different from mutt_atol.
 *
 * returns: 1 - successful conversion, with trailing characters
 *          0 - successful conversion
 *         -1 - invalid input
 */
int mutt_atoul (const char *str, unsigned long *dst)
{
  unsigned long r;
  unsigned long *res = dst ? dst : &r;
  char *e = NULL;

  /* no input: 0 */
  if (!str || !*str)
  {
    *res = 0;
    return 0;
  }

  errno = 0;
  *res = strtoul (str, &e, 10);
  if (*res == ULONG_MAX && errno == ERANGE)
    return -1;
  if (e && *e != '\0')
    return 1;
  return 0;
}
1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203

/* NOTE: this function's return value is different from mutt_atol.
 *
 * returns: 1 - successful conversion, with trailing characters
 *          0 - successful conversion
 *         -1 - invalid input
 */
int mutt_atoull (const char *str, unsigned long long *dst)
{
  unsigned long long r;
  unsigned long long *res = dst ? dst : &r;
  char *e = NULL;

  /* no input: 0 */
  if (!str || !*str)
  {
    *res = 0;
    return 0;
  }

  errno = 0;
  *res = strtoull (str, &e, 10);
  if (*res == ULLONG_MAX && errno == ERANGE)
    return -1;
  if (e && *e != '\0')
    return 1;
  return 0;
}