root/lib-src/movemail.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. xmalloc
  2. main
  3. mail_spool_name
  4. fatal
  5. error
  6. pfatal_with_name
  7. pfatal_and_delete
  8. popmail
  9. pop_retr
  10. mbx_write
  11. movemail_strftime
  12. mbx_delimit_begin
  13. mbx_delimit_end

     1 /* movemail foo bar -- move file foo to file bar,
     2    locking file foo the way /bin/mail respects.
     3 
     4 Copyright (C) 1986, 1992-1994, 1996, 1999, 2001-2023 Free Software
     5 Foundation, Inc.
     6 
     7 This file is part of GNU Emacs.
     8 
     9 GNU Emacs is free software: you can redistribute it and/or modify
    10 it under the terms of the GNU General Public License as published by
    11 the Free Software Foundation, either version 3 of the License, or (at
    12 your option) any later version.
    13 
    14 GNU Emacs is distributed in the hope that it will be useful,
    15 but WITHOUT ANY WARRANTY; without even the implied warranty of
    16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    17 GNU General Public License for more details.
    18 
    19 You should have received a copy of the GNU General Public License
    20 along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
    21 
    22 
    23 /* Important notice: defining MAIL_USE_FLOCK or MAIL_USE_LOCKF *will
    24    cause loss of mail* if you do it on a system that does not normally
    25    use flock/lockf as its way of interlocking access to inbox files.  The
    26    setting of MAIL_USE_FLOCK and MAIL_USE_LOCKF *must agree* with the
    27    system's own conventions.  It is not a choice that is up to you.
    28 
    29    So, if your system uses lock files rather than flock, then the only way
    30    you can get proper operation is to enable movemail to write lockfiles there.
    31    This means you must either give that directory access modes
    32    that permit everyone to write lockfiles in it, or you must make movemail
    33    a setuid or setgid program.  */
    34 
    35 /*
    36  * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
    37  *
    38  * Added POP (Post Office Protocol) service.  When compiled -DMAIL_USE_POP
    39  * movemail will accept input filename arguments of the form
    40  * "po:username".  This will cause movemail to open a connection to
    41  * a pop server running on $MAILHOST (environment variable).  Movemail
    42  * must be setuid to root in order to work with POP.
    43  *
    44  * New module: popmail.c
    45  * Modified routines:
    46  *      main - added code within #ifdef MAIL_USE_POP; added setuid (getuid ())
    47  *              after POP code.
    48  * New routines in movemail.c:
    49  *      get_errmsg - return pointer to system error message
    50  *
    51  * Modified August, 1993 by Jonathan Kamens (OpenVision Technologies)
    52  *
    53  * Move all of the POP code into a separate file, "pop.c".
    54  * Use strerror instead of get_errmsg.
    55  *
    56  */
    57 
    58 #include <config.h>
    59 #include <sys/types.h>
    60 #include <sys/stat.h>
    61 #include <sys/file.h>
    62 #include <stdlib.h>
    63 #include <errno.h>
    64 #include <time.h>
    65 #include <timespec.h>
    66 
    67 #include <getopt.h>
    68 #include <unistd.h>
    69 #include <fcntl.h>
    70 #include <signal.h>
    71 #include <string.h>
    72 
    73 #include <attribute.h>
    74 #include <unlocked-io.h>
    75 
    76 #include "syswait.h"
    77 #ifdef MAIL_USE_POP
    78 #include "pop.h"
    79 #endif
    80 
    81 #ifdef MSDOS
    82 #undef access
    83 #endif /* MSDOS */
    84 
    85 #ifdef WINDOWSNT
    86 #include "ntlib.h"
    87 #undef access
    88 #undef unlink
    89 #define fork() 0
    90 #define waitpid(child, var, flags) (*(var) = 0)
    91 /* Unfortunately, Samba doesn't seem to properly lock Unix files even
    92    though the locking call succeeds (and indeed blocks local access from
    93    other NT programs).  If you have direct file access using an NFS
    94    client or something other than Samba, the locking call might work
    95    properly - make sure it does before you enable this!
    96 
    97    [18-Feb-97 andrewi] I now believe my comment above to be incorrect,
    98    since it was based on a misunderstanding of how locking calls are
    99    implemented and used on Unix.  */
   100 //#define DISABLE_DIRECT_ACCESS
   101 
   102 #include <fcntl.h>
   103 #endif /* WINDOWSNT */
   104 
   105 #ifdef WINDOWSNT
   106 #include <sys/locking.h>
   107 #endif
   108 
   109 /* If your system uses the `flock' or `lockf' system call for mail locking,
   110    define MAIL_USE_SYSTEM_LOCK.  If your system type should always define
   111    MAIL_USE_LOCKF or MAIL_USE_FLOCK but configure does not do this,
   112    please make a bug report.  */
   113 
   114 #ifdef MAIL_USE_LOCKF
   115 #define MAIL_USE_SYSTEM_LOCK
   116 #endif
   117 
   118 #ifdef MAIL_USE_FLOCK
   119 #define MAIL_USE_SYSTEM_LOCK
   120 #endif
   121 
   122 #if (!defined MAIL_USE_SYSTEM_LOCK                              \
   123      && (defined HAVE_LIBMAIL || defined HAVE_LIBLOCKFILE)      \
   124      && defined HAVE_MAILLOCK_H)
   125 #include <maillock.h>
   126 /* We can't use maillock unless we know what directory system mail
   127    files appear in. */
   128 #ifdef MAILDIR
   129 #define MAIL_USE_MAILLOCK
   130 static char *mail_spool_name (char *);
   131 #endif
   132 #endif
   133 
   134 static _Noreturn void fatal (const char *s1, const char *s2, const char *s3);
   135 static void error (const char *s1, const char *s2, const char *s3);
   136 static _Noreturn void pfatal_with_name (char *name);
   137 static _Noreturn void pfatal_and_delete (char *name);
   138 #ifdef MAIL_USE_POP
   139 static int popmail (char *, char *, bool, char *, bool);
   140 static bool pop_retr (popserver, int, FILE *);
   141 static bool mbx_write (char *, int, FILE *);
   142 static bool mbx_delimit_begin (FILE *);
   143 static bool mbx_delimit_end (FILE *);
   144 #endif
   145 
   146 #if (defined MAIL_USE_MAILLOCK                                          \
   147      || (!defined DISABLE_DIRECT_ACCESS && !defined MAIL_USE_SYSTEM_LOCK))
   148 /* Like malloc but get fatal error if memory is exhausted.  */
   149 
   150 static void * ATTRIBUTE_MALLOC
   151 xmalloc (size_t size)
   152 {
   153   void *result = malloc (size);
   154   if (!result)
   155     fatal ("virtual memory exhausted", 0, 0);
   156   return result;
   157 }
   158 #endif
   159 
   160 /* Nonzero means this is name of a lock file to delete on fatal error.  */
   161 static char *delete_lockname;
   162 
   163 int
   164 main (int argc, char **argv)
   165 {
   166   char *inname, *outname;
   167   int indesc, outdesc;
   168   ssize_t nread;
   169   int wait_status;
   170   int c;
   171   bool preserve_mail = false;
   172 
   173 #ifndef MAIL_USE_SYSTEM_LOCK
   174   struct stat st;
   175   int tem;
   176   char *tempname;
   177   size_t inname_len, inname_dirlen;
   178   int desc;
   179 #endif /* not MAIL_USE_SYSTEM_LOCK */
   180 
   181 #ifdef MAIL_USE_POP
   182   bool pop_reverse_order = false;
   183 # define ARGSTR "pr"
   184 #else /* ! MAIL_USE_POP */
   185 # define ARGSTR "p"
   186 #endif /* MAIL_USE_POP */
   187 
   188   uid_t real_gid = getgid ();
   189   uid_t priv_gid = getegid ();
   190 
   191   delete_lockname = 0;
   192 
   193   while (0 <= (c = getopt (argc, argv, ARGSTR)))
   194     {
   195       switch (c) {
   196 #ifdef MAIL_USE_POP
   197       case 'r':
   198         pop_reverse_order = true;
   199         break;
   200 #endif
   201       case 'p':
   202         preserve_mail = true;
   203         break;
   204       default:
   205         return EXIT_FAILURE;
   206       }
   207     }
   208 
   209   if (
   210 #ifdef MAIL_USE_POP
   211       (argc - optind < 2) || (argc - optind > 3)
   212 #else
   213       (argc - optind != 2)
   214 #endif
   215       )
   216     {
   217 #ifdef MAIL_USE_POP
   218       fprintf (stderr, "Usage: movemail [-p] [-r] inbox destfile%s\n",
   219                " [POP-password]");
   220 #else
   221       fprintf (stderr, "Usage: movemail [-p] inbox destfile%s\n", "");
   222 #endif
   223       return EXIT_FAILURE;
   224     }
   225 
   226   inname = argv[optind];
   227   outname = argv[optind+1];
   228 
   229   if (*outname == 0)
   230     fatal ("Destination file name is empty", 0, 0);
   231 
   232 #ifdef MAIL_USE_POP
   233   if (!strncmp (inname, "po:", 3))
   234     {
   235       int status;
   236 
   237       status = popmail (inname + 3, outname, preserve_mail,
   238                         (argc - optind == 3) ? argv[optind+2] : NULL,
   239                         pop_reverse_order);
   240       return status;
   241     }
   242 
   243   if (setuid (getuid ()) < 0)
   244     fatal ("Failed to drop privileges", 0, 0);
   245 
   246 #endif /* MAIL_USE_POP */
   247 
   248 #ifndef DISABLE_DIRECT_ACCESS
   249 
   250   char *lockname = 0;
   251   char *spool_name = 0;
   252 
   253 #ifdef MAIL_USE_MAILLOCK
   254   spool_name = mail_spool_name (inname);
   255 #endif
   256   if (! spool_name)
   257     {
   258 #ifndef MAIL_USE_SYSTEM_LOCK
   259       /* Use a lock file named after our first argument with .lock appended:
   260          If it exists, the mail file is locked.  */
   261       /* Note: this locking mechanism is *required* by the mailer
   262          (on systems which use it) to prevent loss of mail.
   263 
   264          On systems that use a lock file, extracting the mail without locking
   265          WILL occasionally cause loss of mail due to timing errors!
   266 
   267          So, if creation of the lock file fails due to access
   268          permission on the mail spool directory, you simply MUST
   269          change the permission and/or make movemail a setgid program
   270          so it can create lock files properly.
   271 
   272          You might also wish to verify that your system is one which
   273          uses lock files for this purpose.  Some systems use other methods.  */
   274 
   275       bool lockname_unlinked = false;
   276       inname_len = strlen (inname);
   277       lockname = xmalloc (inname_len + sizeof ".lock");
   278       strcpy (lockname, inname);
   279       strcpy (lockname + inname_len, ".lock");
   280       for (inname_dirlen = inname_len;
   281            inname_dirlen && !IS_DIRECTORY_SEP (inname[inname_dirlen - 1]);
   282            inname_dirlen--)
   283         continue;
   284       tempname = xmalloc (inname_dirlen + sizeof "EXXXXXX");
   285 
   286       while (true)
   287         {
   288           /* Create the lock file, but not under the lock file name.  */
   289           /* Give up if cannot do that.  */
   290 
   291           memcpy (tempname, inname, inname_dirlen);
   292           strcpy (tempname + inname_dirlen, "EXXXXXX");
   293           desc = mkostemp (tempname, O_BINARY);
   294           if (desc < 0)
   295             {
   296               int mkostemp_errno = errno;
   297               error ("error while creating what would become the lock file",
   298                      0, 0);
   299               errno = mkostemp_errno;
   300               pfatal_with_name (tempname);
   301             }
   302           close (desc);
   303 
   304           tem = link (tempname, lockname);
   305 
   306           if (tem < 0 && errno != EEXIST)
   307             pfatal_with_name (lockname);
   308 
   309           unlink (tempname);
   310           if (tem >= 0)
   311             break;
   312           sleep (1);
   313 
   314           /* If lock file is five minutes old, unlock it.
   315              Five minutes should be good enough to cope with crashes
   316              and wedgitude, and long enough to avoid being fooled
   317              by time differences between machines.  */
   318           if (!lockname_unlinked
   319               && stat (lockname, &st) == 0
   320               && st.st_ctime < time (0) - 300)
   321             lockname_unlinked = unlink (lockname) == 0 || errno == ENOENT;
   322         }
   323 
   324       delete_lockname = lockname;
   325 #endif /* not MAIL_USE_SYSTEM_LOCK */
   326     }
   327 
   328 #ifdef SIGCHLD
   329   signal (SIGCHLD, SIG_DFL);
   330 #endif
   331 
   332   pid_t child = fork ();
   333   if (child < 0)
   334     fatal ("Error in fork; %s", strerror (errno), 0);
   335 
   336   if (child == 0)
   337     {
   338       int lockcount = 0;
   339       int status = 0;
   340 #if defined (MAIL_USE_MAILLOCK) && defined (HAVE_TOUCHLOCK)
   341       time_t touched_lock;
   342 #endif
   343 
   344       if (setuid (getuid ()) < 0 || setregid (-1, real_gid) < 0)
   345         fatal ("Failed to drop privileges", 0, 0);
   346 
   347 #ifdef MAIL_USE_SYSTEM_LOCK
   348       indesc = open (inname, O_RDWR | O_BINARY);
   349 #else  /* if not MAIL_USE_SYSTEM_LOCK */
   350       indesc = open (inname, O_RDONLY | O_BINARY);
   351 #endif /* not MAIL_USE_SYSTEM_LOCK */
   352 
   353       if (indesc < 0)
   354         pfatal_with_name (inname);
   355 
   356       /* Make sure the user can read the output file.  */
   357       umask (umask (0) & 0377);
   358 
   359       outdesc = open (outname, O_WRONLY | O_BINARY | O_CREAT | O_EXCL, 0666);
   360       if (outdesc < 0)
   361         pfatal_with_name (outname);
   362 
   363       if (setregid (-1, priv_gid) < 0)
   364         fatal ("Failed to regain privileges", 0, 0);
   365 
   366       /* This label exists so we can retry locking
   367          after a delay, if it got EAGAIN or EBUSY.  */
   368     retry_lock:
   369 
   370       /* Try to lock it.  */
   371 #ifdef MAIL_USE_MAILLOCK
   372       if (spool_name)
   373         {
   374           /* The "-" is to make it a negative number if maillock returns
   375              non-zero. */
   376           status = - maillock (spool_name, 1);
   377 #ifdef HAVE_TOUCHLOCK
   378           touched_lock = time (0);
   379 #endif
   380           lockcount = 5;
   381         }
   382       else
   383 #endif /* MAIL_USE_MAILLOCK */
   384         {
   385 #ifdef MAIL_USE_SYSTEM_LOCK
   386 #ifdef MAIL_USE_LOCKF
   387           status = lockf (indesc, F_LOCK, 0);
   388 #else /* not MAIL_USE_LOCKF */
   389 #ifdef WINDOWSNT
   390           status = locking (indesc, LK_RLCK, -1L);
   391 #else
   392           status = flock (indesc, LOCK_EX);
   393 #endif
   394 #endif /* not MAIL_USE_LOCKF */
   395 #endif /* MAIL_USE_SYSTEM_LOCK */
   396         }
   397 
   398       /* If it fails, retry up to 5 times
   399          for certain failure codes.  */
   400       if (status < 0)
   401         {
   402           if (++lockcount <= 5 && (errno == EAGAIN || errno == EBUSY))
   403             {
   404               sleep (1);
   405               goto retry_lock;
   406             }
   407 
   408           pfatal_with_name (inname);
   409         }
   410 
   411       {
   412         char buf[1024];
   413 
   414         while (true)
   415           {
   416             nread = read (indesc, buf, sizeof buf);
   417             if (nread < 0)
   418               pfatal_with_name (inname);
   419             if (nread != write (outdesc, buf, nread))
   420               {
   421                 int saved_errno = errno;
   422                 unlink (outname);
   423                 errno = saved_errno;
   424                 pfatal_with_name (outname);
   425               }
   426             if (nread < sizeof buf)
   427               break;
   428 #if defined (MAIL_USE_MAILLOCK) && defined (HAVE_TOUCHLOCK)
   429             if (spool_name)
   430               {
   431                 time_t now = time (0);
   432                 if (now - touched_lock > 60)
   433                   {
   434                     touchlock ();
   435                     touched_lock = now;
   436                   }
   437               }
   438 #endif /* MAIL_USE_MAILLOCK */
   439           }
   440       }
   441 
   442       if (fsync (outdesc) != 0 && errno != EINVAL)
   443         pfatal_and_delete (outname);
   444 
   445       /* Prevent symlink attacks truncating other users' mailboxes */
   446       if (setregid (-1, real_gid) < 0)
   447         fatal ("Failed to drop privileges", 0, 0);
   448 
   449       /* Check to make sure no errors before we zap the inbox.  */
   450       if (close (outdesc) != 0)
   451         pfatal_and_delete (outname);
   452 
   453 #ifdef MAIL_USE_SYSTEM_LOCK
   454       if (! preserve_mail)
   455         {
   456           if (ftruncate (indesc, 0) != 0)
   457             pfatal_with_name (inname);
   458         }
   459 #endif /* MAIL_USE_SYSTEM_LOCK */
   460 
   461       close (indesc);
   462 
   463 #ifndef MAIL_USE_SYSTEM_LOCK
   464       if (! preserve_mail)
   465         {
   466           /* Delete the input file; if we can't, at least get rid of its
   467              contents.  */
   468 #ifdef MAIL_UNLINK_SPOOL
   469           /* This is generally bad to do, because it destroys the permissions
   470              that were set on the file.  Better to just empty the file.  */
   471           if (unlink (inname) < 0 && errno != ENOENT)
   472 #endif /* MAIL_UNLINK_SPOOL */
   473             close (creat (inname, 0600));
   474         }
   475 #endif /* not MAIL_USE_SYSTEM_LOCK */
   476 
   477       /* End of mailbox truncation */
   478       if (setregid (-1, priv_gid) < 0)
   479         fatal ("Failed to regain privileges", 0, 0);
   480 
   481 #ifdef MAIL_USE_MAILLOCK
   482       /* This has to occur in the child, i.e., in the process that
   483          acquired the lock! */
   484       if (spool_name)
   485         mailunlock ();
   486 #endif
   487       return EXIT_SUCCESS;
   488     }
   489 
   490   if (waitpid (child, &wait_status, 0) < 0)
   491     fatal ("Error in waitpid; %s", strerror (errno), 0);
   492   if (!WIFEXITED (wait_status))
   493     return EXIT_FAILURE;
   494   else if (WEXITSTATUS (wait_status) != 0)
   495     return WEXITSTATUS (wait_status);
   496 
   497   if (lockname)
   498     unlink (lockname);
   499 
   500 #endif /* ! DISABLE_DIRECT_ACCESS */
   501 
   502   return EXIT_SUCCESS;
   503 }
   504 
   505 #ifdef MAIL_USE_MAILLOCK
   506 /* This function uses stat to confirm that the mail directory is
   507    identical to the directory of the input file, rather than just
   508    string-comparing the two paths, because one or both of them might
   509    be symbolic links pointing to some other directory. */
   510 static char *
   511 mail_spool_name (char *inname)
   512 {
   513   struct stat stat1, stat2;
   514   char *indir, *fname;
   515   int status;
   516 
   517   if (! (fname = strrchr (inname, '/')))
   518     return NULL;
   519 
   520   fname++;
   521 
   522   if (stat (MAILDIR, &stat1) < 0)
   523     return NULL;
   524 
   525   indir = xmalloc (fname - inname + 1);
   526   memcpy (indir, inname, fname - inname);
   527   indir[fname-inname] = '\0';
   528 
   529 
   530   status = stat (indir, &stat2);
   531 
   532   free (indir);
   533 
   534   if (status < 0)
   535     return NULL;
   536 
   537   if (stat1.st_dev == stat2.st_dev
   538       && stat1.st_ino == stat2.st_ino)
   539     return fname;
   540 
   541   return NULL;
   542 }
   543 #endif /* MAIL_USE_MAILLOCK */
   544 
   545 /* Print error message and exit.  */
   546 
   547 static void
   548 fatal (const char *s1, const char *s2, const char *s3)
   549 {
   550   if (delete_lockname)
   551     unlink (delete_lockname);
   552   error (s1, s2, s3);
   553   exit (EXIT_FAILURE);
   554 }
   555 
   556 /* Print error message.  `s1' is printf control string, `s2' and `s3'
   557    are args for it or null. */
   558 
   559 static void
   560 error (const char *s1, const char *s2, const char *s3)
   561 {
   562   fprintf (stderr, "movemail: ");
   563   if (s3)
   564     fprintf (stderr, s1, s2, s3);
   565   else if (s2)
   566     fprintf (stderr, s1, s2);
   567   else
   568     fprintf (stderr, "%s", s1);
   569   fprintf (stderr, "\n");
   570 }
   571 
   572 static void
   573 pfatal_with_name (char *name)
   574 {
   575   fatal ("%s for %s", strerror (errno), name);
   576 }
   577 
   578 static void
   579 pfatal_and_delete (char *name)
   580 {
   581   const char *s = strerror (errno);
   582   unlink (name);
   583   fatal ("%s for %s", s, name);
   584 }
   585 
   586 /* This is the guts of the interface to the Post Office Protocol.  */
   587 
   588 #ifdef MAIL_USE_POP
   589 
   590 #ifndef WINDOWSNT
   591 #include <sys/socket.h>
   592 #include <netinet/in.h>
   593 #include <netdb.h>
   594 #else
   595 #undef _WINSOCKAPI_
   596 #include <winsock.h>
   597 #endif
   598 #include <pwd.h>
   599 #include <string.h>
   600 
   601 /*
   602  * The full valid syntax for a POP mailbox specification for movemail
   603  * is "po:username:hostname".  The ":hostname" is optional; if it is
   604  * omitted, the MAILHOST environment variable will be consulted.  Note
   605  * that by the time popmail() is called the "po:" has been stripped
   606  * off of the front of the mailbox name.
   607  *
   608  * If the mailbox is in the form "po:username:hostname", then it is
   609  * modified by this function -- the second colon is replaced by a
   610  * null.
   611  *
   612  * Return a value suitable for passing to `exit'.
   613  */
   614 
   615 static int
   616 popmail (char *mailbox, char *outfile, bool preserve, char *password,
   617          bool reverse_order)
   618 {
   619   int nmsgs, nbytes;
   620   int i;
   621   int mbfi;
   622   FILE *mbf;
   623   popserver server;
   624   int start, end, increment;
   625   char *user, *hostname;
   626 
   627   user = mailbox;
   628   if ((hostname = strchr (mailbox, ':')))
   629     *hostname++ = '\0';
   630 
   631   server = pop_open (hostname, user, password, POP_NO_GETPASS);
   632   if (! server)
   633     {
   634       error ("Error connecting to POP server: %s", pop_error, 0);
   635       return EXIT_FAILURE;
   636     }
   637 
   638   if (pop_stat (server, &nmsgs, &nbytes))
   639     {
   640       error ("Error getting message count from POP server: %s", pop_error, 0);
   641       return EXIT_FAILURE;
   642     }
   643 
   644   if (!nmsgs)
   645     {
   646       pop_close (server);
   647       return EXIT_SUCCESS;
   648     }
   649 
   650   mbfi = open (outfile, O_WRONLY | O_BINARY | O_CREAT | O_EXCL, 0666);
   651   if (mbfi < 0)
   652     {
   653       pop_close (server);
   654       error ("Error in open: %s, %s", strerror (errno), outfile);
   655       return EXIT_FAILURE;
   656     }
   657 
   658   if (fchown (mbfi, getuid (), -1) != 0)
   659     {
   660       int fchown_errno = errno;
   661       struct stat st;
   662       if (fstat (mbfi, &st) != 0 || st.st_uid != getuid ())
   663         {
   664           pop_close (server);
   665           error ("Error in fchown: %s, %s", strerror (fchown_errno), outfile);
   666           return EXIT_FAILURE;
   667         }
   668     }
   669 
   670   mbf = fdopen (mbfi, "wb");
   671   if (!mbf)
   672     {
   673       pop_close (server);
   674       error ("Error in fdopen: %s", strerror (errno), 0);
   675       close (mbfi);
   676       unlink (outfile);
   677       return EXIT_FAILURE;
   678     }
   679 
   680   if (reverse_order)
   681     {
   682       start = nmsgs;
   683       end = 1;
   684       increment = -1;
   685     }
   686   else
   687     {
   688       start = 1;
   689       end = nmsgs;
   690       increment = 1;
   691     }
   692 
   693   for (i = start; i * increment <= end * increment; i += increment)
   694     if (! (mbx_delimit_begin (mbf)
   695            && pop_retr (server, i, mbf)
   696            && mbx_delimit_end (mbf)
   697            && fflush (mbf) == 0))
   698       {
   699         if (errno)
   700           error ("Error in POP retrieving: %s", strerror (errno), 0);
   701         pop_close (server);
   702         fclose (mbf);
   703         return EXIT_FAILURE;
   704       }
   705 
   706   if (fsync (mbfi) != 0 && errno != EINVAL)
   707     {
   708       error ("Error in fsync: %s", strerror (errno), 0);
   709       fclose (mbf);
   710       return EXIT_FAILURE;
   711     }
   712 
   713   if (fclose (mbf) != 0)
   714     {
   715       error ("Error in fclose: %s", strerror (errno), 0);
   716       return EXIT_FAILURE;
   717     }
   718 
   719   if (! preserve)
   720     for (i = 1; i <= nmsgs; i++)
   721       {
   722         if (pop_delete (server, i))
   723           {
   724             error ("Error from POP server: %s", pop_error, 0);
   725             pop_close (server);
   726             return EXIT_FAILURE;
   727           }
   728       }
   729 
   730   if (pop_quit (server))
   731     {
   732       error ("Error from POP server: %s", pop_error, 0);
   733       return EXIT_FAILURE;
   734     }
   735 
   736   return EXIT_SUCCESS;
   737 }
   738 
   739 static bool
   740 pop_retr (popserver server, int msgno, FILE *arg)
   741 {
   742   char *line;
   743   int ret;
   744 
   745   if (pop_retrieve_first (server, msgno, &line))
   746     {
   747       error ("Error from POP server: %s", pop_error, 0);
   748       errno = 0;
   749       return false;
   750     }
   751 
   752   while ((ret = pop_retrieve_next (server, &line)) >= 0)
   753     {
   754       if (! line)
   755         break;
   756 
   757       if (! mbx_write (line, ret, arg))
   758         {
   759           int write_errno = errno;
   760           pop_close (server);
   761           errno = write_errno;
   762           return false;
   763         }
   764     }
   765 
   766   if (ret)
   767     {
   768       error ("Error from POP server: %s", pop_error, 0);
   769       errno = 0;
   770       return false;
   771     }
   772 
   773   return true;
   774 }
   775 
   776 static bool
   777 mbx_write (char *line, int len, FILE *mbf)
   778 {
   779 #ifdef MOVEMAIL_QUOTE_POP_FROM_LINES
   780   /* Do this as a macro instead of using strcmp to save on execution time. */
   781   # define IS_FROM_LINE(a) ((a[0] == 'F')       \
   782                             && (a[1] == 'r')    \
   783                             && (a[2] == 'o')    \
   784                             && (a[3] == 'm')    \
   785                             && (a[4] == ' '))
   786   if (IS_FROM_LINE (line))
   787     {
   788       if (fputc ('>', mbf) < 0)
   789         return false;
   790     }
   791 #endif
   792   if (line[0] == '\037')
   793     {
   794       if (fputs ("^_", mbf) < 0)
   795         return false;
   796       line++;
   797       len--;
   798     }
   799   return fwrite (line, 1, len, mbf) == len && 0 <= fputc ('\n', mbf);
   800 }
   801 
   802 #ifdef WINDOWSNT
   803 /* Work around MS-Windows lack of support for %e or %T with a
   804    special-purpose strftime that assumes the exact format that
   805    movemail uses.  */
   806 static size_t
   807 movemail_strftime (char *s, size_t size, char const *format,
   808                    struct tm const *tm)
   809 {
   810   char fmt[size + 6], *q;
   811   const char *p;
   812 
   813   for (p = format, q = &fmt[0]; *p; )
   814     {
   815       if (*p == '%' && p[1] == 'e')
   816         {
   817           memcpy (q, "%d", 2);
   818           q += 2;
   819           p += 2;
   820         }
   821       else if (*p == '%' && p[1] == 'T')
   822         {
   823           memcpy (q, "%H:%M:%S", 8);
   824           q += 8;
   825           p += 2;
   826         }
   827       else if (*p == '%' && p[1] == '%')
   828         {
   829           memcpy (q, p, 2);
   830           q += 2;
   831           p += 2;
   832         }
   833       else
   834         *q++ = *p++;
   835     }
   836 
   837   size_t n = strftime (s, size, fmt, tm);
   838   char *mday = s + sizeof "From movemail Sun Jan " - 1;
   839   if (*mday == '0')
   840     *mday = ' ';
   841   return n;
   842 }
   843 # undef strftime
   844 # define strftime movemail_strftime
   845 #endif
   846 
   847 static bool
   848 mbx_delimit_begin (FILE *mbf)
   849 {
   850   time_t now = current_timespec ().tv_sec;
   851   struct tm *ltime = localtime (&now);
   852   if (!ltime)
   853     return false;
   854 
   855   char fromline[100];
   856   if (! strftime (fromline, sizeof fromline,
   857                   "From movemail %a %b %e %T %Y\n", ltime))
   858     {
   859       errno = EOVERFLOW;
   860       return false;
   861     }
   862   return 0 <= fputs (fromline, mbf);
   863 }
   864 
   865 static bool
   866 mbx_delimit_end (FILE *mbf)
   867 {
   868   return 0 <= putc ('\n', mbf);
   869 }
   870 
   871 #endif /* MAIL_USE_POP */

/* [<][>][^][v][top][bottom][index][help] */