movemail.c 22.2 KB
Newer Older
Richard M. Stallman's avatar
Richard M. Stallman committed
1 2
/* movemail foo bar -- move file foo to file bar,
   locking file foo the way /bin/mail respects.
3
   Copyright (C) 1986, 1992, 1993, 1994, 1996, 1999, 2001, 2002, 2003, 2004,
Glenn Morris's avatar
Glenn Morris committed
4
                 2005, 2006, 2007, 2008  Free Software Foundation, Inc.
Richard M. Stallman's avatar
Richard M. Stallman committed
5 6 7

This file is part of GNU Emacs.

Joseph Arceneaux's avatar
Joseph Arceneaux committed
8 9
GNU Emacs is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
10
the Free Software Foundation; either version 3, or (at your option)
Joseph Arceneaux's avatar
Joseph Arceneaux committed
11 12
any later version.

Richard M. Stallman's avatar
Richard M. Stallman committed
13
GNU Emacs is distributed in the hope that it will be useful,
Joseph Arceneaux's avatar
Joseph Arceneaux committed
14 15 16
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.
Richard M. Stallman's avatar
Richard M. Stallman committed
17

Joseph Arceneaux's avatar
Joseph Arceneaux committed
18 19
You should have received a copy of the GNU General Public License
along with GNU Emacs; see the file COPYING.  If not, write to
Lute Kamstra's avatar
Lute Kamstra committed
20 21
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.  */
Richard M. Stallman's avatar
Richard M. Stallman committed
22

23 24 25 26 27
/* Important notice: defining MAIL_USE_FLOCK or MAIL_USE_LOCKF *will
   cause loss of mail* if you do it on a system that does not normally
   use flock as its way of interlocking access to inbox files.  The
   setting of MAIL_USE_FLOCK and MAIL_USE_LOCKF *must agree* with the
   system's own conventions.  It is not a choice that is up to you.
28 29 30 31 32 33 34

   So, if your system uses lock files rather than flock, then the only way
   you can get proper operation is to enable movemail to write lockfiles there.
   This means you must either give that directory access modes
   that permit everyone to write lockfiles in it, or you must make movemail
   a setuid or setgid program.  */

Richard M. Stallman's avatar
Richard M. Stallman committed
35 36 37
/*
 * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
 *
Richard M. Stallman's avatar
Richard M. Stallman committed
38
 * Added POP (Post Office Protocol) service.  When compiled -DMAIL_USE_POP
Richard M. Stallman's avatar
Richard M. Stallman committed
39 40 41 42
 * movemail will accept input filename arguments of the form
 * "po:username".  This will cause movemail to open a connection to
 * a pop server running on $MAILHOST (environment variable).  Movemail
 * must be setuid to root in order to work with POP.
43
 *
Richard M. Stallman's avatar
Richard M. Stallman committed
44 45
 * New module: popmail.c
 * Modified routines:
46
 *	main - added code within #ifdef MAIL_USE_POP; added setuid (getuid ())
47
 *		after POP code.
Richard M. Stallman's avatar
Richard M. Stallman committed
48 49 50
 * New routines in movemail.c:
 *	get_errmsg - return pointer to system error message
 *
51 52 53 54 55
 * Modified August, 1993 by Jonathan Kamens (OpenVision Technologies)
 *
 * Move all of the POP code into a separate file, "pop.c".
 * Use strerror instead of get_errmsg.
 *
Richard M. Stallman's avatar
Richard M. Stallman committed
56 57
 */

58
#define NO_SHORTNAMES   /* Tell config not to load remap.h */
59
#include <config.h>
Richard M. Stallman's avatar
Richard M. Stallman committed
60 61 62
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
63
#include <stdio.h>
Richard M. Stallman's avatar
Richard M. Stallman committed
64
#include <errno.h>
65

66
#include <getopt.h>
67 68 69
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
70 71 72
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
73
#include "syswait.h"
74 75 76
#ifdef MAIL_USE_POP
#include "pop.h"
#endif
Richard M. Stallman's avatar
Richard M. Stallman committed
77

78 79 80 81
#ifdef MSDOS
#undef access
#endif /* MSDOS */

82 83 84 85 86 87 88
#ifndef DIRECTORY_SEP
#define DIRECTORY_SEP '/'
#endif
#ifndef IS_DIRECTORY_SEP
#define IS_DIRECTORY_SEP(_c_) ((_c_) == DIRECTORY_SEP)
#endif

