]> code.delx.au - gnu-emacs/blobdiff - lib-src/movemail.c
(Program Modes): Replace inforef to emacs-xtra by conditional xref's, depending
[gnu-emacs] / lib-src / movemail.c
index 67dca3ad6a9dda24881effa0d612b34d70b33f58..1f73ee88ba88a38c6492e42584934686eec1a983 100644 (file)
@@ -1,6 +1,7 @@
 /* movemail foo bar -- move file foo to file bar,
    locking file foo the way /bin/mail respects.
-   Copyright (C) 1986, 1992, 1993, 1994 Free Software Foundation, Inc.
+   Copyright (C) 1986, 1992, 1993, 1994, 1996, 1999, 2002, 2003, 2004,
+                 2005, 2006 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -16,13 +17,14 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GNU Emacs; see the file COPYING.  If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
 
-/* Important notice: defining MAIL_USE_FLOCK *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
-   *must agree* with the system's own conventions.
-   It is not a choice that is up to you.
+/* 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.
 
    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.
@@ -33,67 +35,139 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 /*
  * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
  *
- * Added POP (Post Office Protocol) service.  When compiled -DPOP
+ * Added POP (Post Office Protocol) service.  When compiled -DMAIL_USE_POP
  * 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.
- * 
+ *
  * New module: popmail.c
  * Modified routines:
  *     main - added code within #ifdef MAIL_USE_POP; added setuid (getuid ())
- *             after POP code. 
+ *             after POP code.
  * New routines in movemail.c:
  *     get_errmsg - return pointer to system error message
  *
+ * 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.
+ *
  */
 
+#define NO_SHORTNAMES   /* Tell config not to load remap.h */
+#include <config.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/file.h>
+#include <stdio.h>
 #include <errno.h>
-#define NO_SHORTNAMES   /* Tell config not to load remap.h */
-#include <../src/config.h>
-#include <../src/syswait.h>
+
+#include <getopt.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include "syswait.h"
+#ifdef MAIL_USE_POP
+#include "pop.h"
+#endif
 
 #ifdef MSDOS
 #undef access
 #endif /* MSDOS */
 
-#ifdef USG
+#ifndef DIRECTORY_SEP
+#define DIRECTORY_SEP '/'
+#endif
+#ifndef IS_DIRECTORY_SEP
+#define IS_DIRECTORY_SEP(_c_) ((_c_) == DIRECTORY_SEP)
+#endif
+
+#ifdef WINDOWSNT
+#include "ntlib.h"
+#undef access
+#undef unlink
+#define fork() 0
+#define wait(var) (*(var) = 0)
+/* 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
+   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>
-#include <unistd.h>
+#endif /* WINDOWSNT */
+
 #ifndef F_OK
 #define F_OK 0
 #define X_OK 1
 #define W_OK 2
 #define R_OK 4
 #endif
-#endif /* USG */
 
-#ifdef XENIX
+#if defined (XENIX) || defined (WINDOWSNT)
 #include <sys/locking.h>
 #endif
 
+#ifdef MAIL_USE_LOCKF
+#define MAIL_USE_SYSTEM_LOCK
+#endif
+
+#ifdef MAIL_USE_FLOCK
+#define MAIL_USE_SYSTEM_LOCK
+#endif
+
 #ifdef MAIL_USE_MMDF
 extern int lk_open (), lk_close ();
 #endif
 
-/* Cancel substitutions made by config.h for Emacs.  */
-#undef open
-#undef read
-#undef write
-#undef close
+#if !defined (MAIL_USE_SYSTEM_LOCK) && !defined (MAIL_USE_MMDF) && \
+       (defined (HAVE_LIBMAIL) || defined (HAVE_LIBLOCKFILE)) && \
+        defined (HAVE_MAILLOCK_H)
+#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
 
-char *concat ();
-char *xmalloc ();
 #ifndef errno
 extern int errno;
 #endif
