movemail.c 22.3 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 4 5

Copyright (C) 1986, 1992-1994, 1996, 1999, 2001-2011
  Free Software Foundation, Inc.
Richard M. Stallman's avatar
Richard M. Stallman committed
6 7 8

This file is part of GNU Emacs.

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

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

Joseph Arceneaux's avatar
Joseph Arceneaux committed
19
You should have received a copy of the GNU General Public License
20 21
along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */

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
#include <config.h>
Richard M. Stallman's avatar
Richard M. Stallman committed
59 60 61
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
62
#include <stdio.h>
Richard M. Stallman's avatar
Richard M. Stallman committed
63
#include <errno.h>
Chong Yidong's avatar
Chong Yidong committed
64
#include <time.h>
65

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

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

83
#ifdef WINDOWSNT
84
#include "ntlib.h"
85 86 87
#undef access
#undef unlink
#define fork() 0
Andrew Innes's avatar
Andrew Innes committed
88
#define wait(var) (*(var) = 0)
89 90 91 92
/* 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
93 94 95 96 97 98 99 100
   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>
101 102
#endif /* WINDOWSNT */

103 104 105 106 107 108
#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
109

110
#ifdef WINDOWSNT
Richard M. Stallman's avatar
Richard M. Stallman committed
111 112 113
#include <sys/locking.h>
#endif

114 115 116 117 118 119 120 121
#ifdef MAIL_USE_LOCKF
#define MAIL_USE_SYSTEM_LOCK
#endif

#ifdef MAIL_USE_FLOCK
#define MAIL_USE_SYSTEM_LOCK
#endif

122 123 124 125
#ifdef MAIL_USE_MMDF
extern int lk_open (), lk_close ();
#endif

126
#if !defined (MAIL_USE_SYSTEM_LOCK) && !defined (MAIL_USE_MMDF) && \
127 128
	(defined (HAVE_LIBMAIL) || defined (HAVE_LIBLOCKFILE)) && \
        defined (HAVE_MAILLOCK_H)
129 130 131 132 133 134 135 136 137
#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

Andreas Schwab's avatar
Andreas Schwab committed
138
#ifndef HAVE_STRERROR
139
char *strerror (int);
Andreas Schwab's avatar
Andreas Schwab committed
140
#endif
141

142 143
static void fatal (const char *s1, const char *s2, const char *s3) NO_RETURN;
static void error (const char *s1, const char *s2, const char *s3);
144 145
static void pfatal_with_name (char *name) NO_RETURN;
static void pfatal_and_delete (char *name) NO_RETURN;
146
static char *concat (const char *s1, const char *s2, const char *s3);
Andreas Schwab's avatar
Andreas Schwab committed
147
static long *xmalloc (unsigned int size);
148
#ifdef MAIL_USE_POP
Andreas Schwab's avatar
Andreas Schwab committed
149 150 151 152 153
static int popmail (char *mailbox, char *outfile, int preserve, char *password, int reverse_order);
static int pop_retr (popserver server, int msgno, FILE *arg);
static int mbx_write (char *line, int len, FILE *mbf);
static int mbx_delimit_begin (FILE *mbf);
static int mbx_delimit_end (FILE *mbf);
154
#endif
Richard M. Stallman's avatar
Richard M. Stallman committed
155 156

/* Nonzero means this is name of a lock file to delete on fatal error.  */
157
static char *delete_lockname;
Richard M. Stallman's avatar
Richard M. Stallman committed
158