89
#ifdef WINDOWSNT
90
#include "ntlib.h"
91 92 93
#undef access
#undef unlink
#define fork() 0
Andrew Innes's avatar
Andrew Innes committed
94
#define wait(var) (*(var) = 0)
95 96 97 98
/* Unfortunately, Samba doesn't seem to properly lock Unix files even
   though the locking call succeeds (and indeed blocks local access from
   other NT programs).  If you have direct file access using an NFS
   client or something other than Samba, the locking call might work
99 100 101 102 103 104 105 106
   properly - make sure it does before you enable this!

   [18-Feb-97 andrewi] I now believe my comment above to be incorrect,
   since it was based on a misunderstanding of how locking calls are
   implemented and used on Unix.  */
//#define DISABLE_DIRECT_ACCESS

#include <fcntl.h>
107 108
#endif /* WINDOWSNT */

109 110 111 112 113 114
#ifndef F_OK
#define F_OK 0
#define X_OK 1
#define W_OK 2
#define R_OK 4
#endif
Richard M. Stallman's avatar
Richard M. Stallman committed
115

116
#if defined (XENIX) || defined (WINDOWSNT)
Richard M. Stallman's avatar
Richard M. Stallman committed
117 118 119
#include <sys/locking.h>
#endif

120 121 122 123 124 125 126 127
#ifdef MAIL_USE_LOCKF
#define MAIL_USE_SYSTEM_LOCK
#endif

#ifdef MAIL_USE_FLOCK
#define MAIL_USE_SYSTEM_LOCK
#endif

128 129 130 131
#ifdef MAIL_USE_MMDF
extern int lk_open (), lk_close ();
#endif

132
#if !defined (MAIL_USE_SYSTEM_LOCK) && !defined (MAIL_USE_MMDF) && \
133 134
	(defined (HAVE_LIBMAIL) || defined (HAVE_LIBLOCKFILE)) && \
        defined (HAVE_MAILLOCK_H)
135 136 137 138 139 140 141 142 143
#include <maillock.h>
/* We can't use maillock unless we know what directory system mail
   files appear in. */
#ifdef MAILDIR
#define MAIL_USE_MAILLOCK
static char *mail_spool_name ();
#endif
#endif

David J. MacKenzie's avatar
David J. MacKenzie committed
144
#ifndef errno
Richard M. Stallman's avatar
Richard M. Stallman committed
145
extern int errno;
David J. MacKenzie's avatar
David J. MacKenzie committed
146
#endif
147
char *strerror ();
148 149 150 151 152 153
#ifdef HAVE_INDEX
extern char *index __P ((const char *, int));
#endif
#ifdef HAVE_RINDEX
extern char *rindex __P((const char *, int));
#endif
154 155 156 157 158 159

void fatal ();
void error ();
void pfatal_with_name ();
void pfatal_and_delete ();
char *concat ();
160
long *xmalloc ();
161 162 163 164 165
int popmail ();
int pop_retr ();
int mbx_write ();
int mbx_delimit_begin ();
int mbx_delimit_end ();
Richard M. Stallman's avatar
Richard M. Stallman committed
166 167 168 169

/* Nonzero means this is name of a lock file to delete on fatal error.  */
char *delete_lockname;