+char *strerror ();
+#ifdef HAVE_INDEX
+extern char *index __P ((const char *, int));
+#endif
+#ifdef HAVE_RINDEX
+extern char *rindex __P((const char *, int));
+#endif
+
+void fatal ();
+void error ();
+void pfatal_with_name ();
+void pfatal_and_delete ();
+char *concat ();
+long *xmalloc ();
+int popmail ();
+int pop_retr ();
+int mbx_write ();
+int mbx_delimit_begin ();
+int mbx_delimit_end ();
 
 /* Nonzero means this is name of a lock file to delete on fatal error.  */
 char *delete_lockname;
 
+int
 main (argc, argv)
      int argc;
      char **argv;
@@ -102,39 +176,89 @@ main (argc, argv)
   int indesc, outdesc;
   int nread;
   WAITTYPE status;
+  int c, preserve_mail = 0;
 
-#ifndef MAIL_USE_FLOCK
+#ifndef MAIL_USE_SYSTEM_LOCK
   struct stat st;
   long now;
   int tem;
   char *lockname, *p;
   char *tempname;
   int desc;
-#endif /* not MAIL_USE_FLOCK */
+#endif /* not MAIL_USE_SYSTEM_LOCK */
+
+#ifdef MAIL_USE_MAILLOCK
+  char *spool_name;
+#endif
+
+#ifdef MAIL_USE_POP
+  int pop_reverse_order = 0;
+# define ARGSTR "pr"
+#else /* ! MAIL_USE_POP */
+# define ARGSTR "p"
+#endif /* MAIL_USE_POP */
+
+#ifdef WINDOWSNT
+  /* Ensure all file i/o is in binary mode. */
+  _fmode = _O_BINARY;
+#endif
 
   delete_lockname = 0;
 
-  if (argc < 3)
-    fatal ("two arguments required");
+  while ((c = getopt (argc, argv, ARGSTR)) != EOF)
+    {
+      switch (c) {
+#ifdef MAIL_USE_POP
+      case 'r':
+       pop_reverse_order = 1;
+       break;
+#endif
+      case 'p':
+       preserve_mail++;
+       break;
+      default:
+       exit (EXIT_FAILURE);
+      }
+    }
+
+  if (
+#ifdef MAIL_USE_POP
+      (argc - optind < 2) || (argc - optind > 3)
+#else
+      (argc - optind != 2)
+#endif
+      )
+    {
+#ifdef MAIL_USE_POP
+      fprintf (stderr, "Usage: movemail [-p] inbox destfile%s\n",
+              " [POP-password]");
+#else
+      fprintf (stderr, "Usage: movemail [-p] inbox destfile%s\n", "");
+#endif
+      exit (EXIT_FAILURE);
+    }
 
-  inname = argv[1];
-  outname = argv[2];
+  inname = argv[optind];
+  outname = argv[optind+1];
 
 #ifdef MAIL_USE_MMDF
   mmdf_init (argv[0]);
 #endif
 
+  if (*outname == 0)
+    fatal ("Destination file name is empty", 0, 0);
+
   /* Check access to output file.  */
   if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
     pfatal_with_name (outname);
 
