1 /* movemail foo bar -- move file foo to file bar,
2 locking file foo the way /bin/mail respects.
4 Copyright (C) 1986, 1992-1994, 1996, 1999, 2001-2015 Free Software
7 This file is part of GNU Emacs.
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
12 (at your option) any later version.
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.
19 You should have received a copy of the GNU General Public License
20 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
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.
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. */
36 * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
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.
44 * New module: popmail.c
46 * main - added code within #ifdef MAIL_USE_POP; added setuid (getuid ())
48 * New routines in movemail.c:
49 * get_errmsg - return pointer to system error message
51 * Modified August, 1993 by Jonathan Kamens (OpenVision Technologies)
53 * Move all of the POP code into a separate file, "pop.c".
54 * Use strerror instead of get_errmsg.
59 #include <sys/types.h>
86 #define waitpid(child, var, flags) (*(var) = 0)
87 /* Unfortunately, Samba doesn't seem to properly lock Unix files even
88 though the locking call succeeds (and indeed blocks local access from
89 other NT programs). If you have direct file access using an NFS
90 client or something other than Samba, the locking call might work
91 properly - make sure it does before you enable this!
93 [18-Feb-97 andrewi] I now believe my comment above to be incorrect,
94 since it was based on a misunderstanding of how locking calls are
95 implemented and used on Unix. */
96 //#define DISABLE_DIRECT_ACCESS
99 #endif /* WINDOWSNT */
102 #include <sys/locking.h>
105 /* If your system uses the `flock' or `lockf' system call for mail locking,
106 define MAIL_USE_SYSTEM_LOCK. If your system type should always define
107 MAIL_USE_LOCKF or MAIL_USE_FLOCK but configure does not do this,
108 please make a bug report. */
110 #ifdef MAIL_USE_LOCKF
111 #define MAIL_USE_SYSTEM_LOCK
114 #ifdef MAIL_USE_FLOCK
115 #define MAIL_USE_SYSTEM_LOCK
119 extern int lk_open (), lk_close ();
122 #if !defined (MAIL_USE_SYSTEM_LOCK) && !defined (MAIL_USE_MMDF) && \
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
129 #define MAIL_USE_MAILLOCK
130 static char *mail_spool_name (char *);
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
);
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 *);
146 #if (defined MAIL_USE_MAILLOCK \
147 || (!defined DISABLE_DIRECT_ACCESS && !defined MAIL_USE_MMDF \
148 && !defined MAIL_USE_SYSTEM_LOCK))
149 /* Like malloc but get fatal error if memory is exhausted. */
152 xmalloc (size_t size
)
154 void *result
= malloc (size
);
156 fatal ("virtual memory exhausted", 0, 0);
161 /* Nonzero means this is name of a lock file to delete on fatal error. */
162 static char *delete_lockname
;
165 main (int argc
, char **argv
)
167 char *inname
, *outname
;
172 bool preserve_mail
= false;
174 #ifndef MAIL_USE_SYSTEM_LOCK
178 size_t inname_len
, inname_dirlen
;
180 #endif /* not MAIL_USE_SYSTEM_LOCK */
182 char *spool_name
= 0;
185 bool pop_reverse_order
= false;
187 #else /* ! MAIL_USE_POP */
189 #endif /* MAIL_USE_POP */
191 uid_t real_gid
= getgid ();
192 uid_t priv_gid
= getegid ();
196 while (0 <= (c
= getopt (argc
, argv
, ARGSTR
)))
201 pop_reverse_order
= true;
205 preserve_mail
= true;
214 (argc
- optind
< 2) || (argc
- optind
> 3)
221 fprintf (stderr
, "Usage: movemail [-p] [-r] inbox destfile%s\n",
224 fprintf (stderr
, "Usage: movemail [-p] inbox destfile%s\n", "");
229 inname
= argv
[optind
];
230 outname
= argv
[optind
+1];
237 fatal ("Destination file name is empty", 0, 0);
240 if (!strncmp (inname
, "po:", 3))
244 status
= popmail (inname
+ 3, outname
, preserve_mail
,
245 (argc
- optind
== 3) ? argv
[optind
+2] : NULL
,
250 if (setuid (getuid ()) < 0)
251 fatal ("Failed to drop privileges", 0, 0);
253 #endif /* MAIL_USE_POP */
255 #ifndef DISABLE_DIRECT_ACCESS
259 #ifndef MAIL_USE_MMDF
260 #ifndef MAIL_USE_SYSTEM_LOCK
261 #ifdef MAIL_USE_MAILLOCK
262 spool_name
= mail_spool_name (inname
);
266 /* Use a lock file named after our first argument with .lock appended:
267 If it exists, the mail file is locked. */
268 /* Note: this locking mechanism is *required* by the mailer
269 (on systems which use it) to prevent loss of mail.
271 On systems that use a lock file, extracting the mail without locking
272 WILL occasionally cause loss of mail due to timing errors!
274 So, if creation of the lock file fails due to access
275 permission on the mail spool directory, you simply MUST
276 change the permission and/or make movemail a setgid program
277 so it can create lock files properly.
279 You might also wish to verify that your system is one which
280 uses lock files for this purpose. Some systems use other methods. */
282 inname_len
= strlen (inname
);
283 lockname
= xmalloc (inname_len
+ sizeof ".lock");
284 strcpy (lockname
, inname
);
285 strcpy (lockname
+ inname_len
, ".lock");
286 for (inname_dirlen
= inname_len
;
287 inname_dirlen
&& !IS_DIRECTORY_SEP (inname
[inname_dirlen
- 1]);
290 tempname
= xmalloc (inname_dirlen
+ sizeof "EXXXXXX");
294 /* Create the lock file, but not under the lock file name. */
295 /* Give up if cannot do that. */
297 memcpy (tempname
, inname
, inname_dirlen
);
298 strcpy (tempname
+ inname_dirlen
, "EXXXXXX");
299 desc
= mkostemp (tempname
, O_BINARY
);
302 int mkostemp_errno
= errno
;
303 error ("error while creating what would become the lock file",
305 errno
= mkostemp_errno
;
306 pfatal_with_name (tempname
);
310 tem
= link (tempname
, lockname
);
312 if (tem
< 0 && errno
!= EEXIST
)
313 pfatal_with_name (lockname
);
320 /* If lock file is five minutes old, unlock it.
321 Five minutes should be good enough to cope with crashes
322 and wedgitude, and long enough to avoid being fooled
323 by time differences between machines. */
324 if (stat (lockname
, &st
) >= 0)
326 time_t now
= time (0);
327 if (st
.st_ctime
< now
- 300)
335 delete_lockname
= lockname
;
337 #endif /* not MAIL_USE_SYSTEM_LOCK */
338 #endif /* not MAIL_USE_MMDF */
341 signal (SIGCHLD
, SIG_DFL
);
344 pid_t child
= fork ();
346 fatal ("Error in fork; %s", strerror (errno
), 0);
352 #if defined (MAIL_USE_MAILLOCK) && defined (HAVE_TOUCHLOCK)
353 time_t touched_lock
IF_LINT (= 0);
356 if (setuid (getuid ()) < 0 || setregid (-1, real_gid
) < 0)
357 fatal ("Failed to drop privileges", 0, 0);
359 #ifndef MAIL_USE_MMDF
360 #ifdef MAIL_USE_SYSTEM_LOCK
361 indesc
= open (inname
, O_RDWR
| O_BINARY
);
362 #else /* if not MAIL_USE_SYSTEM_LOCK */
363 indesc
= open (inname
, O_RDONLY
| O_BINARY
);
364 #endif /* not MAIL_USE_SYSTEM_LOCK */
365 #else /* MAIL_USE_MMDF */
366 indesc
= lk_open (inname
, O_RDONLY
| O_BINARY
, 0, 0, 10);
367 #endif /* MAIL_USE_MMDF */
370 pfatal_with_name (inname
);
372 /* Make sure the user can read the output file. */
373 umask (umask (0) & 0377);
375 outdesc
= open (outname
, O_WRONLY
| O_BINARY
| O_CREAT
| O_EXCL
, 0666);
377 pfatal_with_name (outname
);
379 if (setregid (-1, priv_gid
) < 0)
380 fatal ("Failed to regain privileges", 0, 0);
382 /* This label exists so we can retry locking
383 after a delay, if it got EAGAIN or EBUSY. */
386 /* Try to lock it. */
387 #ifdef MAIL_USE_MAILLOCK
390 /* The "-" is to make it a negative number if maillock returns
392 status
= - maillock (spool_name
, 1);
393 #ifdef HAVE_TOUCHLOCK
394 touched_lock
= time (0);
399 #endif /* MAIL_USE_MAILLOCK */
401 #ifdef MAIL_USE_SYSTEM_LOCK
402 #ifdef MAIL_USE_LOCKF
403 status
= lockf (indesc
, F_LOCK
, 0);
404 #else /* not MAIL_USE_LOCKF */
406 status
= locking (indesc
, LK_RLCK
, -1L);
408 status
= flock (indesc
, LOCK_EX
);
410 #endif /* not MAIL_USE_LOCKF */
411 #endif /* MAIL_USE_SYSTEM_LOCK */
414 /* If it fails, retry up to 5 times
415 for certain failure codes. */
418 if (++lockcount
<= 5 && (errno
== EAGAIN
|| errno
== EBUSY
))
424 pfatal_with_name (inname
);
432 nread
= read (indesc
, buf
, sizeof buf
);
434 pfatal_with_name (inname
);
435 if (nread
!= write (outdesc
, buf
, nread
))
437 int saved_errno
= errno
;
440 pfatal_with_name (outname
);
442 if (nread
< sizeof buf
)
444 #if defined (MAIL_USE_MAILLOCK) && defined (HAVE_TOUCHLOCK)
447 time_t now
= time (0);
448 if (now
- touched_lock
> 60)
454 #endif /* MAIL_USE_MAILLOCK */
458 if (fsync (outdesc
) != 0 && errno
!= EINVAL
)
459 pfatal_and_delete (outname
);
461 /* Prevent symlink attacks truncating other users' mailboxes */
462 if (setregid (-1, real_gid
) < 0)
463 fatal ("Failed to drop privileges", 0, 0);
465 /* Check to make sure no errors before we zap the inbox. */
466 if (close (outdesc
) != 0)
467 pfatal_and_delete (outname
);
469 #ifdef MAIL_USE_SYSTEM_LOCK
472 if (ftruncate (indesc
, 0) != 0)
473 pfatal_with_name (inname
);
475 #endif /* MAIL_USE_SYSTEM_LOCK */
478 lk_close (indesc
, 0, 0, 0);
483 #ifndef MAIL_USE_SYSTEM_LOCK
486 /* Delete the input file; if we can't, at least get rid of its
488 #ifdef MAIL_UNLINK_SPOOL
489 /* This is generally bad to do, because it destroys the permissions
490 that were set on the file. Better to just empty the file. */
491 if (unlink (inname
) < 0 && errno
!= ENOENT
)
492 #endif /* MAIL_UNLINK_SPOOL */
493 creat (inname
, 0600);
495 #endif /* not MAIL_USE_SYSTEM_LOCK */
497 /* End of mailbox truncation */
498 if (setregid (-1, priv_gid
) < 0)
499 fatal ("Failed to regain privileges", 0, 0);
501 #ifdef MAIL_USE_MAILLOCK
502 /* This has to occur in the child, i.e., in the process that
503 acquired the lock! */
510 if (waitpid (child
, &wait_status
, 0) < 0)
511 fatal ("Error in waitpid; %s", strerror (errno
), 0);
512 if (!WIFEXITED (wait_status
))
514 else if (WEXITSTATUS (wait_status
) != 0)
515 return WEXITSTATUS (wait_status
);
520 #endif /* ! DISABLE_DIRECT_ACCESS */
525 #ifdef MAIL_USE_MAILLOCK
526 /* This function uses stat to confirm that the mail directory is
527 identical to the directory of the input file, rather than just
528 string-comparing the two paths, because one or both of them might
529 be symbolic links pointing to some other directory. */
531 mail_spool_name (char *inname
)
533 struct stat stat1
, stat2
;
537 if (! (fname
= strrchr (inname
, '/')))
542 if (stat (MAILDIR
, &stat1
) < 0)
545 indir
= xmalloc (fname
- inname
+ 1);
546 memcpy (indir
, inname
, fname
- inname
);
547 indir
[fname
-inname
] = '\0';
550 status
= stat (indir
, &stat2
);
557 if (stat1
.st_dev
== stat2
.st_dev
558 && stat1
.st_ino
== stat2
.st_ino
)
563 #endif /* MAIL_USE_MAILLOCK */
565 /* Print error message and exit. */
568 fatal (const char *s1
, const char *s2
, const char *s3
)
571 unlink (delete_lockname
);
576 /* Print error message. `s1' is printf control string, `s2' and `s3'
577 are args for it or null. */
580 error (const char *s1
, const char *s2
, const char *s3
)
582 fprintf (stderr
, "movemail: ");
584 fprintf (stderr
, s1
, s2
, s3
);
586 fprintf (stderr
, s1
, s2
);
588 fprintf (stderr
, "%s", s1
);
589 fprintf (stderr
, "\n");
593 pfatal_with_name (char *name
)
595 fatal ("%s for %s", strerror (errno
), name
);
599 pfatal_and_delete (char *name
)
601 char *s
= strerror (errno
);
603 fatal ("%s for %s", s
, name
);
606 /* This is the guts of the interface to the Post Office Protocol. */
611 #include <sys/socket.h>
612 #include <netinet/in.h>
622 * The full valid syntax for a POP mailbox specification for movemail
623 * is "po:username:hostname". The ":hostname" is optional; if it is
624 * omitted, the MAILHOST environment variable will be consulted. Note
625 * that by the time popmail() is called the "po:" has been stripped
626 * off of the front of the mailbox name.
628 * If the mailbox is in the form "po:username:hostname", then it is
629 * modified by this function -- the second colon is replaced by a
632 * Return a value suitable for passing to `exit'.
636 popmail (char *mailbox
, char *outfile
, bool preserve
, char *password
,
644 int start
, end
, increment
;
645 char *user
, *hostname
;
648 if ((hostname
= strchr (mailbox
, ':')))
651 server
= pop_open (hostname
, user
, password
, POP_NO_GETPASS
);
654 error ("Error connecting to POP server: %s", pop_error
, 0);
658 if (pop_stat (server
, &nmsgs
, &nbytes
))
660 error ("Error getting message count from POP server: %s", pop_error
, 0);
670 mbfi
= open (outfile
, O_WRONLY
| O_BINARY
| O_CREAT
| O_EXCL
, 0666);
674 error ("Error in open: %s, %s", strerror (errno
), outfile
);
678 if (fchown (mbfi
, getuid (), -1) != 0)
680 int fchown_errno
= errno
;
682 if (fstat (mbfi
, &st
) != 0 || st
.st_uid
!= getuid ())
685 error ("Error in fchown: %s, %s", strerror (fchown_errno
), outfile
);
690 mbf
= fdopen (mbfi
, "wb");
694 error ("Error in fdopen: %s", strerror (errno
), 0);
713 for (i
= start
; i
* increment
<= end
* increment
; i
+= increment
)
714 if (! (mbx_delimit_begin (mbf
)
715 && pop_retr (server
, i
, mbf
)
716 && mbx_delimit_end (mbf
)
717 && fflush (mbf
) == 0))
720 error ("Error in POP retrieving: %s", strerror (errno
), 0);
726 if (fsync (mbfi
) != 0 && errno
!= EINVAL
)
728 error ("Error in fsync: %s", strerror (errno
), 0);
733 if (fclose (mbf
) != 0)
735 error ("Error in fclose: %s", strerror (errno
), 0);
740 for (i
= 1; i
<= nmsgs
; i
++)
742 if (pop_delete (server
, i
))
744 error ("Error from POP server: %s", pop_error
, 0);
750 if (pop_quit (server
))
752 error ("Error from POP server: %s", pop_error
, 0);
760 pop_retr (popserver server
, int msgno
, FILE *arg
)
765 if (pop_retrieve_first (server
, msgno
, &line
))
767 error ("Error from POP server: %s", pop_error
, 0);
772 while ((ret
= pop_retrieve_next (server
, &line
)) >= 0)
777 if (! mbx_write (line
, ret
, arg
))
779 int write_errno
= errno
;
788 error ("Error from POP server: %s", pop_error
, 0);
797 mbx_write (char *line
, int len
, FILE *mbf
)
799 #ifdef MOVEMAIL_QUOTE_POP_FROM_LINES
800 /* Do this as a macro instead of using strcmp to save on execution time. */
801 # define IS_FROM_LINE(a) ((a[0] == 'F') \
806 if (IS_FROM_LINE (line
))
808 if (fputc ('>', mbf
) < 0)
812 if (line
[0] == '\037')
814 if (fputs ("^_", mbf
) < 0)
819 return fwrite (line
, 1, len
, mbf
) == len
&& 0 <= fputc ('\n', mbf
);
823 mbx_delimit_begin (FILE *mbf
)
825 time_t now
= time (NULL
);
826 struct tm
*ltime
= localtime (&now
);
831 if (! strftime (fromline
, sizeof fromline
,
832 "From movemail %a %b %e %T %Y\n", ltime
))
837 return 0 <= fputs (fromline
, mbf
);
841 mbx_delimit_end (FILE *mbf
)
843 return 0 <= putc ('\n', mbf
);
846 #endif /* MAIL_USE_POP */