170
int
Richard M. Stallman's avatar
Richard M. Stallman committed
171 172 173 174 175 176 177
main (argc, argv)
     int argc;
     char **argv;
{
  char *inname, *outname;
  int indesc, outdesc;
  int nread;
Richard M. Stallman's avatar
Richard M. Stallman committed
178
  WAITTYPE status;
179
  int c, preserve_mail = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
180

181
#ifndef MAIL_USE_SYSTEM_LOCK
Richard M. Stallman's avatar
Richard M. Stallman committed
182 183 184 185
  struct stat st;
  long now;
  int tem;
  char *lockname, *p;
186
  char *tempname;
Richard M. Stallman's avatar
Richard M. Stallman committed
187
  int desc;
188
#endif /* not MAIL_USE_SYSTEM_LOCK */
Richard M. Stallman's avatar
Richard M. Stallman committed
189

190 191 192 193
#ifdef MAIL_USE_MAILLOCK
  char *spool_name;
#endif

194 195 196 197 198 199 200
#ifdef MAIL_USE_POP
  int pop_reverse_order = 0;
# define ARGSTR "pr"
#else /* ! MAIL_USE_POP */
# define ARGSTR "p"
#endif /* MAIL_USE_POP */

201 202 203 204 205
#ifdef WINDOWSNT
  /* Ensure all file i/o is in binary mode. */
  _fmode = _O_BINARY;
#endif

Richard M. Stallman's avatar
Richard M. Stallman committed
206 207
  delete_lockname = 0;

208
  while ((c = getopt (argc, argv, ARGSTR)) != EOF)
209
    {
210
      switch (c) {
211 212 213 214 215
#ifdef MAIL_USE_POP
      case 'r':
	pop_reverse_order = 1;
	break;
#endif
216 217 218 219
      case 'p':
	preserve_mail++;
	break;
      default:
220
	exit (EXIT_FAILURE);
221 222 223 224 225 226 227 228 229 230 231 232
      }
    }

  if (
#ifdef MAIL_USE_POP
      (argc - optind < 2) || (argc - optind > 3)
#else
      (argc - optind != 2)
#endif
      )
    {
#ifdef MAIL_USE_POP
233 234
      fprintf (stderr, "Usage: movemail [-p] inbox destfile%s\n",
	       " [POP-password]");
235
#else
236
      fprintf (stderr, "Usage: movemail [-p] inbox destfile%s\n", "");
237
#endif
238
      exit (EXIT_FAILURE);
239
    }
Richard M. Stallman's avatar
Richard M. Stallman committed
240

241 242
  inname = argv[optind];
  outname = argv[optind+1];
Richard M. Stallman's avatar
Richard M. Stallman committed
243

244 245 246 247
#ifdef MAIL_USE_MMDF
  mmdf_init (argv[0]);
#endif

248
  if (*outname == 0)
249
    fatal ("Destination file name is empty", 0, 0);
250

251
  /* Check access to output file.  */
Richard M. Stallman's avatar
Richard M. Stallman committed
252 253 254
  if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
    pfatal_with_name (outname);

Karl Heuer's avatar
Karl Heuer committed
255
  /* Also check that outname's directory is writable to the real uid.  */
Richard M. Stallman's avatar
Richard M. Stallman committed
256
  {
257
    char *buf = (char *) xmalloc (strlen (outname) + 1);
258
    char *p;
Richard M. Stallman's avatar
Richard M. Stallman committed
259 260
    strcpy (buf, outname);
    p = buf + strlen (buf);
261
    while (p > buf && !IS_DIRECTORY_SEP (p[-1]))
Richard M. Stallman's avatar
Richard M. Stallman committed
262 263 264 265 266 267 268 269 270
      *--p = 0;
    if (p == buf)
      *p++ = '.';
    if (access (buf, W_OK) != 0)
      pfatal_with_name (buf);
    free (buf);
  }

#ifdef MAIL_USE_POP
271
  if (!strncmp (inname, "po:", 3))
Richard M. Stallman's avatar
Richard M. Stallman committed
272
    {
273
      int status;
Richard M. Stallman's avatar
Richard M. Stallman committed
274

275
      status = popmail (inname + 3, outname, preserve_mail,
276 277
			(argc - optind == 3) ? argv[optind+2] : NULL,
			pop_reverse_order);
Richard M. Stallman's avatar
Richard M. Stallman committed
278 279 280
      exit (status);
    }

281
  setuid (getuid ());
Richard M. Stallman's avatar
Richard M. Stallman committed
282 283
#endif /* MAIL_USE_POP */

284 285
#ifndef DISABLE_DIRECT_ACCESS

286 287 288 289
  /* Check access to input file.  */
  if (access (inname, R_OK | W_OK) != 0)
    pfatal_with_name (inname);

290
#ifndef MAIL_USE_MMDF
291
#ifndef MAIL_USE_SYSTEM_LOCK
292 293 294 295
#ifdef MAIL_USE_MAILLOCK
  spool_name = mail_spool_name (inname);
  if (! spool_name)
#endif
Richard M. Stallman's avatar
Richard M. Stallman committed
296
    {
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
      /* Use a lock file named after our first argument with .lock appended:
	 If it exists, the mail file is locked.  */
      /* Note: this locking mechanism is *required* by the mailer
	 (on systems which use it) to prevent loss of mail.

	 On systems that use a lock file, extracting the mail without locking
	 WILL occasionally cause loss of mail due to timing errors!

	 So, if creation of the lock file fails
	 due to access permission on the mail spool directory,
	 you simply MUST change the permission
	 and/or make movemail a setgid program
	 so it can create lock files properly.

	 You might also wish to verify that your system is one
	 which uses lock files for this purpose.  Some systems use other methods.

	 If your system uses the `flock' system call for mail locking,
	 define MAIL_USE_SYSTEM_LOCK in config.h or the s-*.h file
	 and recompile movemail.  If the s- file for your system
	 should define MAIL_USE_SYSTEM_LOCK but does not, send a bug report
	 to bug-gnu-emacs@prep.ai.mit.edu so we can fix it.  */

      lockname = concat (inname, ".lock", "");
      tempname = (char *) xmalloc (strlen (inname) + strlen ("EXXXXXX") + 1);
      strcpy (tempname, inname);
      p = tempname + strlen (tempname);
      while (p != tempname && !IS_DIRECTORY_SEP (p[-1]))
	p--;
      *p = 0;
      strcpy (p, "EXXXXXX");
      mktemp (tempname);
329
      unlink (tempname);
Richard M. Stallman's avatar
Richard M. Stallman committed
330

331
      while (1)
Richard M. Stallman's avatar
Richard M. Stallman committed
332
	{
333 334 335 336 337 338
	  /* Create the lock file, but not under the lock file name.  */
	  /* Give up if cannot do that.  */
	  desc = open (tempname, O_WRONLY | O_CREAT | O_EXCL, 0666);
	  if (desc < 0)
	    {
	      char *message = (char *) xmalloc (strlen (tempname) + 50);
Richard M. Stallman's avatar
Richard M. Stallman committed
339
	      sprintf (message, "creating %s, which would become the lock file",
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
		       tempname);
	      pfatal_with_name (message);
	    }
	  close (desc);

	  tem = link (tempname, lockname);
	  unlink (tempname);
	  if (tem >= 0)
	    break;
	  sleep (1);

	  /* If lock file is five minutes old, unlock it.
	     Five minutes should be good enough to cope with crashes
	     and wedgitude, and long enough to avoid being fooled
	     by time differences between machines.  */
	  if (stat (lockname, &st) >= 0)
	    {
	      now = time (0);
	      if (st.st_ctime < now - 300)
		unlink (lockname);
	    }
Richard M. Stallman's avatar
Richard M. Stallman committed
361 362
	}

363 364
      delete_lockname = lockname;
    }
365 366
#endif /* not MAIL_USE_SYSTEM_LOCK */
#endif /* not MAIL_USE_MMDF */
Richard M. Stallman's avatar
Richard M. Stallman committed
367

Richard M. Stallman's avatar
Richard M. Stallman committed
368 369
  if (fork () == 0)
    {
370
      int lockcount = 0;
371 372 373 374
      int status = 0;
#if defined (MAIL_USE_MAILLOCK) && defined (HAVE_TOUCHLOCK)
      long touched_lock, now;
#endif
375

Karl Heuer's avatar
Karl Heuer committed
376
      setuid (getuid ());
Richard M. Stallman's avatar
Richard M. Stallman committed
377

378 379
#ifndef MAIL_USE_MMDF
#ifdef MAIL_USE_SYSTEM_LOCK
Richard M. Stallman's avatar
Richard M. Stallman committed
380
      indesc = open (inname, O_RDWR);
381
#else  /* if not MAIL_USE_SYSTEM_LOCK */
Richard M. Stallman's avatar
Richard M. Stallman committed
382
      indesc = open (inname, O_RDONLY);
383
#endif /* not MAIL_USE_SYSTEM_LOCK */
Richard M. Stallman's avatar
Richard M. Stallman committed
384 385
#else  /* MAIL_USE_MMDF */
      indesc = lk_open (inname, O_RDONLY, 0, 0, 10);
386 387
#endif /* MAIL_USE_MMDF */

Richard M. Stallman's avatar
Richard M. Stallman committed
388 389
      if (indesc < 0)
	pfatal_with_name (inname);
Richard M. Stallman's avatar
Richard M. Stallman committed
390

Karl Heuer's avatar
Karl Heuer committed
391
#if defined (BSD_SYSTEM) || defined (XENIX)
Richard M. Stallman's avatar
Richard M. Stallman committed
392 393 394 395 396
      /* In case movemail is setuid to root, make sure the user can
	 read the output file.  */
      /* This is desirable for all systems
	 but I don't want to assume all have the umask system call */
      umask (umask (0) & 0333);
Karl Heuer's avatar
Karl Heuer committed
397
#endif /* BSD_SYSTEM || XENIX */
Richard M. Stallman's avatar
Richard M. Stallman committed
398 399 400
      outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
      if (outdesc < 0)
	pfatal_with_name (outname);
401 402 403 404 405 406

      /* This label exists so we can retry locking
	 after a delay, if it got EAGAIN or EBUSY.  */
    retry_lock:

      /* Try to lock it.  */
407 408 409 410 411 412 413 414 415 416 417 418 419 420
#ifdef MAIL_USE_MAILLOCK
      if (spool_name)
	{
	  /* The "0 - " is to make it a negative number if maillock returns
	     non-zero. */
	  status = 0 - maillock (spool_name, 1);
#ifdef HAVE_TOUCHLOCK
	  touched_lock = time (0);
#endif
	  lockcount = 5;
	}
      else
#endif /* MAIL_USE_MAILLOCK */
	{
421 422
#ifdef MAIL_USE_SYSTEM_LOCK
#ifdef MAIL_USE_LOCKF
423
	  status = lockf (indesc, F_LOCK, 0);
424
#else /* not MAIL_USE_LOCKF */
Richard M. Stallman's avatar
Richard M. Stallman committed
425
#ifdef XENIX
426
	  status = locking (indesc, LK_RLCK, 0L);
427 428
#else
#ifdef WINDOWSNT
429
	  status = locking (indesc, LK_RLCK, -1L);
Richard M. Stallman's avatar
Richard M. Stallman committed
430
#else
431
	  status = flock (indesc, LOCK_EX);
Richard M. Stallman's avatar
Richard M. Stallman committed
432
#endif
433
#endif
434 435
#endif /* not MAIL_USE_LOCKF */
#endif /* MAIL_USE_SYSTEM_LOCK */
436
	}
Richard M. Stallman's avatar
Richard M. Stallman committed
437

438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
      /* If it fails, retry up to 5 times
	 for certain failure codes.  */
      if (status < 0)
	{
	  if (++lockcount <= 5)
	    {
#ifdef EAGAIN
	      if (errno == EAGAIN)
		{
		  sleep (1);
		  goto retry_lock;
		}
#endif
#ifdef EBUSY
	      if (errno == EBUSY)
		{
		  sleep (1);
		  goto retry_lock;
		}
#endif
	    }

	  pfatal_with_name (inname);
	}
462

Jim Blandy's avatar
Jim Blandy committed
463
      {
Richard M. Stallman's avatar
Richard M. Stallman committed
464 465 466
	char buf[1024];

	while (1)
Jim Blandy's avatar
Jim Blandy committed
467
	  {
Richard M. Stallman's avatar
Richard M. Stallman committed
468
	    nread = read (indesc, buf, sizeof buf);
469 470
	    if (nread < 0)
	      pfatal_with_name (inname);
Richard M. Stallman's avatar
Richard M. Stallman committed
471 472 473 474 475 476 477 478 479
	    if (nread != write (outdesc, buf, nread))
	      {
		int saved_errno = errno;
		unlink (outname);
		errno = saved_errno;
		pfatal_with_name (outname);
	      }
	    if (nread < sizeof buf)
	      break;
480 481 482 483 484 485 486 487 488 489 490
#if defined (MAIL_USE_MAILLOCK) && defined (HAVE_TOUCHLOCK)
	    if (spool_name)
	      {
		now = time (0);
		if (now - touched_lock > 60)
		  {
		    touchlock ();
		    touched_lock = now;
		  }
	      }
#endif /* MAIL_USE_MAILLOCK */
Jim Blandy's avatar
Jim Blandy committed
491 492
	  }
      }
Richard M. Stallman's avatar
Richard M. Stallman committed
493

Karl Heuer's avatar
Karl Heuer committed
494
#ifdef BSD_SYSTEM
Richard M. Stallman's avatar
Richard M. Stallman committed
495 496
      if (fsync (outdesc) < 0)
	pfatal_and_delete (outname);
Richard M. Stallman's avatar
Richard M. Stallman committed
497 498
#endif

Richard M. Stallman's avatar
Richard M. Stallman committed
499 500 501
      /* Check to make sure no errors before we zap the inbox.  */
      if (close (outdesc) != 0)
	pfatal_and_delete (outname);
Richard M. Stallman's avatar
Richard M. Stallman committed
502

503
#ifdef MAIL_USE_SYSTEM_LOCK
504 505
      if (! preserve_mail)
	{
506
#if defined (STRIDE) || defined (XENIX)
507 508 509
	  /* Stride, xenix have file locking, but no ftruncate.
	     This mess will do. */
	  close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
Richard M. Stallman's avatar
Richard M. Stallman committed
510
#else
511
	  ftruncate (indesc, 0L);
Richard M. Stallman's avatar
Richard M. Stallman committed
512
#endif /* STRIDE or XENIX */
513
	}
514
#endif /* MAIL_USE_SYSTEM_LOCK */
515 516

#ifdef MAIL_USE_MMDF
Richard M. Stallman's avatar
Richard M. Stallman committed
517
      lk_close (indesc, 0, 0, 0);
518
#else
Richard M. Stallman's avatar
Richard M. Stallman committed
519
      close (indesc);
520
#endif
Richard M. Stallman's avatar
Richard M. Stallman committed
521

522
#ifndef MAIL_USE_SYSTEM_LOCK
523 524 525 526
      if (! preserve_mail)
	{
	  /* Delete the input file; if we can't, at least get rid of its
	     contents.  */
David J. MacKenzie's avatar
David J. MacKenzie committed
527
#ifdef MAIL_UNLINK_SPOOL
528 529 530
	  /* This is generally bad to do, because it destroys the permissions
	     that were set on the file.  Better to just empty the file.  */
	  if (unlink (inname) < 0 && errno != ENOENT)
David J. MacKenzie's avatar
David J. MacKenzie committed
531
#endif /* MAIL_UNLINK_SPOOL */
532 533
	    creat (inname, 0600);
	}
534
#endif /* not MAIL_USE_SYSTEM_LOCK */
Richard M. Stallman's avatar
Richard M. Stallman committed
535

536 537 538 539 540 541
#ifdef MAIL_USE_MAILLOCK
      /* This has to occur in the child, i.e., in the process that
         acquired the lock! */
      if (spool_name)
	mailunlock ();
#endif
542
      exit (EXIT_SUCCESS);
Richard M. Stallman's avatar
Richard M. Stallman committed
543 544 545 546
    }

  wait (&status);
  if (!WIFEXITED (status))
547
    exit (EXIT_FAILURE);
Richard M. Stallman's avatar
Richard M. Stallman committed
548 549 550
  else if (WRETCODE (status) != 0)
    exit (WRETCODE (status));

551
#if !defined (MAIL_USE_MMDF) && !defined (MAIL_USE_SYSTEM_LOCK)
552 553 554 555
#ifdef MAIL_USE_MAILLOCK
  if (! spool_name)
#endif /* MAIL_USE_MAILLOCK */
    unlink (lockname);
556
#endif /* not MAIL_USE_MMDF and not MAIL_USE_SYSTEM_LOCK */
557 558 559

#endif /* ! DISABLE_DIRECT_ACCESS */

560
  return EXIT_SUCCESS;
Richard M. Stallman's avatar
Richard M. Stallman committed
561
}
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595