-  /* Also check that outname's directory is writeable to the real uid.  */
+  /* Also check that outname's directory is writable to the real uid.  */
   {
     char *buf = (char *) xmalloc (strlen (outname) + 1);
-    char *p, q;
+    char *p;
     strcpy (buf, outname);
     p = buf + strlen (buf);
-    while (p > buf && p[-1] != '/')
+    while (p > buf && !IS_DIRECTORY_SEP (p[-1]))
       *--p = 0;
     if (p == buf)
       *p++ = '.';
@@ -146,95 +270,117 @@ main (argc, argv)
 #ifdef MAIL_USE_POP
   if (!strncmp (inname, "po:", 3))
     {
-      int status; char *user;
-
-      for (user = &inname[strlen (inname) - 1]; user >= inname; user--)
-       if (*user == ':')
-         break;
+      int status;
 
-      status = popmail (user, outname);
+      status = popmail (inname + 3, outname, preserve_mail,
+                       (argc - optind == 3) ? argv[optind+2] : NULL,
+                       pop_reverse_order);
       exit (status);
     }
 
   setuid (getuid ());
 #endif /* MAIL_USE_POP */
 
+#ifndef DISABLE_DIRECT_ACCESS
+
   /* Check access to input file.  */
   if (access (inname, R_OK | W_OK) != 0)
     pfatal_with_name (inname);
 
 #ifndef MAIL_USE_MMDF
-#ifndef MAIL_USE_FLOCK
-  /* Use a lock file named /usr/spool/mail/$USER.lock:
-     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 /usr/spool/mail,
-     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_FLOCK in config.h or the s-*.h file
-     and recompile movemail.  If the s- file for your system
-     should define MAIL_USE_FLOCK 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 && p[-1] != '/')
-    p--;
-  *p = 0;
-  strcpy (p, "EXXXXXX");
-  mktemp (tempname);
-  unlink (tempname);
-
-  while (1)
+#ifndef MAIL_USE_SYSTEM_LOCK
+#ifdef MAIL_USE_MAILLOCK
+  spool_name = mail_spool_name (inname);
+  if (! spool_name)
+#endif
     {
-      /* 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)
-       pfatal_with_name ("lock file--see source file lib-src/movemail.c");
-      close (desc);
-
-      tem = link (tempname, lockname);
+      /* 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);
       unlink (tempname);
-      if (tem >= 0)
-       break;
-      sleep (1);
 
-      /* If lock file is a minute old, unlock it.  */
-      if (stat (lockname, &st) >= 0)
+      while (1)
        {
-         now = time (0);
-         if (st.st_ctime < now - 60)
-           unlink (lockname);
+         /* 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);
+             sprintf (message, "creating %s, which would become the lock file",
+                      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);
+           }
        }
-    }
 
-  delete_lockname = lockname;
-#endif /* not MAIL_USE_FLOCK */
+      delete_lockname = lockname;
+    }
+#endif /* not MAIL_USE_SYSTEM_LOCK */
+#endif /* not MAIL_USE_MMDF */
 
   if (fork () == 0)
     {
-      seteuid (getuid ());
+      int lockcount = 0;
+      int status = 0;
+#if defined (MAIL_USE_MAILLOCK) && defined (HAVE_TOUCHLOCK)
+      long touched_lock, now;
+#endif
 
-#ifdef MAIL_USE_FLOCK
+      setuid (getuid ());
+
+#ifndef MAIL_USE_MMDF
+#ifdef MAIL_USE_SYSTEM_LOCK
       indesc = open (inname, O_RDWR);
-#else  /* if not MAIL_USE_FLOCK */
+#else  /* if not MAIL_USE_SYSTEM_LOCK */
       indesc = open (inname, O_RDONLY);
-#endif /* not MAIL_USE_FLOCK */
+#endif /* not MAIL_USE_SYSTEM_LOCK */
 #else  /* MAIL_USE_MMDF */
       indesc = lk_open (inname, O_RDONLY, 0, 0, 10);
 #endif /* MAIL_USE_MMDF */
@@ -242,23 +388,77 @@ main (argc, argv)
       if (indesc < 0)
        pfatal_with_name (inname);
 
-#if defined (BSD) || defined (XENIX)
+#if defined (BSD_SYSTEM) || defined (XENIX)
       /* 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);
-#endif /* BSD or Xenix */
+#endif /* BSD_SYSTEM || XENIX */
       outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
       if (outdesc < 0)
        pfatal_with_name (outname);
-#ifdef MAIL_USE_FLOCK
+
+      /* This label exists so we can retry locking
+        after a delay, if it got EAGAIN or EBUSY.  */
+    retry_lock:
+
+      /* Try to lock it.  */
+#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 */
+       {
+#ifdef MAIL_USE_SYSTEM_LOCK
+#ifdef MAIL_USE_LOCKF
+         status = lockf (indesc, F_LOCK, 0);
+#else /* not MAIL_USE_LOCKF */
 #ifdef XENIX
-      if (locking (indesc, LK_RLCK, 0L) < 0) pfatal_with_name (inname);
+         status = locking (indesc, LK_RLCK, 0L);
 #else
-      if (flock (indesc, LOCK_EX) < 0) pfatal_with_name (inname);
+#ifdef WINDOWSNT
+         status = locking (indesc, LK_RLCK, -1L);
+#else
+         status = flock (indesc, LOCK_EX);
+#endif
+#endif
+#endif /* not MAIL_USE_LOCKF */
+#endif /* MAIL_USE_SYSTEM_LOCK */
+       }
+
+      /* 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
-#endif /* MAIL_USE_FLOCK */
+           }
+
+         pfatal_with_name (inname);
+       }
 
       {
        char buf[1024];
@@ -266,6 +466,8 @@ main (argc, argv)
        while (1)
          {
            nread = read (indesc, buf, sizeof buf);
+           if (nread < 0)
+             pfatal_with_name (inname);
            if (nread != write (outdesc, buf, nread))
              {
                int saved_errno = errno;
@@ -275,10 +477,21 @@ main (argc, argv)
              }
            if (nread < sizeof buf)
              break;
+#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 */
          }
       }
 