159
int
160
main (int argc, char **argv)
Richard M. Stallman's avatar
Richard M. Stallman committed
161 162 163
{
  char *inname, *outname;
  int indesc, outdesc;
Dan Nicolaescu's avatar
Dan Nicolaescu committed
164
  ssize_t nread;
165
  int wait_status;
166
  int c, preserve_mail = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
167

168
#ifndef MAIL_USE_SYSTEM_LOCK
Richard M. Stallman's avatar
Richard M. Stallman committed
169 170 171 172
  struct stat st;
  long now;
  int tem;
  char *lockname, *p;
173
  char *tempname;
Richard M. Stallman's avatar
Richard M. Stallman committed
174
  int desc;
175
#endif /* not MAIL_USE_SYSTEM_LOCK */
Richard M. Stallman's avatar
Richard M. Stallman committed
176

177 178 179 180
#ifdef MAIL_USE_MAILLOCK
  char *spool_name;
#endif

181 182 183 184 185 186 187
#ifdef MAIL_USE_POP
  int pop_reverse_order = 0;
# define ARGSTR "pr"
#else /* ! MAIL_USE_POP */
# define ARGSTR "p"
#endif /* MAIL_USE_POP */

188 189 190
  uid_t real_gid = getgid();
  uid_t priv_gid = getegid();

191 192 193 194 195
#ifdef WINDOWSNT
  /* Ensure all file i/o is in binary mode. */
  _fmode = _O_BINARY;
#endif

Richard M. Stallman's avatar
Richard M. Stallman committed
196 197
  delete_lockname = 0;

198
  while ((c = getopt (argc, argv, ARGSTR)) != EOF)
199
    {
200
      switch (c) {
201 202 203 204 205
#ifdef MAIL_USE_POP
      case 'r':
	pop_reverse_order = 1;
	break;
#endif
206 207 208 209
      case 'p':
	preserve_mail++;
	break;
      default:
210
	exit (EXIT_FAILURE);
211 212 213 214 215 216 217 218 219 220 221 222
      }
    }

  if (
#ifdef MAIL_USE_POP
      (argc - optind < 2) || (argc - optind > 3)
#else
      (argc - optind != 2)
#endif
      )
    {
#ifdef MAIL_USE_POP
223
      fprintf (stderr, "Usage: movemail [-p] [-r] inbox destfile%s\n",
224
	       " [POP-password]");
225
#else
226
      fprintf (stderr, "Usage: movemail [-p] inbox destfile%s\n", "");
227
#endif
228
      exit (EXIT_FAILURE);
229
    }
Richard M. Stallman's avatar
Richard M. Stallman committed
230

231 232
  inname = argv[optind];
  outname = argv[optind+1];
Richard M. Stallman's avatar
Richard M. Stallman committed
233

234 235 236 237
#ifdef MAIL_USE_MMDF
  mmdf_init (argv[0]);
#endif

238
  if (*outname == 0)
239
    fatal ("Destination file name is empty", 0, 0);
240

Richard M. Stallman's avatar
Richard M. Stallman committed
241
#ifdef MAIL_USE_POP
242
  if (!strncmp (inname, "po:", 3))
Richard M. Stallman's avatar
Richard M. Stallman committed
243
    {
244
      int status;
Richard M. Stallman's avatar
Richard M. Stallman committed
245

246
      status = popmail (inname + 3, outname, preserve_mail,
247 248
			(argc - optind == 3) ? argv[optind+2] : NULL,
			pop_reverse_order);
Richard M. Stallman's avatar
Richard M. Stallman committed
249 250 251
      exit (status);
    }

252 253 254
  if (setuid (getuid ()) < 0)
    fatal ("Failed to drop privileges", 0, 0);

Richard M. Stallman's avatar
Richard M. Stallman committed
255 256
#endif /* MAIL_USE_POP */

257
#ifndef DISABLE_DIRECT_ACCESS
258
#ifndef MAIL_USE_MMDF
259
#ifndef MAIL_USE_SYSTEM_LOCK
260 261 262 263
#ifdef MAIL_USE_MAILLOCK
  spool_name = mail_spool_name (inname);
  if (! spool_name)
#endif
Richard M. Stallman's avatar
Richard M. Stallman committed
264
    {
265 266 267 268 269 270 271
      #ifndef DIRECTORY_SEP
       #define DIRECTORY_SEP '/'
      #endif
      #ifndef IS_DIRECTORY_SEP
       #define IS_DIRECTORY_SEP(_c_) ((_c_) == DIRECTORY_SEP)
      #endif

272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
      /* 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);
304
      unlink (tempname);
Richard M. Stallman's avatar
Richard M. Stallman committed
305

306
      while (1)
Richard M. Stallman's avatar
Richard M. Stallman committed
307
	{
308 309 310 311 312 313
	  /* 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
314
	      sprintf (message, "creating %s, which would become the lock file",
315 316 317 318 319 320
		       tempname);
	      pfatal_with_name (message);
	    }
	  close (desc);

	  tem = link (tempname, lockname);
321 322 323 324 325 326 327

#ifdef EPERM
	  if (tem < 0 && errno == EPERM)
	    fatal ("Unable to create hard link between %s and %s",
		   tempname, lockname);
#endif

328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
	  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
343 344
	}

345 346
      delete_lockname = lockname;
    }
347 348
#endif /* not MAIL_USE_SYSTEM_LOCK */
#endif /* not MAIL_USE_MMDF */
Richard M. Stallman's avatar
Richard M. Stallman committed
349

Richard M. Stallman's avatar
Richard M. Stallman committed
350 351
  if (fork () == 0)
    {
352
      int lockcount = 0;
353 354
      int status = 0;
#if defined (MAIL_USE_MAILLOCK) && defined (HAVE_TOUCHLOCK)
Chong Yidong's avatar
Chong Yidong committed
355
      time_t touched_lock, now;
356
#endif
357

358
      if (setuid (getuid ()) < 0 || setregid (-1, real_gid) < 0)
359
	fatal ("Failed to drop privileges", 0, 0);
Richard M. Stallman's avatar
Richard M. Stallman committed
360

361 362
#ifndef MAIL_USE_MMDF
#ifdef MAIL_USE_SYSTEM_LOCK
Richard M. Stallman's avatar
Richard M. Stallman committed
363
      indesc = open (inname, O_RDWR);
364
#else  /* if not MAIL_USE_SYSTEM_LOCK */
Richard M. Stallman's avatar
Richard M. Stallman committed
365
      indesc = open (inname, O_RDONLY);
366
#endif /* not MAIL_USE_SYSTEM_LOCK */
Richard M. Stallman's avatar
Richard M. Stallman committed
367 368
#else  /* MAIL_USE_MMDF */
      indesc = lk_open (inname, O_RDONLY, 0, 0, 10);
369 370
#endif /* MAIL_USE_MMDF */

Richard M. Stallman's avatar
Richard M. Stallman committed
371 372
      if (indesc < 0)
	pfatal_with_name (inname);
Richard M. Stallman's avatar
Richard M. Stallman committed
373

374
#ifdef BSD_SYSTEM
Richard M. Stallman's avatar
Richard M. Stallman committed
375 376 377 378 379
      /* 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);
380
#endif /* BSD_SYSTEM */
Richard M. Stallman's avatar
Richard M. Stallman committed
381 382 383
      outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
      if (outdesc < 0)
	pfatal_with_name (outname);
384

385
      if (setregid (-1, priv_gid) < 0)
386 387
	fatal ("Failed to regain privileges", 0, 0);

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

      /* Try to lock it.  */
393 394 395 396 397 398 399 400 401 402 403 404 405 406
#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 */
	{
407 408
#ifdef MAIL_USE_SYSTEM_LOCK
#ifdef MAIL_USE_LOCKF
409
	  status = lockf (indesc, F_LOCK, 0);
410
#else /* not MAIL_USE_LOCKF */
411
#ifdef WINDOWSNT
412
	  status = locking (indesc, LK_RLCK, -1L);
Richard M. Stallman's avatar
Richard M. Stallman committed
413
#else
414
	  status = flock (indesc, LOCK_EX);
Richard M. Stallman's avatar
Richard M. Stallman committed
415
#endif
416 417
#endif /* not MAIL_USE_LOCKF */
#endif /* MAIL_USE_SYSTEM_LOCK */
418
	}
Richard M. Stallman's avatar
Richard M. Stallman committed
419

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
      /* 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);
	}
444

Jim Blandy's avatar
Jim Blandy committed
445
      {
Richard M. Stallman's avatar
Richard M. Stallman committed
446 447 448
	char buf[1024];

	while (1)
Jim Blandy's avatar
Jim Blandy committed
449
	  {
Richard M. Stallman's avatar
Richard M. Stallman committed
450
	    nread = read (indesc, buf, sizeof buf);
451 452
	    if (nread < 0)
	      pfatal_with_name (inname);
Richard M. Stallman's avatar
Richard M. Stallman committed
453 454 455 456 457 458 459 460 461
	    if (nread != write (outdesc, buf, nread))
	      {
		int saved_errno = errno;
		unlink (outname);
		errno = saved_errno;
		pfatal_with_name (outname);
	      }
	    if (nread < sizeof buf)
	      break;
462 463 464 465 466 467 468 469 470 471 472
#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
473 474
	  }
      }
Richard M. Stallman's avatar
Richard M. Stallman committed
475

Karl Heuer's avatar
Karl Heuer committed
476
#ifdef BSD_SYSTEM
Richard M. Stallman's avatar
Richard M. Stallman committed
477 478
      if (fsync (outdesc) < 0)
	pfatal_and_delete (outname);
Richard M. Stallman's avatar
Richard M. Stallman committed
479 480
#endif

481
      /* Prevent symlink attacks truncating other users' mailboxes */
482
      if (setregid (-1, real_gid) < 0)
483 484
	fatal ("Failed to drop privileges", 0, 0);

Richard M. Stallman's avatar
Richard M. Stallman committed
485 486 487
      /* 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
488

489
#ifdef MAIL_USE_SYSTEM_LOCK
490 491
      if (! preserve_mail)
	{
492 493
	  if (ftruncate (indesc, 0L) != 0)
	    pfatal_with_name (inname);
494
	}
495
#endif /* MAIL_USE_SYSTEM_LOCK */
496 497

#ifdef MAIL_USE_MMDF
Richard M. Stallman's avatar
Richard M. Stallman committed
498
      lk_close (indesc, 0, 0, 0);
499
#else
Richard M. Stallman's avatar
Richard M. Stallman committed
500
      close (indesc);
501
#endif
Richard M. Stallman's avatar
Richard M. Stallman committed
502

503
#ifndef MAIL_USE_SYSTEM_LOCK
504 505 506 507
      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
508
#ifdef MAIL_UNLINK_SPOOL
509 510 511
	  /* 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
512
#endif /* MAIL_UNLINK_SPOOL */
513 514
	    creat (inname, 0600);
	}
515
#endif /* not MAIL_USE_SYSTEM_LOCK */
Richard M. Stallman's avatar
Richard M. Stallman committed
516

517
      /* End of mailbox truncation */
518
      if (setregid (-1, priv_gid) < 0)
519 520
	fatal ("Failed to regain privileges", 0, 0);

521 522 523 524 525 526
#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
527
      exit (EXIT_SUCCESS);
Richard M. Stallman's avatar
Richard M. Stallman committed
528 529
    }

530 531
  wait (&wait_status);
  if (!WIFEXITED (wait_status))
532
    exit (EXIT_FAILURE);
533 534
  else if (WRETCODE (wait_status) != 0)
    exit (WRETCODE (wait_status));
Richard M. Stallman's avatar
Richard M. Stallman committed
535

536
#if !defined (MAIL_USE_MMDF) && !defined (MAIL_USE_SYSTEM_LOCK)
537 538 539 540
#ifdef MAIL_USE_MAILLOCK
  if (! spool_name)
#endif /* MAIL_USE_MAILLOCK */
    unlink (lockname);
541
#endif /* not MAIL_USE_MMDF and not MAIL_USE_SYSTEM_LOCK */
542 543 544

#endif /* ! DISABLE_DIRECT_ACCESS */

545
  return EXIT_SUCCESS;
Richard M. Stallman's avatar
Richard M. Stallman committed
546
}
547 548 549 550 551 552 553