#ifdef MAIL_USE_MAILLOCK
/* This function uses stat to confirm that the mail directory is
   identical to the directory of the input file, rather than just
   string-comparing the two paths, because one or both of them might
   be symbolic links pointing to some other directory. */
static char *
mail_spool_name (inname)
     char *inname;
{
  struct stat stat1, stat2;
  char *indir, *fname;
  int status;

  if (! (fname = rindex (inname, '/')))
    return NULL;

  fname++;

  if (stat (MAILDIR, &stat1) < 0)
    return NULL;

  indir = (char *) xmalloc (fname - inname + 1);
  strncpy (indir, inname, fname - inname);
  indir[fname-inname] = '\0';


  status = stat (indir, &stat2);

  free (indir);

  if (status < 0)
    return NULL;

596 597
  if (stat1.st_dev == stat2.st_dev
      && stat1.st_ino == stat2.st_ino)
598 599 600 601 602
    return fname;

  return NULL;
}
#endif /* MAIL_USE_MAILLOCK */
Richard M. Stallman's avatar
Richard M. Stallman committed
603 604 605

/* Print error message and exit.  */

606
void
607 608
fatal (s1, s2, s3)
     char *s1, *s2, *s3;
Richard M. Stallman's avatar
Richard M. Stallman committed
609 610 611
{
  if (delete_lockname)
    unlink (delete_lockname);
612
  error (s1, s2, s3);
613
  exit (EXIT_FAILURE);
Richard M. Stallman's avatar
Richard M. Stallman committed
614 615
}

