(socket_connection): Remove AI_ADDRCONFIG.
[bpt/emacs.git] / lib-src / movemail.c
index be18441..aaaf91b 100644 (file)
@@ -1,12 +1,13 @@
 /* movemail foo bar -- move file foo to file bar,
    locking file foo the way /bin/mail respects.
 /* 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, 2001, 2002, 2003, 2004,
+                 2005, 2006, 2007  Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
 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
 
 This file is part of GNU Emacs.
 
 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
-the Free Software Foundation; either version 2, or (at your option)
+the Free Software Foundation; either version 3, or (at your option)
 any later version.
 
 GNU Emacs is distributed in the hope that it will be useful,
 any later version.
 
 GNU Emacs is distributed in the hope that it will be useful,
@@ -16,7 +17,8 @@ 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
 
 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 or MAIL_USE_LOCKF *will
    cause loss of mail* if you do it on a system that does not normally
 
 /* 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
@@ -33,16 +35,16 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 /*
  * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
  *
 /*
  * 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.
  * 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 ())
  * 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
  *
  * New routines in movemail.c:
  *     get_errmsg - return pointer to system error message
  *
@@ -54,13 +56,21 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  */
 
 #define NO_SHORTNAMES   /* Tell config not to load remap.h */
  */
 
 #define NO_SHORTNAMES   /* Tell config not to load remap.h */
-#include <../src/config.h>
+#include <config.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/file.h>
 #include <stdio.h>
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/file.h>
 #include <stdio.h>
 #include <errno.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 MAIL_USE_POP
 #include "pop.h"
 #endif
@@ -69,22 +79,41 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #undef access
 #endif /* 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 <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
 #ifndef F_OK
 #define F_OK 0
 #define X_OK 1
 #define W_OK 2
 #define R_OK 4
 #endif
-#endif /* USG */
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
 
 
-#ifdef XENIX
+#if defined (XENIX) || defined (WINDOWSNT)
 #include <sys/locking.h>
 #endif
 
 #include <sys/locking.h>
 #endif
 
@@ -100,23 +129,35 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 extern int lk_open (), lk_close ();
 #endif
 
 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
 
 #ifndef errno
 extern int errno;
 #endif
 char *strerror ();
 
 #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 ();
 
 void fatal ();
 void error ();
 void pfatal_with_name ();
 void pfatal_and_delete ();
 char *concat ();
-char *xmalloc ();
+long *xmalloc ();
 int popmail ();
 int pop_retr ();
 int mbx_write ();
 int popmail ();
 int pop_retr ();
 int mbx_write ();
@@ -135,6 +176,7 @@ main (argc, argv)
   int indesc, outdesc;
   int nread;
   WAITTYPE status;
   int indesc, outdesc;
   int nread;
   WAITTYPE status;
+  int c, preserve_mail = 0;
 
 #ifndef MAIL_USE_SYSTEM_LOCK
   struct stat st;
 
 #ifndef MAIL_USE_SYSTEM_LOCK
   struct stat st;
@@ -145,32 +187,78 @@ main (argc, argv)
   int desc;
 #endif /* not MAIL_USE_SYSTEM_LOCK */
 
   int desc;
 #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;
 
   delete_lockname = 0;
 