-#ifdef BSD
+#ifdef BSD_SYSTEM
       if (fsync (outdesc) < 0)
        pfatal_and_delete (outname);
 #endif
@@ -287,14 +500,18 @@ main (argc, argv)
       if (close (outdesc) != 0)
        pfatal_and_delete (outname);
 
-#ifdef MAIL_USE_FLOCK
+#ifdef MAIL_USE_SYSTEM_LOCK
+      if (! preserve_mail)
+       {
 #if defined (STRIDE) || defined (XENIX)
-      /* Stride, xenix have file locking, but no ftruncate.  This mess will do. */
-      close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
+         /* Stride, xenix have file locking, but no ftruncate.
+            This mess will do. */
+         close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
 #else
-      ftruncate (indesc, 0L);
+         ftruncate (indesc, 0L);
 #endif /* STRIDE or XENIX */
-#endif /* MAIL_USE_FLOCK */
+       }
+#endif /* MAIL_USE_SYSTEM_LOCK */
 
 #ifdef MAIL_USE_MMDF
       lk_close (indesc, 0, 0, 0);
@@ -302,74 +519,131 @@ main (argc, argv)
       close (indesc);
 #endif
 
-#ifndef MAIL_USE_FLOCK
-      /* Delete the input file; if we can't, at least get rid of its
-        contents.  */
+#ifndef MAIL_USE_SYSTEM_LOCK
+      if (! preserve_mail)
+       {
+         /* Delete the input file; if we can't, at least get rid of its
+            contents.  */
 #ifdef MAIL_UNLINK_SPOOL
-      /* 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)
+         /* 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)
 #endif /* MAIL_UNLINK_SPOOL */
-       creat (inname, 0600);
-#endif /* not MAIL_USE_FLOCK */
+           creat (inname, 0600);
+       }
+#endif /* not MAIL_USE_SYSTEM_LOCK */
 
-      exit (0);
+#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
+      exit (EXIT_SUCCESS);
     }
 
   wait (&status);
   if (!WIFEXITED (status))
-    exit (1);
+    exit (EXIT_FAILURE);
   else if (WRETCODE (status) != 0)
     exit (WRETCODE (status));
 
-#if !defined (MAIL_USE_MMDF) && !defined (MAIL_USE_FLOCK)
-  unlink (lockname);
-#endif /* not MAIL_USE_MMDF and not MAIL_USE_FLOCK */
-  exit (0);
+#if !defined (MAIL_USE_MMDF) && !defined (MAIL_USE_SYSTEM_LOCK)
+#ifdef MAIL_USE_MAILLOCK
+  if (! spool_name)
+#endif /* MAIL_USE_MAILLOCK */
+    unlink (lockname);
+#endif /* not MAIL_USE_MMDF and not MAIL_USE_SYSTEM_LOCK */
+
+#endif /* ! DISABLE_DIRECT_ACCESS */
+
+  return EXIT_SUCCESS;
 }
+
+#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;
+
+  if (stat1.st_dev == stat2.st_dev
+      && stat1.st_ino == stat2.st_ino)
+    return fname;
+
+  return NULL;
+}
+#endif /* MAIL_USE_MAILLOCK */
 \f
 /* Print error message and exit.  */
 