616 617
/* Print error message.  `s1' is printf control string, `s2' and `s3'
   are args for it or null. */
Richard M. Stallman's avatar
Richard M. Stallman committed
618

619
void
620 621
error (s1, s2, s3)
     char *s1, *s2, *s3;
Richard M. Stallman's avatar
Richard M. Stallman committed
622
{
623
  fprintf (stderr, "movemail: ");
624 625 626 627 628 629
  if (s3)
    fprintf (stderr, s1, s2, s3);
  else if (s2)
    fprintf (stderr, s1, s2);
  else
    fprintf (stderr, s1);
630
  fprintf (stderr, "\n");
Richard M. Stallman's avatar
Richard M. Stallman committed
631 632
}

633
void
Richard M. Stallman's avatar
Richard M. Stallman committed
634 635 636
pfatal_with_name (name)
     char *name;
{
637
  fatal ("%s for %s", strerror (errno), name);
Richard M. Stallman's avatar
Richard M. Stallman committed
638 639
}

640
void
641 642 643
pfatal_and_delete (name)
     char *name;
{
644
  char *s = strerror (errno);
645
  unlink (name);
646
  fatal ("%s for %s", s, name);
647 648
}

Richard M. Stallman's avatar
Richard M. Stallman committed
649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667
/* Return a newly-allocated string whose contents concatenate those of s1, s2, s3.  */