-  if (argc < 3)
+  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
+      )
     {
     {
-      fprintf (stderr, "Usage: movemail inbox destfile");
-      exit(1);
+#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
 
 
 #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);
 
   /* 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;
     strcpy (buf, outname);
     p = buf + strlen (buf);
   {
     char *buf = (char *) xmalloc (strlen (outname) + 1);
     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++ = '.';
       *--p = 0;
     if (p == buf)
       *p++ = '.';
@@ -182,89 +270,109 @@ main (argc, argv)
 #ifdef MAIL_USE_POP
   if (!strncmp (inname, "po:", 3))
     {
 #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 */
 
       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_SYSTEM_LOCK
   /* Check access to input file.  */
   if (access (inname, R_OK | W_OK) != 0)
     pfatal_with_name (inname);
 
 #ifndef MAIL_USE_MMDF
 #ifndef MAIL_USE_SYSTEM_LOCK
-  /* 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_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 && p[-1] != '/')
-    p--;
-  *p = 0;
-  strcpy (p, "EXXXXXX");
-  mktemp (tempname);
-  unlink (tempname);
-
-  while (1)
+#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);
       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;
+      delete_lockname = lockname;
+    }
 #endif /* not MAIL_USE_SYSTEM_LOCK */
 #endif /* not MAIL_USE_MMDF */
 
   if (fork () == 0)
     {
 #endif /* not MAIL_USE_SYSTEM_LOCK */
 #endif /* not MAIL_USE_MMDF */
 
   if (fork () == 0)
     {
+      int lockcount = 0;
+      int status = 0;
+#if defined (MAIL_USE_MAILLOCK) && defined (HAVE_TOUCHLOCK)
+      long touched_lock, now;
+#endif
+
       setuid (getuid ());
 
 #ifndef MAIL_USE_MMDF
       setuid (getuid ());
 
 #ifndef MAIL_USE_MMDF
@@ -280,27 +388,77 @@ main (argc, argv)
       if (indesc < 0)
        pfatal_with_name (inname);
 
       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);
       /* 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);
       outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
       if (outdesc < 0)
        pfatal_with_name (outname);
+
+      /* 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
 #ifdef MAIL_USE_SYSTEM_LOCK
 #ifdef MAIL_USE_LOCKF
-      if (lockf (indesc, F_LOCK, 0) < 0) pfatal_with_name (inname);
+         status = lockf (indesc, F_LOCK, 0);
 #else /* not MAIL_USE_LOCKF */
 #ifdef XENIX
 #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
+#ifdef WINDOWSNT
+         status = locking (indesc, LK_RLCK, -1L);
 #else
 #else
-      if (flock (indesc, LOCK_EX) < 0) pfatal_with_name (inname);
+         status = flock (indesc, LOCK_EX);
+#endif
 #endif
 #endif /* not MAIL_USE_LOCKF */
 #endif /* MAIL_USE_SYSTEM_LOCK */
 #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
+           }
+
+         pfatal_with_name (inname);
+       }
 
       {
        char buf[1024];
 
       {
        char buf[1024];
@@ -308,6 +466,8 @@ main (argc, argv)
        while (1)
          {
            nread = read (indesc, buf, sizeof buf);
        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;
            if (nread != write (outdesc, buf, nread))
              {
                int saved_errno = errno;
@@ -317,10 +477,21 @@ main (argc, argv)
              }
            if (nread < sizeof buf)
              break;
              }
            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
       if (fsync (outdesc) < 0)
        pfatal_and_delete (outname);
 #endif
@@ -330,12 +501,16 @@ main (argc, argv)
        pfatal_and_delete (outname);
 
 #ifdef MAIL_USE_SYSTEM_LOCK
        pfatal_and_delete (outname);
 
 #ifdef MAIL_USE_SYSTEM_LOCK
+      if (! preserve_mail)
+       {
 #if defined (STRIDE) || defined (XENIX)
 #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
 #else
-      ftruncate (indesc, 0L);
+         ftruncate (indesc, 0L);
 #endif /* STRIDE or XENIX */
 #endif /* STRIDE or XENIX */
+       }
 #endif /* MAIL_USE_SYSTEM_LOCK */
 
 #ifdef MAIL_USE_MMDF
 #endif /* MAIL_USE_SYSTEM_LOCK */
 
 #ifdef MAIL_USE_MMDF
@@ -345,51 +520,113 @@ main (argc, argv)
 #endif
 
 #ifndef MAIL_USE_SYSTEM_LOCK
 #endif
 
 #ifndef MAIL_USE_SYSTEM_LOCK
-      /* Delete the input file; if we can't, at least get rid of its
-        contents.  */
+      if (! preserve_mail)
+       {
+         /* Delete the input file; if we can't, at least get rid of its
+            contents.  */
 #ifdef MAIL_UNLINK_SPOOL
 #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 */
 #endif /* MAIL_UNLINK_SPOOL */
-       creat (inname, 0600);
+           creat (inname, 0600);
+       }
 #endif /* not MAIL_USE_SYSTEM_LOCK */
 
 #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))
     }
 
   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_SYSTEM_LOCK)
   else if (WRETCODE (status) != 0)
     exit (WRETCODE (status));
 
 #if !defined (MAIL_USE_MMDF) && !defined (MAIL_USE_SYSTEM_LOCK)
-  unlink (lockname);
+#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 /* not MAIL_USE_MMDF and not MAIL_USE_SYSTEM_LOCK */
-  return 0;
+
+#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.  */
 
 void
 \f
 /* Print error message and exit.  */
 
 void
-fatal (s1, s2)
-     char *s1, *s2;
+fatal (s1, s2, s3)
+     char *s1, *s2, *s3;
 {
   if (delete_lockname)
     unlink (delete_lockname);
 {
   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;
 {
   fprintf (stderr, "movemail: ");
 
 void
 error (s1, s2, s3)
      char *s1, *s2, *s3;
 {
   fprintf (stderr, "movemail: ");
-  fprintf (stderr, s1, s2, s3);
+  if (s3)
+    fprintf (stderr, s1, s2, s3);
+  else if (s2)
+    fprintf (stderr, s1, s2);
+  else
+    fprintf (stderr, s1);
   fprintf (stderr, "\n");
 }
 
   fprintf (stderr, "\n");
 }
 
@@ -397,17 +634,16 @@ void
 pfatal_with_name (name)
      char *name;
 {
 pfatal_with_name (name)
      char *name;
 {
-  char *s = concat ("", strerror (errno), " for %s");
-  fatal (s, name);
+  fatal ("%s for %s", strerror (errno), name);
 }
 
 void
 pfatal_and_delete (name)
      char *name;
 {
 }
 
 void
 pfatal_and_delete (name)
      char *name;
 {
-  char *s = concat ("", strerror (errno), " for %s");
+  char *s = strerror (errno);
   unlink (name);
   unlink (name);
-  fatal (s, name);
+  fatal ("%s for %s", s, name);
 }
 
 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3.  */
 }
 
 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3.  */
@@ -429,13 +665,13 @@ concat (s1, s2, s3)
 
 /* Like malloc but get fatal error if memory is exhausted.  */
 
 
 /* Like malloc but get fatal error if memory is exhausted.  */
 
-char *
+long *
 xmalloc (size)
      unsigned size;
 {
 xmalloc (size)
      unsigned size;
 {
-  char *result = (char *) malloc (size);
+  long *result = (long *) malloc (size);
   if (!result)
   if (!result)
-    fatal ("virtual memory exhausted", 0);
+    fatal ("virtual memory exhausted", 0, 0);
   return result;
 }
 \f
   return result;
 }
 \f
@@ -443,21 +679,16 @@ xmalloc (size)
 
 #ifdef MAIL_USE_POP
 
 
 #ifdef MAIL_USE_POP
 
+#ifndef WINDOWSNT
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netdb.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netdb.h>
-#include <stdio.h>
+#else
+#undef _WINSOCKAPI_
+#include <winsock.h>
+#endif
 #include <pwd.h>
 
 #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
 #define NOTOK (-1)
 #define OK 0
 #define DONE 1
@@ -467,38 +698,61 @@ FILE *sfi;
 FILE *sfo;
 char ibuffer[BUFSIZ];
 char obuffer[BUFSIZ];
 FILE *sfo;
 char ibuffer[BUFSIZ];
 char obuffer[BUFSIZ];
-char Errmsg[80];
+char Errmsg[200];              /* POP errors, at least, can exceed
+                                  the original length of 80.  */
+
+/*
+ * 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;
      char *outfile;
+     int preserve;
+     char *password;
+     int reverse_order;
 {
   int nmsgs, nbytes;
   register int i;
   int mbfi;
   FILE *mbf;
   char *getenv ();
 {
   int nmsgs, nbytes;
   register int i;
   int mbfi;
   FILE *mbf;
   char *getenv ();
-  int mbx_write ();
-  PopServer server;
-  extern char *strerror ();
+  popserver server;
+  int start, end, increment;
+  char *user, *hostname;
 
 
-  server = pop_open (0, user, 0, POP_NO_GETPASS);
+  user = mailbox;
+  if ((hostname = index(mailbox, ':')))
+    *hostname++ = '\0';
+
+  server = pop_open (hostname, user, password, POP_NO_GETPASS);
   if (! server)
     {
   if (! server)
     {
-      error (pop_error);
-      return (1);
+      error ("Error connecting to POP server: %s", pop_error, 0);
+      return EXIT_FAILURE;
     }
 
   if (pop_stat (server, &nmsgs, &nbytes))
     {
     }
 
   if (pop_stat (server, &nmsgs, &nbytes))
     {
-      error (pop_error);
-      return (1);
+      error ("Error getting message count from POP server: %s", pop_error, 0);
+      return EXIT_FAILURE;
     }
 
   if (!nmsgs)
     {
       pop_close (server);
     }
 
   if (!nmsgs)
     {
       pop_close (server);
-      return (0);
+      return EXIT_SUCCESS;
     }
 
   mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
     }
 
   mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
@@ -506,36 +760,49 @@ popmail (user, outfile)
     {
       pop_close (server);
       error ("Error in open: %s, %s", strerror (errno), outfile);
     {
       pop_close (server);
       error ("Error in open: %s, %s", strerror (errno), outfile);
-      return (1);
+      return EXIT_FAILURE;
     }
   fchown (mbfi, getuid (), -1);
 
     }
   fchown (mbfi, getuid (), -1);
 
-  if ((mbf = fdopen (mbfi, "w")) == NULL)
+  if ((mbf = fdopen (mbfi, "wb")) == NULL)
     {
       pop_close (server);
     {
       pop_close (server);
-      error ("Error in fdopen: %s", strerror (errno));
+      error ("Error in fdopen: %s", strerror (errno), 0);
       close (mbfi);
       unlink (outfile);
       close (mbfi);
       unlink (outfile);
-      return (1);
+      return EXIT_FAILURE;
+    }
+
+  if (reverse_order)
+    {
+      start = nmsgs;
+      end = 1;
+      increment = -1;
+    }
+  else
+    {
+      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);
     {
       mbx_delimit_begin (mbf);
-      if (pop_retr (server, i, mbx_write, mbf) != OK)
+      if (pop_retr (server, i, mbf) != OK)
        {
        {
-         error (Errmsg);
+         error ("%s", Errmsg, 0);
          close (mbfi);
          close (mbfi);
-         return (1);
+         return EXIT_FAILURE;
        }
       mbx_delimit_end (mbf);
       fflush (mbf);
       if (ferror (mbf))
        {
        }
       mbx_delimit_end (mbf);
       fflush (mbf);
       if (ferror (mbf))
        {
-         error ("Error in fflush: %s", strerror (errno));
+         error ("Error in fflush: %s", strerror (errno), 0);
          pop_close (server);
          close (mbfi);
          pop_close (server);
          close (mbfi);
-         return (1);
+         return EXIT_FAILURE;
        }
     }
 
        }
     }
 
@@ -545,42 +812,45 @@ popmail (user, outfile)
    *      directories have lost mail when over quota because these checks were
    *      not made in previous versions of movemail. */
 
    *      directories have lost mail when over quota because these checks were
    *      not made in previous versions of movemail. */
 
-#ifdef BSD
+#ifdef BSD_SYSTEM
   if (fsync (mbfi) < 0)
     {
   if (fsync (mbfi) < 0)
     {
-      error ("Error in fsync: %s", strerror (errno));
-      return (1);
+      error ("Error in fsync: %s", strerror (errno), 0);
+      return EXIT_FAILURE;
     }
 #endif
 
   if (close (mbfi) == -1)
     {
     }
 #endif
 
   if (close (mbfi) == -1)
     {
-      error ("Error in close: %s", strerror (errno));
-      return (1);
+      error ("Error in close: %s", strerror (errno), 0);
+      return EXIT_FAILURE;
     }
 
     }
 
-  for (i = 1; i <= nmsgs; i++)
-    {
-      if (pop_delete (server, i))
-       {
-         error (pop_error);
-         pop_close (server);
-         return (1);
-       }
-    }
+  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 (pop_quit (server))
     {
 
   if (pop_quit (server))
     {
-      error (pop_error);
-      return (1);
+      error ("Error from POP server: %s", pop_error, 0);
+      return EXIT_FAILURE;
     }
     }
-    
-  return (0);
+
+  return EXIT_SUCCESS;
 }
 
 }
 
-pop_retr (server, msgno, action, arg)
-     PopServer server;
-     int (*action)();
+int
+pop_retr (server, msgno, arg)
+     popserver server;
+     int msgno;
+     FILE *arg;
 {
   extern char *strerror ();
   char *line;
 {
   extern char *strerror ();
   char *line;
@@ -588,17 +858,19 @@ pop_retr (server, msgno, action, arg)
 
   if (pop_retrieve_first (server, msgno, &line))
     {
 
   if (pop_retrieve_first (server, msgno, &line))
     {
-      strncpy (Errmsg, pop_error, sizeof (Errmsg));
+      char *error = concat ("Error from POP server: ", pop_error, "");
+      strncpy (Errmsg, error, sizeof (Errmsg));
       Errmsg[sizeof (Errmsg)-1] = '\0';
       Errmsg[sizeof (Errmsg)-1] = '\0';
+      free(error);
       return (NOTOK);
     }
 
       return (NOTOK);
     }
 
-  while (! (ret = pop_retrieve_next (server, &line)))
+  while ((ret = pop_retrieve_next (server, &line)) >= 0)
     {
       if (! line)
        break;
 
     {
       if (! line)
        break;
 
-      if ((*action)(line, arg) != OK)
+      if (mbx_write (line, ret, arg) != OK)
        {
          strcpy (Errmsg, strerror (errno));
          pop_close (server);
        {
          strcpy (Errmsg, strerror (errno));
          pop_close (server);
@@ -608,8 +880,10 @@ pop_retr (server, msgno, action, arg)
 
   if (ret)
     {
 
   if (ret)
     {
-      strncpy (Errmsg, pop_error, sizeof (Errmsg));
+      char *error = concat ("Error from POP server: ", pop_error, "");
+      strncpy (Errmsg, error, sizeof (Errmsg));
       Errmsg[sizeof (Errmsg)-1] = '\0';
       Errmsg[sizeof (Errmsg)-1] = '\0';
+      free(error);
       return (NOTOK);
     }
 
       return (NOTOK);
     }
 
@@ -624,16 +898,26 @@ pop_retr (server, msgno, action, arg)
                         && (a[4] == ' '))
 
 int
                         && (a[4] == ' '))
 
 int
-mbx_write (line, mbf)
+mbx_write (line, len, mbf)
      char *line;
      char *line;
+     int len;
      FILE *mbf;
 {
      FILE *mbf;
 {
+#ifdef MOVEMAIL_QUOTE_POP_FROM_LINES
   if (IS_FROM_LINE (line))
     {
       if (fputc ('>', mbf) == EOF)
        return (NOTOK);
     }
   if (IS_FROM_LINE (line))
     {
       if (fputc ('>', mbf) == EOF)
        return (NOTOK);
     }
-  if (fputs (line, mbf) == EOF) 
+#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 (NOTOK);
   if (fputc (0x0a, mbf) == EOF)
     return (NOTOK);
@@ -649,6 +933,7 @@ mbx_delimit_begin (mbf)
   return (OK);
 }
 
   return (OK);
 }
 
+int
 mbx_delimit_end (mbf)
      FILE *mbf;
 {
 mbx_delimit_end (mbf)
      FILE *mbf;
 {
@@ -673,3 +958,8 @@ strerror (errnum)
 }
 
 #endif /* ! HAVE_STRERROR */
 }
 
 #endif /* ! HAVE_STRERROR */
+
+/* arch-tag: 1c323112-41fe-4fe5-8de9-494de631f73f
+   (do not change this comment) */
+
+/* movemail.c ends here */