-fatal (s1, s2)
-     char *s1, *s2;
+void
+fatal (s1, s2, s3)
+     char *s1, *s2, *s3;
 {
   if (delete_lockname)
     unlink (delete_lockname);
-  error (s1, s2);
-  exit (1);
+  error (s1, s2, s3);
+  exit (EXIT_FAILURE);
 }
 
-/* Print error message.  `s1' is printf control string, `s2' is arg for it. */
+/* Print error message.  `s1' is printf control string, `s2' and `s3'
+   are args for it or null. */
 
+void
 error (s1, s2, s3)
      char *s1, *s2, *s3;
 {
-  printf ("movemail: ");
-  printf (s1, s2, s3);
-  printf ("\n");
+  fprintf (stderr, "movemail: ");
+  if (s3)
+    fprintf (stderr, s1, s2, s3);
+  else if (s2)
+    fprintf (stderr, s1, s2);
+  else
+    fprintf (stderr, s1);
+  fprintf (stderr, "\n");
 }
 
+void
 pfatal_with_name (name)
      char *name;
 {
-  extern int errno;
-  extern char *strerror ();
-  char *s;
-
-  s = concat ("", strerror (errno), " for %s");
-  fatal (s, name);
+  fatal ("%s for %s", strerror (errno), name);
 }
 
+void
 pfatal_and_delete (name)
      char *name;
 {
-  extern int errno;
-  extern char *strerror ();
-  char *s;
-
-  s = concat ("", strerror (errno), " for %s");
+  char *s = strerror (errno);
   unlink (name);
-  fatal (s, name);
+  fatal ("%s for %s", s, name);
 }
 
 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3.  */
@@ -391,13 +665,13 @@ concat (s1, s2, s3)
 
 /* Like malloc but get fatal error if memory is exhausted.  */
 
-char *
+long *
 xmalloc (size)
      unsigned size;
 {
-  char *result = (char *) malloc (size);
+  long *result = (long *) malloc (size);
   if (!result)
-    fatal ("virtual memory exhausted", 0);
+    fatal ("virtual memory exhausted", 0, 0);
   return result;
 }
 \f
@@ -405,21 +679,16 @@ xmalloc (size)
 
 #ifdef MAIL_USE_POP
 
+#ifndef WINDOWSNT
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netdb.h>
-#include <stdio.h>
+#else
+#undef _WINSOCKAPI_
+#include <winsock.h>
+#endif
 #include <pwd.h>
 
-#ifdef USG
-#include <fcntl.h>
-/* Cancel substitutions made by config.h for Emacs.  */
-#undef open
-#undef read
-#undef write
-#undef close
-#endif /* USG */
-
 #define NOTOK (-1)
 #define OK 0
 #define DONE 1
@@ -427,346 +696,250 @@ xmalloc (size)
 char *progname;
 FILE *sfi;
 FILE *sfo;
-char Errmsg[80];
-
-static int debug = 0;
+char ibuffer[BUFSIZ];
+char obuffer[BUFSIZ];
+char Errmsg[200];              /* POP errors, at least, can exceed
+                                  the original length of 80.  */
 
-char *get_errmsg ();
-char *getenv ();
-int mbx_write ();
+/*
+ * 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.
+ *
+ * Return a value suitable for passing to `exit'.
+ */
 
-popmail (user, outfile)
-     char *user;
+int
+popmail (mailbox, outfile, preserve, password, reverse_order)
+     char *mailbox;
      char *outfile;