char *
concat (s1, s2, s3)
     char *s1, *s2, *s3;
{
  int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
  char *result = (char *) xmalloc (len1 + len2 + len3 + 1);

  strcpy (result, s1);
  strcpy (result + len1, s2);
  strcpy (result + len1 + len2, s3);
  *(result + len1 + len2 + len3) = 0;

  return result;
}

/* Like malloc but get fatal error if memory is exhausted.  */

668
long *
Richard M. Stallman's avatar
Richard M. Stallman committed
669
xmalloc (size)
David J. MacKenzie's avatar
David J. MacKenzie committed
670
     unsigned size;
Richard M. Stallman's avatar
Richard M. Stallman committed
671
{
672
  long *result = (long *) malloc (size);
Richard M. Stallman's avatar
Richard M. Stallman committed
673
  if (!result)
674
    fatal ("virtual memory exhausted", 0, 0);
Richard M. Stallman's avatar
Richard M. Stallman committed
675 676 677 678 679 680 681
  return result;
}

/* This is the guts of the interface to the Post Office Protocol.  */

#ifdef MAIL_USE_POP

682
#ifndef WINDOWSNT
Richard M. Stallman's avatar
Richard M. Stallman committed
683 684 685
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
686 687 688 689
#else
#undef _WINSOCKAPI_
#include <winsock.h>
#endif
690
#include <pwd.h>
Richard M. Stallman's avatar
Richard M. Stallman committed
691 692 693 694 695 696 697 698

