X-Git-Url: http://git.hcoop.net/bpt/emacs.git/blobdiff_plain/1a9654a0c30ebaf6c00199c39d87be5bd3eb063a..efa2a55cbd069dc74315ca7b4c6a9d3244a70df4:/lib-src/movemail.c diff --git a/lib-src/movemail.c b/lib-src/movemail.c index be18441695..85a28615e4 100644 --- a/lib-src/movemail.c +++ b/lib-src/movemail.c @@ -1,6 +1,6 @@ /* 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 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -16,7 +16,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 -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, 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 @@ -33,7 +34,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Modified January, 1986 by Michael R. Gretzinger (Project Athena) * - * Added POP (Post Office Protocol) service. When compiled -DPOP + * Added POP (Post Office Protocol) service. When compiled -DMAIL_USE_POP * movemail will accept input filename arguments of the form * "po:username". This will cause movemail to open a connection to * a pop server running on $MAILHOST (environment variable). Movemail @@ -61,6 +62,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include <../src/syswait.h> +#include #ifdef MAIL_USE_POP #include "pop.h" #endif @@ -69,6 +71,41 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #undef access #endif /* MSDOS */ +#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 + +/* Ensure all file i/o is in binary mode. */ +#include +int _fmode = _O_BINARY; +#endif /* WINDOWSNT */ + +/* Cancel substitutions made by config.h for Emacs. */ +#undef open +#undef read +#undef write +#undef close + #ifdef USG #include #include @@ -84,7 +121,11 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #endif -#ifdef XENIX +#ifdef STDC_HEADERS +#include +#endif + +#if defined (XENIX) || defined (WINDOWSNT) #include #endif @@ -100,23 +141,29 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ 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_MAILLOCK_H) +#include +/* 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 (); +extern char *rindex (); 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 (); @@ -135,6 +182,7 @@ main (argc, argv) int indesc, outdesc; int nread; WAITTYPE status; + int c, preserve_mail = 0; #ifndef MAIL_USE_SYSTEM_LOCK struct stat st; @@ -145,32 +193,74 @@ main (argc, argv) 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 */ + 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(1); + } + } + + if ( +#ifdef MAIL_USE_POP + (argc - optind < 2) || (argc - optind > 3) +#else + (argc - optind != 2) +#endif + ) { - fprintf (stderr, "Usage: movemail inbox destfile"); - exit(1); + fprintf (stderr, "Usage: movemail [-p] inbox destfile%s\n", +#ifdef MAIL_USE_POP + " [POP-password]" +#else + "" +#endif + ); + exit (1); } - inname = argv[1]; - outname = argv[2]; + inname = argv[optind]; + outname = argv[optind+1]; #ifdef MAIL_USE_MMDF mmdf_init (argv[0]); #endif + if (*outname == 0) + fatal ("Destination file name is empty", 0); + /* 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); - while (p > buf && p[-1] != '/') + while (p > buf && !IS_DIRECTORY_SEP (p[-1])) *--p = 0; if (p == buf) *p++ = '.'; @@ -182,89 +272,109 @@ main (argc, argv) #ifdef MAIL_USE_POP if (!strncmp (inname, "po:", 3)) { - int status; char *user; + int status; - for (user = &inname[strlen (inname) - 1]; user >= inname; user--) - if (*user == ':') - break; - - status = popmail (user, outname); + status = popmail (inname + 3, outname, preserve_mail, + (argc - optind == 3) ? argv[optind+2] : NULL, + pop_reverse_order); exit (status); } setuid (getuid ()); #endif /* MAIL_USE_POP */ +#ifndef DISABLE_DIRECT_ACCESS + /* Check access to input file. */ if (access (inname, R_OK | W_OK) != 0) pfatal_with_name (inname); #ifndef MAIL_USE_MMDF #ifndef MAIL_USE_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); - 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, "%s--see source file lib-src/movemail.c", + 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) { + 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 @@ -280,28 +390,78 @@ main (argc, argv) if (indesc < 0) pfatal_with_name (inname); -#if defined (BSD) || defined (XENIX) +#if defined (BSD_SYSTEM) || defined (XENIX) /* In case movemail is setuid to root, make sure the user can read the output file. */ /* This is desirable for all systems but I don't want to assume all have the umask system call */ umask (umask (0) & 0333); -#endif /* BSD or Xenix */ +#endif /* BSD_SYSTEM || XENIX */ outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666); if (outdesc < 0) pfatal_with_name (outname); + + /* 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 - if (lockf (indesc, F_LOCK, 0) < 0) pfatal_with_name (inname); + status = lockf (indesc, F_LOCK, 0); #else /* not MAIL_USE_LOCKF */ #ifdef XENIX - if (locking (indesc, LK_RLCK, 0L) < 0) pfatal_with_name (inname); + status = locking (indesc, LK_RLCK, 0L); +#else +#ifdef WINDOWSNT + status = locking (indesc, LK_RLCK, -1L); #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 */ + } + + /* 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]; @@ -317,10 +477,21 @@ main (argc, argv) } if (nread < sizeof buf) break; +#if defined (MAIL_USE_MAILLOCK) && defined (HAVE_TOUCHLOCK) + if (spool_name) + { + now = time (0); + if (now - touched_lock > 60) + { + touchlock (); + touched_lock = now; + } + } +#endif /* MAIL_USE_MAILLOCK */ } } -#ifdef BSD +#ifdef BSD_SYSTEM if (fsync (outdesc) < 0) pfatal_and_delete (outname); #endif @@ -330,12 +501,16 @@ main (argc, argv) pfatal_and_delete (outname); #ifdef MAIL_USE_SYSTEM_LOCK + if (! preserve_mail) + { #if defined (STRIDE) || defined (XENIX) - /* Stride, xenix have file locking, but no ftruncate. This mess will do. */ - close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666)); + /* Stride, xenix have file locking, but no ftruncate. + This mess will do. */ + close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666)); #else - ftruncate (indesc, 0L); + ftruncate (indesc, 0L); #endif /* STRIDE or XENIX */ + } #endif /* MAIL_USE_SYSTEM_LOCK */ #ifdef MAIL_USE_MMDF @@ -345,16 +520,25 @@ main (argc, argv) #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 - /* This is generally bad to do, because it destroys the permissions - that were set on the file. Better to just empty the file. */ - if (unlink (inname) < 0 && errno != ENOENT) + /* This is generally bad to do, because it destroys the permissions + that were set on the file. Better to just empty the file. */ + if (unlink (inname) < 0 && errno != ENOENT) #endif /* MAIL_UNLINK_SPOOL */ - creat (inname, 0600); + creat (inname, 0600); + } #endif /* not MAIL_USE_SYSTEM_LOCK */ +#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 (0); } @@ -365,10 +549,57 @@ main (argc, argv) 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 /* ! DISABLE_DIRECT_ACCESS */ + return 0; } + +#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 */ /* Print error message and exit. */ @@ -429,11 +660,11 @@ concat (s1, s2, s3) /* Like malloc but get fatal error if memory is exhausted. */ -char * +long * xmalloc (size) unsigned size; { - char *result = (char *) malloc (size); + long *result = (long *) malloc (size); if (!result) fatal ("virtual memory exhausted", 0); return result; @@ -443,21 +674,16 @@ xmalloc (size) #ifdef MAIL_USE_POP +#ifndef WINDOWSNT #include #include #include -#include +#else +#undef _WINSOCKAPI_ +#include +#endif #include -#ifdef USG -#include -/* 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 @@ -469,29 +695,31 @@ char ibuffer[BUFSIZ]; char obuffer[BUFSIZ]; char Errmsg[80]; -popmail (user, outfile) +popmail (user, outfile, preserve, password, reverse_order) char *user; char *outfile; + int preserve; + char *password; + int reverse_order; { 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; - server = pop_open (0, user, 0, POP_NO_GETPASS); + server = pop_open (0, user, password, POP_NO_GETPASS); if (! server) { - error (pop_error); + error ("Error connecting to POP server: %s", pop_error); return (1); } if (pop_stat (server, &nmsgs, &nbytes)) { - error (pop_error); + error ("Error getting message count from POP server: %s", pop_error); return (1); } @@ -510,7 +738,7 @@ popmail (user, outfile) } fchown (mbfi, getuid (), -1); - if ((mbf = fdopen (mbfi, "w")) == NULL) + if ((mbf = fdopen (mbfi, "wb")) == NULL) { pop_close (server); error ("Error in fdopen: %s", strerror (errno)); @@ -519,10 +747,23 @@ popmail (user, outfile) return (1); } - for (i = 1; i <= nmsgs; i++) + if (reverse_order) + { + start = nmsgs; + end = 1; + increment = -1; + } + else + { + start = 1; + end = nmsgs; + increment = 1; + } + + for (i = start; i * increment <= end * increment; i += increment) { mbx_delimit_begin (mbf); - if (pop_retr (server, i, mbx_write, mbf) != OK) + if (pop_retr (server, i, mbf) != OK) { error (Errmsg); close (mbfi); @@ -545,7 +786,7 @@ popmail (user, outfile) * 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) { error ("Error in fsync: %s", strerror (errno)); @@ -559,28 +800,30 @@ popmail (user, outfile) return (1); } - 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); + pop_close (server); + return (1); + } + } if (pop_quit (server)) { - error (pop_error); + error ("Error from POP server: %s", pop_error); return (1); } return (0); } -pop_retr (server, msgno, action, arg) - PopServer server; - int (*action)(); +int +pop_retr (server, msgno, arg) + popserver server; + FILE *arg; { extern char *strerror (); char *line; @@ -588,17 +831,19 @@ pop_retr (server, msgno, action, arg) 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'; + free(error); return (NOTOK); } - while (! (ret = pop_retrieve_next (server, &line))) + while ((ret = pop_retrieve_next (server, &line)) >= 0) { if (! line) break; - if ((*action)(line, arg) != OK) + if (mbx_write (line, ret, arg) != OK) { strcpy (Errmsg, strerror (errno)); pop_close (server); @@ -608,8 +853,10 @@ pop_retr (server, msgno, action, arg) 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'; + free(error); return (NOTOK); } @@ -624,16 +871,26 @@ pop_retr (server, msgno, action, arg) && (a[4] == ' ')) int -mbx_write (line, mbf) +mbx_write (line, len, mbf) char *line; + int len; FILE *mbf; { +#ifdef MOVEMAIL_QUOTE_POP_FROM_LINES 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);