#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 *
Dan Nicolaescu's avatar
Dan Nicolaescu committed
554
mail_spool_name (char *inname)
555 556 557 558 559
{
  struct stat stat1, stat2;
  char *indir, *fname;
  int status;

560
  if (! (fname = strrchr (inname, '/')))
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
    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;

580 581
  if (stat1.st_dev == stat2.st_dev
      && stat1.st_ino == stat2.st_ino)
582 583 584 585 586
    return fname;

  return NULL;
}
#endif /* MAIL_USE_MAILLOCK */
Richard M. Stallman's avatar
Richard M. Stallman committed
587 588 589

/* Print error message and exit.  */

Andreas Schwab's avatar
Andreas Schwab committed
590
static void
591
fatal (const char *s1, const char *s2, const char *s3)
Richard M. Stallman's avatar
Richard M. Stallman committed
592 593 594
{
  if (delete_lockname)
    unlink (delete_lockname);
595
  error (s1, s2, s3);
596
  exit (EXIT_FAILURE);
Richard M. Stallman's avatar
Richard M. Stallman committed
597 598
}

599 600
/* 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
601

Andreas Schwab's avatar
Andreas Schwab committed
602
static void
603
error (const char *s1, const char *s2, const char *s3)
Richard M. Stallman's avatar
Richard M. Stallman committed
604
{
605
  fprintf (stderr, "movemail: ");
606 607 608 609 610
  if (s3)
    fprintf (stderr, s1, s2, s3);
  else if (s2)
    fprintf (stderr, s1, s2);
  else
611
    fprintf (stderr, "%s", s1);
612
  fprintf (stderr, "\n");
Richard M. Stallman's avatar
Richard M. Stallman committed
613 614
}

Andreas Schwab's avatar
Andreas Schwab committed
615
static void
616
pfatal_with_name (char *name)
Richard M. Stallman's avatar
Richard M. Stallman committed
617
{
618
  fatal ("%s for %s", strerror (errno), name);
Richard M. Stallman's avatar
Richard M. Stallman committed
619 620
}

Andreas Schwab's avatar
Andreas Schwab committed
621
static void
622
pfatal_and_delete (char *name)
623
{
624
  char *s = strerror (errno);
625
  unlink (name);
626
  fatal ("%s for %s", s, name);
627 628
}

Richard M. Stallman's avatar
Richard M. Stallman committed
629 630
/* Return a newly-allocated string whose contents concatenate those of s1, s2, s3.  */