#define NOTOK (-1)
#define OK 0
#define DONE 1

char *progname;
FILE *sfi;
FILE *sfo;
699 700
char ibuffer[BUFSIZ];
char obuffer[BUFSIZ];
701 702
char Errmsg[200];		/* POP errors, at least, can exceed
				   the original length of 80.  */
Richard M. Stallman's avatar
Richard M. Stallman committed
703

704 705 706 707 708 709 710 711 712 713
/*
 * The full legal syntax for a POP mailbox specification for movemail
 * is "po:username:hostname".  The ":hostname" is optional; if it is
 * omitted, the MAILHOST environment variable will be consulted.  Note
 * that by the time popmail() is called the "po:" has been stripped
 * off of the front of the mailbox name.
 *
 * If the mailbox is in the form "po:username:hostname", then it is
 * modified by this function -- the second colon is replaced by a
 * null.
714 715
 *
 * Return a value suitable for passing to `exit'.
716 717
 */

718
int
719 720
popmail (mailbox, outfile, preserve, password, reverse_order)
     char *mailbox;
721
     char *outfile;
722
     int preserve;
723
     char *password;
724
     int reverse_order;
Richard M. Stallman's avatar
Richard M. Stallman committed
725
{
726 727 728 729
  int nmsgs, nbytes;
  register int i;
  int mbfi;
  FILE *mbf;
730
  char *getenv ();
731
  popserver server;
732
  int start, end, increment;
733 734 735 736 737
  char *user, *hostname;

  user = mailbox;
  if ((hostname = index(mailbox, ':')))
    *hostname++ = '\0';
Richard M. Stallman's avatar
Richard M. Stallman committed
738

739
  server = pop_open (hostname, user, password, POP_NO_GETPASS);
740
  if (! server)
741
    {
742
      error ("Error connecting to POP server: %s", pop_error, 0);
743
      return EXIT_FAILURE;
Richard M. Stallman's avatar
Richard M. Stallman committed
744 745
    }

746
  if (pop_stat (server, &nmsgs, &nbytes))
747
    {
748
      error ("Error getting message count from POP server: %s", pop_error, 0);
749
      return EXIT_FAILURE;
Richard M. Stallman's avatar
Richard M. Stallman committed
750 751
    }

752 753
  if (!nmsgs)
    {
754
      pop_close (server);
755
      return EXIT_SUCCESS;
756 757 758 759 760
    }

  mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
  if (mbfi < 0)
    {
761 762
      pop_close (server);
      error ("Error in open: %s, %s", strerror (errno), outfile);
763
      return EXIT_FAILURE;
764 765 766
    }
  fchown (mbfi, getuid (), -1);

767
  if ((mbf = fdopen (mbfi, "wb")) == NULL)
768
    {
769
      pop_close (server);
770
      error ("Error in fdopen: %s", strerror (errno), 0);
771 772
      close (mbfi);
      unlink (outfile);
773
      return EXIT_FAILURE;
774 775
    }

776 777 778 779 780 781 782 783 784 785 786 787 788 789
  if (reverse_order)
    {
      start = nmsgs;
      end = 1;
      increment = -1;
    }
  else
    {
      start = 1;
      end = nmsgs;
      increment = 1;
    }

  for (i = start; i * increment <= end * increment; i += increment)
790 791
    {
      mbx_delimit_begin (mbf);
792
      if (pop_retr (server, i, mbf) != OK)
793
	{
794
	  error ("%s", Errmsg, 0);
795
	  close (mbfi);
796
	  return EXIT_FAILURE;
Richard M. Stallman's avatar
Richard M. Stallman committed
797
	}
798 799
      mbx_delimit_end (mbf);
      fflush (mbf);
800 801
      if (ferror (mbf))
	{
802
	  error ("Error in fflush: %s", strerror (errno), 0);
803 804
	  pop_close (server);
	  close (mbfi);
805
	  return EXIT_FAILURE;
806
	}
Richard M. Stallman's avatar
Richard M. Stallman committed
807 808
    }

809 810 811 812 813 814
  /* On AFS, a call to write only modifies the file in the local
   *     workstation's AFS cache.  The changes are not written to the server
   *      until a call to fsync or close is made.  Users with AFS home
   *      directories have lost mail when over quota because these checks were
   *      not made in previous versions of movemail. */

Karl Heuer's avatar
Karl Heuer committed
815
#ifdef BSD_SYSTEM
816 817
  if (fsync (mbfi) < 0)
    {
818
      error ("Error in fsync: %s", strerror (errno), 0);
819
      return EXIT_FAILURE;
820
    }
821
#endif
822 823 824

  if (close (mbfi) == -1)
    {
825
      error ("Error in close: %s", strerror (errno), 0);
826
      return EXIT_FAILURE;
827 828
    }

829 830 831 832 833
  if (! preserve)
    for (i = 1; i <= nmsgs; i++)
      {
	if (pop_delete (server, i))
	  {
834
	    error ("Error from POP server: %s", pop_error, 0);
835
	    pop_close (server);
836
	    return EXIT_FAILURE;
837 838
	  }
      }
Richard M. Stallman's avatar
Richard M. Stallman committed
839

840
  if (pop_quit (server))
841
    {
842
      error ("Error from POP server: %s", pop_error, 0);
843
      return EXIT_FAILURE;
Richard M. Stallman's avatar
Richard M. Stallman committed
844
    }
845

846
  return EXIT_SUCCESS;
Richard M. Stallman's avatar
Richard M. Stallman committed
847 848
}

