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

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