+     int preserve;
+     char *password;
+     int reverse_order;
 {
-  char *host;
   int nmsgs, nbytes;
-  char response[128];
   register int i;
   int mbfi;
   FILE *mbf;
-  struct passwd *pw = (struct passwd *) getpwuid (getuid ());
-  if (pw == NULL)
-    fatal ("cannot determine user name");
+  char *getenv ();
+  popserver server;
+  int start, end, increment;
+  char *user, *hostname;
 
-  host = getenv ("MAILHOST");
-  if (host == NULL)
-    {
-      fatal ("no MAILHOST defined");
-    }
+  user = mailbox;
+  if ((hostname = index(mailbox, ':')))
+    *hostname++ = '\0';
 
-  if (pop_init (host) == NOTOK)
+  server = pop_open (hostname, user, password, POP_NO_GETPASS);
+  if (! server)
     {
-      fatal (Errmsg);
+      error ("Error connecting to POP server: %s", pop_error, 0);
+      return EXIT_FAILURE;
     }
 
-  if (getline (response, sizeof response, sfi) != OK)
+  if (pop_stat (server, &nmsgs, &nbytes))
     {
-      fatal (response);
+      error ("Error getting message count from POP server: %s", pop_error, 0);
+      return EXIT_FAILURE;
     }
 
-  if (pop_command ("USER %s", user) == NOTOK
-      || pop_command ("RPOP %s", pw->pw_name) == NOTOK)
+  if (!nmsgs)
     {
-      pop_command ("QUIT");
-      fatal (Errmsg);
+      pop_close (server);
+      return EXIT_SUCCESS;
     }
 
-  if (pop_stat (&nmsgs, &nbytes) == NOTOK)
+  mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
+  if (mbfi < 0)
     {
-      pop_command ("QUIT");
-      fatal (Errmsg);
+      pop_close (server);
+      error ("Error in open: %s, %s", strerror (errno), outfile);
+      return EXIT_FAILURE;
     }
+  fchown (mbfi, getuid (), -1);
 
-  if (!nmsgs)
+  if ((mbf = fdopen (mbfi, "wb")) == NULL)
     {
-      pop_command ("QUIT");
-      return 0;
+      pop_close (server);
+      error ("Error in fdopen: %s", strerror (errno), 0);
+      close (mbfi);
+      unlink (outfile);
+      return EXIT_FAILURE;
     }
 
-  mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
-  if (mbfi < 0)
+  if (reverse_order)
     {
-      pop_command ("QUIT");
-      pfatal_and_delete (outfile);
+      start = nmsgs;
+      end = 1;
+      increment = -1;
     }
-  fchown (mbfi, getuid (), -1);
-
-  if ((mbf = fdopen (mbfi, "w")) == NULL)
+  else
     {
-      pop_command ("QUIT");
-      pfatal_and_delete (outfile);
+      start = 1;
+      end = nmsgs;
+      increment = 1;
     }
 
-  for (i = 1; i <= nmsgs; i++)
+  for (i = start; i * increment <= end * increment; i += increment)
     {
       mbx_delimit_begin (mbf);
-      if (pop_retr (i, mbx_write, mbf) != OK)
+      if (pop_retr (server, i, mbf) != OK)
        {
-         pop_command ("QUIT");
+         error ("%s", Errmsg, 0);
          close (mbfi);
-         unlink (outfile);
-         fatal (Errmsg);
+         return EXIT_FAILURE;
        }
       mbx_delimit_end (mbf);
       fflush (mbf);
-    }
-
-  if (fsync (mbfi) < 0)
-    {
-      pop_command ("QUIT");
-      pfatal_and_delete (outfile);
-    }
-
-  if (close (mbfi) == -1)
-    {
-      pop_command ("QUIT");
-      pfatal_and_delete (outfile);
-    }
-
-  for (i = 1; i <= nmsgs; i++)
-    {
-      if (pop_command ("DELE %d", i) == NOTOK)
+      if (ferror (mbf))
        {
-         /* Better to ignore this failure.  */
+         error ("Error in fflush: %s", strerror (errno), 0);
+         pop_close (server);
+         close (mbfi);
+         return EXIT_FAILURE;
        }
     }
 
-  pop_command ("QUIT");
-  return (0);
-}
+  /* 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. */
 