Andreas Schwab's avatar
Andreas Schwab committed
631
static char *
632
concat (const char *s1, const char *s2, const char *s3)
Richard M. Stallman's avatar
Richard M. Stallman committed
633
{
Dan Nicolaescu's avatar
Dan Nicolaescu committed
634
  size_t len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
Richard M. Stallman's avatar
Richard M. Stallman committed
635 636 637 638 639 640 641 642 643 644 645 646
  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.  */

Andreas Schwab's avatar
Andreas Schwab committed
647
static long *
648
xmalloc (unsigned int size)
Richard M. Stallman's avatar
Richard M. Stallman committed
649
{
650
  long *result = (long *) malloc (size);
Richard M. Stallman's avatar
Richard M. Stallman committed
651
  if (!result)
652
    fatal ("virtual memory exhausted", 0, 0);
Richard M. Stallman's avatar
Richard M. Stallman committed
653 654 655 656 657 658 659
  return result;
}

/* This is the guts of the interface to the Post Office Protocol.  */

#ifdef MAIL_USE_POP

660
#ifndef WINDOWSNT
Richard M. Stallman's avatar
Richard M. Stallman committed
661 662 663
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
664 665 666 667
#else
#undef _WINSOCKAPI_
#include <winsock.h>
#endif
668
#include <pwd.h>
669
#include <string.h>
Richard M. Stallman's avatar
Richard M. Stallman committed
670 671 672 673

#define NOTOK (-1)
#define OK 0

674
static char Errmsg[200];	/* POP errors, at least, can exceed
675
				   the original length of 80.  */
Richard M. Stallman's avatar
Richard M. Stallman committed
676

677
/*
Juanma Barranquero's avatar
Juanma Barranquero committed
678
 * The full valid syntax for a POP mailbox specification for movemail
679 680 681 682 683 684 685 686
 * 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.
687 688
 *
 * Return a value suitable for passing to `exit'.
689 690
 */

Andreas Schwab's avatar
Andreas Schwab committed
691
static int
692
popmail (char *mailbox, char *outfile, int preserve, char *password, int reverse_order)
Richard M. Stallman's avatar
Richard M. Stallman committed
693
{
694 695 696 697
  int nmsgs, nbytes;
  register int i;
  int mbfi;
  FILE *mbf;
698
  char *getenv (const char *);
699
  popserver server;
700
  int start, end, increment;
701 702 703
  char *user, *hostname;

  user = mailbox;
704
  if ((hostname = strchr (mailbox, ':')))
705
    *hostname++ = '\0';
Richard M. Stallman's avatar
Richard M. Stallman committed
706

707
  server = pop_open (hostname, user, password, POP_NO_GETPASS);
708
  if (! server)
709
    {
710
      error ("Error connecting to POP server: %s", pop_error, 0);
711
      return EXIT_FAILURE;
Richard M. Stallman's avatar
Richard M. Stallman committed
712 713
    }

714
  if (pop_stat (server, &nmsgs, &nbytes))
715
    {
716
      error ("Error getting message count from POP server: %s", pop_error, 0);
717
      return EXIT_FAILURE;
Richard M. Stallman's avatar
Richard M. Stallman committed
718 719
    }

720 721
  if (!nmsgs)
    {
722
      pop_close (server);
723
      return EXIT_SUCCESS;
724 725 726 727 728
    }

  mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
  if (mbfi < 0)
    {
729 730
      pop_close (server);
      error ("Error in open: %s, %s", strerror (errno), outfile);
731
      return EXIT_FAILURE;
732
    }
733 734 735 736 737 738 739 740 741 742 743 744

  if (fchown (mbfi, getuid (), -1) != 0)
    {
      int fchown_errno = errno;
      struct stat st;
      if (fstat (mbfi, &st) != 0 || st.st_uid != getuid ())
	{
	  pop_close (server);
	  error ("Error in fchown: %s, %s", strerror (fchown_errno), outfile);
	  return EXIT_FAILURE;
	}
    }
745

746
  if ((mbf = fdopen (mbfi, "wb")) == NULL)
747
    {
748
      pop_close (server);
749
      error ("Error in fdopen: %s", strerror (errno), 0);
750 751
      close (mbfi);
      unlink (outfile);
752
      return EXIT_FAILURE;
753 754
    }

755 756 757 758 759 760 761 762 763 764 765 766 767 768
  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)
769 770
    {
      mbx_delimit_begin (mbf);
771
      if (pop_retr (server, i, mbf) != OK)
772
	{
773
	  error ("%s", Errmsg, 0);
774
	  close (mbfi);
775
	  return EXIT_FAILURE;
Richard M. Stallman's avatar
Richard M. Stallman committed
776
	}
777 778
      mbx_delimit_end (mbf);
      fflush (mbf);
779 780
      if (ferror (mbf))
	{
781
	  error ("Error in fflush: %s", strerror (errno), 0);
782 783
	  pop_close (server);
	  close (mbfi);
784
	  return EXIT_FAILURE;
785
	}
Richard M. Stallman's avatar
Richard M. Stallman committed
786 787
    }

788 789 790 791 792 793
  /* 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
794
#ifdef BSD_SYSTEM
795 796
  if (fsync (mbfi) < 0)
    {
797
      error ("Error in fsync: %s", strerror (errno), 0);
798
      return EXIT_FAILURE;
799
    }
800
#endif
801 802 803

  if (close (mbfi) == -1)
    {
804
      error ("Error in close: %s", strerror (errno), 0);
805
      return EXIT_FAILURE;
806 807
    }

808 809 810 811 812
  if (! preserve)
    for (i = 1; i <= nmsgs; i++)
      {
	if (pop_delete (server, i))
	  {
813
	    error ("Error from POP server: %s", pop_error, 0);
814
	    pop_close (server);
815
	    return EXIT_FAILURE;
816 817
	  }
      }
Richard M. Stallman's avatar
Richard M. Stallman committed
818

819
  if (pop_quit (server))
820
    {
821
      error ("Error from POP server: %s", pop_error, 0);
822
      return EXIT_FAILURE;
Richard M. Stallman's avatar
Richard M. Stallman committed
823
    }
824

825
  return EXIT_SUCCESS;
Richard M. Stallman's avatar
Richard M. Stallman committed
826 827
}

Andreas Schwab's avatar
Andreas Schwab committed
828
static int
829
pop_retr (popserver server, int msgno, FILE *arg)
Richard M. Stallman's avatar
Richard M. Stallman committed
830
{
831 832
  char *line;
  int ret;
Richard M. Stallman's avatar
Richard M. Stallman committed
833

834
  if (pop_retrieve_first (server, msgno, &line))
835
    {
836 837
      char *msg = concat ("Error from POP server: ", pop_error, "");
      strncpy (Errmsg, msg, sizeof (Errmsg));
838
      Errmsg[sizeof (Errmsg)-1] = '\0';
839
      free (msg);
840
      return (NOTOK);
Richard M. Stallman's avatar
Richard M. Stallman committed
841 842
    }

843
  while ((ret = pop_retrieve_next (server, &line)) >= 0)
844
    {
845 846 847
      if (! line)
	break;

848
      if (mbx_write (line, ret, arg) != OK)
849
	{
850 851 852
	  strcpy (Errmsg, strerror (errno));
	  pop_close (server);
	  return (NOTOK);
Richard M. Stallman's avatar
Richard M. Stallman committed
853 854 855
	}
    }

856
  if (ret)
857
    {
858 859
      char *msg = concat ("Error from POP server: ", pop_error, "");
      strncpy (Errmsg, msg, sizeof (Errmsg));
860
      Errmsg[sizeof (Errmsg)-1] = '\0';
861
      free (msg);
862
      return (NOTOK);
Richard M. Stallman's avatar
Richard M. Stallman committed
863 864
    }

865
  return (OK);
Richard M. Stallman's avatar
Richard M. Stallman committed
866 867
}

Andreas Schwab's avatar
Andreas Schwab committed
868
static int
869
mbx_write (char *line, int len, FILE *mbf)
Richard M. Stallman's avatar
Richard M. Stallman committed
870
{
871
#ifdef MOVEMAIL_QUOTE_POP_FROM_LINES
872 873 874 875 876 877
  /* 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] == ' '))
878 879 880 881 882
  if (IS_FROM_LINE (line))
    {
      if (fputc ('>', mbf) == EOF)
	return (NOTOK);
    }
883 884 885 886 887 888 889 890
#endif
  if (line[0] == '\037')
    {
      if (fputs ("^_", mbf) == EOF)
	return (NOTOK);
      line++;
      len--;
    }
891
  if (fwrite (line, 1, len, mbf) != len)
892 893 894 895
    return (NOTOK);
  if (fputc (0x0a, mbf) == EOF)
    return (NOTOK);
  return (OK);
Richard M. Stallman's avatar
Richard M. Stallman committed
896 897
}

Andreas Schwab's avatar
Andreas Schwab committed
898
static int
899
mbx_delimit_begin (FILE *mbf)
Richard M. Stallman's avatar
Richard M. Stallman committed
900
{
901 902 903 904 905 906 907 908 909 910
  time_t now;
  struct tm *ltime;
  char fromline[40] = "From movemail ";

  now = time (NULL);
  ltime = localtime (&now);

  strcat (fromline, asctime (ltime));

  if (fputs (fromline, mbf) == EOF)
911 912
    return (NOTOK);
  return (OK);
Richard M. Stallman's avatar
Richard M. Stallman committed
913 914
}

Andreas Schwab's avatar
Andreas Schwab committed
915
static int
916
mbx_delimit_end (FILE *mbf)
Richard M. Stallman's avatar
Richard M. Stallman committed
917
{
918
  if (putc ('\n', mbf) == EOF)
919 920
    return (NOTOK);
  return (OK);
Richard M. Stallman's avatar
Richard M. Stallman committed
921 922 923
}

#endif /* MAIL_USE_POP */
924 925 926 927 928 929 930 931 932 933 934 935 936 937 938

#ifndef HAVE_STRERROR
char *
strerror (errnum)
     int errnum;
{
  extern char *sys_errlist[];
  extern int sys_nerr;

  if (errnum >= 0 && errnum < sys_nerr)
    return sys_errlist[errnum];
  return (char *) "Unknown error";
}

#endif /* ! HAVE_STRERROR */
Miles Bader's avatar
Miles Bader committed
939

940 941

/* movemail.c ends here */