849 850
int
pop_retr (server, msgno, arg)
851
     popserver server;
Dave Love's avatar
Dave Love committed
852
     int msgno;
853
     FILE *arg;
Richard M. Stallman's avatar
Richard M. Stallman committed
854
{
855 856 857
  extern char *strerror ();
  char *line;
  int ret;
Richard M. Stallman's avatar
Richard M. Stallman committed
858

859
  if (pop_retrieve_first (server, msgno, &line))
860
    {
861 862
      char *error = concat ("Error from POP server: ", pop_error, "");
      strncpy (Errmsg, error, sizeof (Errmsg));
863
      Errmsg[sizeof (Errmsg)-1] = '\0';
864
      free(error);
865
      return (NOTOK);
Richard M. Stallman's avatar
Richard M. Stallman committed
866 867
    }

868
  while ((ret = pop_retrieve_next (server, &line)) >= 0)
869
    {
870 871 872
      if (! line)
	break;

873
      if (mbx_write (line, ret, arg) != OK)
874
	{
875 876 877
	  strcpy (Errmsg, strerror (errno));
	  pop_close (server);
	  return (NOTOK);
Richard M. Stallman's avatar
Richard M. Stallman committed
878 879 880
	}
    }

881
  if (ret)
882
    {
883 884
      char *error = concat ("Error from POP server: ", pop_error, "");
      strncpy (Errmsg, error, sizeof (Errmsg));
885
      Errmsg[sizeof (Errmsg)-1] = '\0';
886
      free(error);
887
      return (NOTOK);
Richard M. Stallman's avatar
Richard M. Stallman committed
888 889
    }

890
  return (OK);
Richard M. Stallman's avatar
Richard M. Stallman committed
891 892
}

893 894 895 896 897 898
/* Do this as a macro instead of using strcmp to save on execution time. */
#define IS_FROM_LINE(a) ((a[0] == 'F') \
			 && (a[1] == 'r') \
			 && (a[2] == 'o') \
			 && (a[3] == 'm') \
			 && (a[4] == ' '))
Richard M. Stallman's avatar
Richard M. Stallman committed
899

900
int
901
mbx_write (line, len, mbf)
902
     char *line;
903
     int len;
904
     FILE *mbf;
Richard M. Stallman's avatar
Richard M. Stallman committed
905
{
906
#ifdef MOVEMAIL_QUOTE_POP_FROM_LINES
907 908 909 910 911
  if (IS_FROM_LINE (line))
    {
      if (fputc ('>', mbf) == EOF)
	return (NOTOK);
    }
912 913 914 915 916 917 918 919
#endif
  if (line[0] == '\037')
    {
      if (fputs ("^_", mbf) == EOF)
	return (NOTOK);
      line++;
      len--;
    }
920
  if (fwrite (line, 1, len, mbf) != len)
921 922 923 924
    return (NOTOK);
  if (fputc (0x0a, mbf) == EOF)
    return (NOTOK);
  return (OK);
Richard M. Stallman's avatar
Richard M. Stallman committed
925 926
}

927
int
928 929
mbx_delimit_begin (mbf)
     FILE *mbf;
Richard M. Stallman's avatar
Richard M. Stallman committed
930
{
931 932 933
  if (fputs ("\f\n0, unseen,,\n", mbf) == EOF)
    return (NOTOK);
  return (OK);
Richard M. Stallman's avatar
Richard M. Stallman committed
934 935
}

936
int