-pop_init (host)
-     char *host;
-{
-  register struct hostent *hp;
-  register struct servent *sp;
-  int lport = IPPORT_RESERVED - 1;
-  struct sockaddr_in sin;
-  register int s;
-
-  hp = gethostbyname (host);
-  if (hp == NULL)
-    {
-      sprintf (Errmsg, "MAILHOST unknown: %s", host);
-      return NOTOK;
-    }
-
-  sp = getservbyname ("pop", "tcp");
-  if (sp == 0)
-    {
-      strcpy (Errmsg, "tcp/pop: unknown service");
-      return NOTOK;
-    }
-
-  sin.sin_family = hp->h_addrtype;
-  bcopy (hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
-  sin.sin_port = sp->s_port;
-  s = rresvport (&lport);
-  if (s < 0)
-    {
-      sprintf (Errmsg, "error creating socket: %s", get_errmsg ());
-      return NOTOK;
-    }
-
-  if (connect (s, (char *)&sin, sizeof sin) < 0)
+#ifdef BSD_SYSTEM
+  if (fsync (mbfi) < 0)
     {
-      sprintf (Errmsg, "error during connect: %s", get_errmsg ());
-      close (s);
-      return NOTOK;
+      error ("Error in fsync: %s", strerror (errno), 0);
+      return EXIT_FAILURE;
     }
+#endif
 
-  sfi = fdopen (s, "r");
-  sfo = fdopen (s, "w");
-  if (sfi == NULL || sfo == NULL)
+  if (close (mbfi) == -1)
     {
-      sprintf (Errmsg, "error in fdopen: %s", get_errmsg ());
-      close (s);
-      return NOTOK;
+      error ("Error in close: %s", strerror (errno), 0);
+      return EXIT_FAILURE;
     }
 
-  return OK;
-}
-
-pop_command (fmt, a, b, c, d)
-     char *fmt;
-{
-  char buf[128];
-  char errmsg[64];
-
-  sprintf (buf, fmt, a, b, c, d);
-
-  if (debug) fprintf (stderr, "---> %s\n", buf);
-  if (putline (buf, Errmsg, sfo) == NOTOK) return NOTOK;
+  if (! preserve)
+    for (i = 1; i <= nmsgs; i++)
+      {
+       if (pop_delete (server, i))
+         {
+           error ("Error from POP server: %s", pop_error, 0);
+           pop_close (server);
+           return EXIT_FAILURE;
+         }
+      }
 
-  if (getline (buf, sizeof buf, sfi) != OK)
+  if (pop_quit (server))
     {
-      strcpy (Errmsg, buf);
-      return NOTOK;
+      error ("Error from POP server: %s", pop_error, 0);
+      return EXIT_FAILURE;
     }
 
-  if (debug)
-    fprintf (stderr, "<--- %s\n", buf);
-  if (*buf != '+')
-    {
-      strcpy (Errmsg, buf);
-      return NOTOK;
-    }
-  else
-    {
-      return OK;
-    }
+  return EXIT_SUCCESS;
 }
 
-    
-pop_stat (nmsgs, nbytes)
-     int *nmsgs, *nbytes;
+int
+pop_retr (server, msgno, arg)
+     popserver server;
+     int msgno;
+     FILE *arg;
 {
-  char buf[128];
-
-  if (debug)
-    fprintf (stderr, "---> STAT\n");
-  if (putline ("STAT", Errmsg, sfo) == NOTOK)
-    return NOTOK;
-
-  if (getline (buf, sizeof buf, sfi) != OK)
-    {
-      strcpy (Errmsg, buf);
-      return NOTOK;
-    }
+  extern char *strerror ();
+  char *line;
+  int ret;
 
-  if (debug) fprintf (stderr, "<--- %s\n", buf);
-  if (*buf != '+')
-    {
-      strcpy (Errmsg, buf);
-      return NOTOK;
-    }
-  else
+  if (pop_retrieve_first (server, msgno, &line))
     {
-      sscanf (buf, "+OK %d %d", nmsgs, nbytes);
-      return OK;
+      char *error = concat ("Error from POP server: ", pop_error, "");
+      strncpy (Errmsg, error, sizeof (Errmsg));
+      Errmsg[sizeof (Errmsg)-1] = '\0';
+      free(error);
+      return (NOTOK);
     }
-}
-
-pop_retr (msgno, action, arg)
-     int (*action)();
-{
-  char buf[128];
-
-  sprintf (buf, "RETR %d", msgno);
-  if (debug) fprintf (stderr, "%s\n", buf);
-  if (putline (buf, Errmsg, sfo) == NOTOK) return NOTOK;
 
-  if (getline (buf, sizeof buf, sfi) != OK)
+  while ((ret = pop_retrieve_next (server, &line)) >= 0)
     {
-      strcpy (Errmsg, buf);
-      return NOTOK;
-    }
+      if (! line)
+       break;
 
-  while (1)
-    {
-      switch (multiline (buf, sizeof buf, sfi))
+      if (mbx_write (line, ret, arg) != OK)
        {
-       case OK:
-         (*action)(buf, arg);
-         break;
-       case DONE:
-         return OK;
-       case NOTOK:
-         strcpy (Errmsg, buf);
-         return NOTOK;
+         strcpy (Errmsg, strerror (errno));
+         pop_close (server);
+         return (NOTOK);
        }
     }
-}
-
-getline (buf, n, f)
-     char *buf;
-     register int n;
-     FILE *f;
-{
-  register char *p;
-  int c;
-
-  p = buf;
-  while (--n > 0 && (c = fgetc (f)) != EOF)
-    if ((*p++ = c) == '\n') break;
-
-  if (ferror (f))
-    {
-      strcpy (buf, "error on connection");
-      return NOTOK;
-    }
 
-  if (c == EOF && p == buf)
+  if (ret)
     {
-      strcpy (buf, "connection closed by foreign host");
-      return DONE;
+      char *error = concat ("Error from POP server: ", pop_error, "");
+      strncpy (Errmsg, error, sizeof (Errmsg));
+      Errmsg[sizeof (Errmsg)-1] = '\0';
+      free(error);
+      return (NOTOK);
     }
 
-  *p = NULL;
-  if (*--p == '\n') *p = NULL;
-  if (*--p == '\r') *p = NULL;
-  return OK;
+  return (OK);
 }
 
-multiline (buf, n, f)
-     char *buf;
-     register int n;
-     FILE *f;
-{
-  if (getline (buf, n, f) != OK)
-    return NOTOK;
-  if (*buf == '.')
-    {
-      if (*(buf+1) == NULL)
-       return DONE;
-      else
-       strcpy (buf, buf+1);
-    }
-  return OK;
-}
+/* 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] == ' '))
 
-char *
-get_errmsg ()
-{
-  extern int errno;
-  extern char *strerror ();
-  return strerror (errno);
-}
-
-putline (buf, err, f)
-     char *buf;
-     char *err;
-     FILE *f;
-{
-  fprintf (f, "%s\r\n", buf);
-  fflush (f);
-  if (ferror (f))
-    {
-      strcpy (err, "lost connection");
-      return NOTOK;
-    }
-  return OK;
-}
-
-mbx_write (line, mbf)
+int
+mbx_write (line, len, mbf)
      char *line;
+     int len;
      FILE *mbf;
 {
-  fputs (line, mbf);
-  fputc (0x0a, mbf);
+#ifdef MOVEMAIL_QUOTE_POP_FROM_LINES
+  if (IS_FROM_LINE (line))
+    {
+      if (fputc ('>', mbf) == EOF)
+       return (NOTOK);
+    }
+#endif
+  if (line[0] == '\037')
+    {
+      if (fputs ("^_", mbf) == EOF)
+       return (NOTOK);
+      line++;
+      len--;
+    }
+  if (fwrite (line, 1, len, mbf) != len)
+    return (NOTOK);
+  if (fputc (0x0a, mbf) == EOF)
+    return (NOTOK);
+  return (OK);
 }
 
+int
 mbx_delimit_begin (mbf)
      FILE *mbf;
 {
-  fputs ("\f\n0, unseen,,\n", mbf);
+  if (fputs ("\f\n0, unseen,,\n", mbf) == EOF)
+    return (NOTOK);
+  return (OK);
 }
 
+int
 mbx_delimit_end (mbf)
      FILE *mbf;
 {
-  putc ('\037', mbf);
+  if (putc ('\037', mbf) == EOF)
+    return (NOTOK);
+  return (OK);
 }
 
 #endif /* MAIL_USE_POP */
@@ -785,3 +958,8 @@ strerror (errnum)
 }
 
 #endif /* ! HAVE_STRERROR */
+
+/* arch-tag: 1c323112-41fe-4fe5-8de9-494de631f73f
+   (do not change this comment) */
+
+/* movemail.c ends here */