1 /* movemail foo bar -- move file foo to file bar,
2 locking file foo the way /bin/mail respects.
3 Copyright (C) 1986, 1992, 1993, 1994, 1996 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
7 GNU Emacs is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
22 /* Important notice: defining MAIL_USE_FLOCK or MAIL_USE_LOCKF *will
23 cause loss of mail* if you do it on a system that does not normally
24 use flock as its way of interlocking access to inbox files. The
25 setting of MAIL_USE_FLOCK and MAIL_USE_LOCKF *must agree* with the
26 system's own conventions. It is not a choice that is up to you.
28 So, if your system uses lock files rather than flock, then the only way
29 you can get proper operation is to enable movemail to write lockfiles there.
30 This means you must either give that directory access modes
31 that permit everyone to write lockfiles in it, or you must make movemail
32 a setuid or setgid program. */
35 * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
37 * Added POP (Post Office Protocol) service. When compiled -DMAIL_USE_POP
38 * movemail will accept input filename arguments of the form
39 * "po:username". This will cause movemail to open a connection to
40 * a pop server running on $MAILHOST (environment variable). Movemail
41 * must be setuid to root in order to work with POP.
43 * New module: popmail.c
45 * main - added code within #ifdef MAIL_USE_POP; added setuid (getuid ())
47 * New routines in movemail.c:
48 * get_errmsg - return pointer to system error message
50 * Modified August, 1993 by Jonathan Kamens (OpenVision Technologies)
52 * Move all of the POP code into a separate file, "pop.c".
53 * Use strerror instead of get_errmsg.
57 #define NO_SHORTNAMES /* Tell config not to load remap.h */
58 #define DONT_ENCAPSULATE
59 #include <../src/config.h>
60 #include <sys/types.h>
64 #include "../src/sysfile.h"
65 #include "../src/syswait.h"
67 #include "../src/systime.h"
74 #include "../src/regex.h"
78 extern int optind, opterr;
81 char * strerror (int errnum);
82 #endif /* HAVE_STRERROR */
89 #define DIRECTORY_SEP '/'
91 #ifndef IS_DIRECTORY_SEP
92 #define IS_DIRECTORY_SEP(_c_) ((_c_) == DIRECTORY_SEP)
99 #define sys_wait(var) (*(var) = 0)
100 /* Unfortunately, Samba doesn't seem to properly lock Unix files even
101 though the locking call succeeds (and indeed blocks local access from
102 other NT programs). If you have direct file access using an NFS
103 client or something other than Samba, the locking call might work
104 properly - make sure it does before you enable this! */
105 #define DISABLE_DIRECT_ACCESS
107 #endif /* WINDOWSNT */
109 #if defined (HAVE_UNISTD_H) || defined (USG)
111 #endif /* unistd.h */
119 #if defined (HAVE_FCNTL_H) || defined (USG)
123 #if defined (XENIX) || defined (WINDOWSNT)
124 #include <sys/locking.h>
127 #ifdef MAIL_USE_LOCKF
128 #define MAIL_USE_SYSTEM_LOCK
131 #ifdef MAIL_USE_FLOCK
132 #define MAIL_USE_SYSTEM_LOCK
136 extern int lk_open (), lk_close ();
139 /* Cancel substitutions made by config.h for Emacs. */
145 static void fatal (char *, char*);
146 static void error (char *, char *, char *);
147 static void pfatal_with_name (char *);
148 static void pfatal_and_delete (char *);
149 static char *concat (char *, char *, char *);
150 static long *xmalloc (unsigned int);
152 static int popmail (char *, char *, char *);
153 static int pop_retr (popserver server, int msgno, int (*action)(), void *arg);
154 static int mbx_write (char *, FILE *);
155 static int mbx_delimit_begin (FILE *);
156 static int mbx_delimit_end (FILE *);
157 static struct re_pattern_buffer* compile_regex (char* regexp_pattern);
158 static int pop_search_top (popserver server, int msgno, int lines,
159 struct re_pattern_buffer* regexp);
162 /* Nonzero means this is name of a lock file to delete on fatal error. */
163 char *delete_lockname;
169 struct re_pattern_buffer* regexp_pattern=0;
173 #define VERBOSE(x) if (verbose) { printf x; fflush(stdout); }
175 struct option longopts[] =
177 { "inbox", required_argument, NULL, 'i' },
178 { "outfile", required_argument, NULL, 'o' },
180 { "password", required_argument, NULL, 'p' },
181 { "reverse-pop-order", no_argument, NULL, 'x' },
182 { "keep-messages", no_argument, NULL, 'k' },
183 { "regex", required_argument, NULL, 'r' },
184 { "match-lines", required_argument, NULL, 'l' },
186 { "verbose", no_argument, NULL, 'v' },
191 main (int argc, char *argv[])
193 char *inname=0, *outname=0, *poppass=0;
194 #ifndef DISABLE_DIRECT_ACCESS
200 #ifndef MAIL_USE_SYSTEM_LOCK
207 #endif /* not MAIL_USE_SYSTEM_LOCK */
214 char* optstring = "i:o:p:l:r:xvk";
216 char* optstring = "i:o:v";
218 int opt = getopt_long (argc, argv, optstring, longopts, 0);
227 case 1: /* one of the standard arguments seen */
236 case 'i': /* infile */
240 case 'o': /* outfile */
244 case 'p': /* pop password */
247 case 'k': keep_messages=1; break;
248 case 'x': reverse = 1; break;
249 case 'l': /* lines to match */
250 match_lines = atoi (optarg);
253 case 'r': /* regular expression */
254 regexp_pattern = compile_regex (optarg);
257 case 'v': verbose = 1; break;
261 while (optind < argc)
264 inname = argv[optind];
266 outname = argv[optind];
268 poppass = argv[optind];
272 if (!inname || !outname)
274 fprintf (stderr, "Usage: movemail [-rvxk] [-l lines ] [-i] inbox [-o] destfile [[-p] POP-password]\n");
283 fatal ("Destination file name is empty", 0);
285 /* Check access to output file. */
286 if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
287 pfatal_with_name (outname);
289 /* Also check that outname's directory is writable to the real uid. */
291 char *buf = (char *) xmalloc (strlen (outname) + 1);
293 strcpy (buf, outname);
294 cp = buf + strlen (buf);
295 while (cp > buf && !IS_DIRECTORY_SEP (cp[-1]))
299 if (access (buf, W_OK) != 0)
300 pfatal_with_name (buf);
305 if (!strncmp (inname, "po:", 3))
307 int retcode = popmail (inname + 3, outname, poppass);
314 #endif /* MAIL_USE_POP */
316 #ifndef DISABLE_DIRECT_ACCESS
318 /* Check access to input file. */
319 if (access (inname, R_OK | W_OK) != 0)
320 pfatal_with_name (inname);
322 #ifndef MAIL_USE_MMDF
323 #ifndef MAIL_USE_SYSTEM_LOCK
324 /* Use a lock file named after our first argument with .lock appended:
325 If it exists, the mail file is locked. */
326 /* Note: this locking mechanism is *required* by the mailer
327 (on systems which use it) to prevent loss of mail.
329 On systems that use a lock file, extracting the mail without locking
330 WILL occasionally cause loss of mail due to timing errors!
332 So, if creation of the lock file fails
333 due to access permission on the mail spool directory,
334 you simply MUST change the permission
335 and/or make movemail a setgid program
336 so it can create lock files properly.
338 You might also wish to verify that your system is one
339 which uses lock files for this purpose. Some systems use other methods.
341 If your system uses the `flock' system call for mail locking,
342 define MAIL_USE_SYSTEM_LOCK in config.h or the s-*.h file
343 and recompile movemail. If the s- file for your system
344 should define MAIL_USE_SYSTEM_LOCK but does not, send a bug report
345 to bug-gnu-emacs@prep.ai.mit.edu so we can fix it. */
347 lockname = concat (inname, ".lock", "");
348 tempname = (char *) xmalloc (strlen (inname) + strlen ("EXXXXXX") + 1);
349 strcpy (tempname, inname);
350 p = tempname + strlen (tempname);
351 while (p != tempname && !IS_DIRECTORY_SEP (p[-1]))
354 strcpy (p, "EXXXXXX");
360 /* Create the lock file, but not under the lock file name. */
361 /* Give up if cannot do that. */
362 desc = open (tempname, O_WRONLY | O_CREAT | O_EXCL, 0666);
365 char *message = (char *) xmalloc (strlen (tempname) + 50);
366 sprintf (message, "%s--see source file lib-src/movemail.c",
368 pfatal_with_name (message);
372 tem = link (tempname, lockname);
378 /* If lock file is five minutes old, unlock it.
379 Five minutes should be good enough to cope with crashes
380 and wedgitude, and long enough to avoid being fooled
381 by time differences between machines. */
382 if (stat (lockname, &st) >= 0)
385 if (st.st_ctime < now - 300)
390 delete_lockname = lockname;
391 #endif /* not MAIL_USE_SYSTEM_LOCK */
392 #endif /* not MAIL_USE_MMDF */
398 #ifndef MAIL_USE_MMDF
399 #ifdef MAIL_USE_SYSTEM_LOCK
400 indesc = open (inname, O_RDWR);
401 #else /* if not MAIL_USE_SYSTEM_LOCK */
402 indesc = open (inname, O_RDONLY);
403 #endif /* not MAIL_USE_SYSTEM_LOCK */
404 #else /* MAIL_USE_MMDF */
405 indesc = lk_open (inname, O_RDONLY, 0, 0, 10);
406 #endif /* MAIL_USE_MMDF */
409 pfatal_with_name (inname);
411 #if defined (BSD) || defined (XENIX)
412 /* In case movemail is setuid to root, make sure the user can
413 read the output file. */
414 /* This is desirable for all systems
415 but I don't want to assume all have the umask system call */
416 umask (umask (0) & 0333);
417 #endif /* BSD or Xenix */
418 outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
420 pfatal_with_name (outname);
421 #ifdef MAIL_USE_SYSTEM_LOCK
422 #ifdef MAIL_USE_LOCKF
423 if (lockf (indesc, F_LOCK, 0) < 0) pfatal_with_name (inname);
424 #else /* not MAIL_USE_LOCKF */
426 if (locking (indesc, LK_RLCK, 0L) < 0) pfatal_with_name (inname);
429 if (locking (indesc, LK_RLCK, -1L) < 0) pfatal_with_name (inname);
431 if (flock (indesc, LOCK_EX) < 0) pfatal_with_name (inname);
434 #endif /* not MAIL_USE_LOCKF */
435 #endif /* MAIL_USE_SYSTEM_LOCK */
442 nread = read (indesc, buf, sizeof buf);
443 if (nread != write (outdesc, buf, nread))
445 int saved_errno = errno;
448 pfatal_with_name (outname);
450 if (nread < sizeof buf)
456 if (fsync (outdesc) < 0)
457 pfatal_and_delete (outname);
460 /* Check to make sure no errors before we zap the inbox. */
461 if (close (outdesc) != 0)
462 pfatal_and_delete (outname);
464 #ifdef MAIL_USE_SYSTEM_LOCK
465 #if defined (STRIDE) || defined (XENIX) || defined (WINDOWSNT)
466 /* Stride, xenix have file locking, but no ftruncate. This mess will do. */
467 close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
469 ftruncate (indesc, 0L);
470 #endif /* STRIDE or XENIX */
471 #endif /* MAIL_USE_SYSTEM_LOCK */
474 lk_close (indesc, 0, 0, 0);
479 #ifndef MAIL_USE_SYSTEM_LOCK
480 /* Delete the input file; if we can't, at least get rid of its
482 #ifdef MAIL_UNLINK_SPOOL
483 /* This is generally bad to do, because it destroys the permissions
484 that were set on the file. Better to just empty the file. */
485 if (unlink (inname) < 0 && errno != ENOENT)
486 #endif /* MAIL_UNLINK_SPOOL */
487 creat (inname, 0600);
488 #endif /* not MAIL_USE_SYSTEM_LOCK */
494 if (!WIFEXITED (status))
496 else if (WEXITSTATUS (status) != 0)
497 exit (WEXITSTATUS (status));
499 #if !defined (MAIL_USE_MMDF) && !defined (MAIL_USE_SYSTEM_LOCK)
501 #endif /* not MAIL_USE_MMDF and not MAIL_USE_SYSTEM_LOCK */
503 #endif /* ! DISABLE_DIRECT_ACCESS */
508 /* Print error message and exit. */
511 fatal (char *s1, char *s2)
514 unlink (delete_lockname);
515 error (s1, s2, NULL);
519 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
522 error (char *s1, char *s2, char *s3)
524 fprintf (stderr, "movemail: ");
525 fprintf (stderr, s1, s2, s3);
526 fprintf (stderr, "\n");
530 pfatal_with_name (char *name)
532 char *s = concat ("", strerror (errno), " for %s");
537 pfatal_and_delete (char *name)
539 char *s = concat ("", strerror (errno), " for %s");
544 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
547 concat (char *s1, char *s2, char *s3)
549 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
550 char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
553 strcpy (result + len1, s2);
554 strcpy (result + len1 + len2, s3);
555 *(result + len1 + len2 + len3) = 0;
560 /* Like malloc but get fatal error if memory is exhausted. */
563 xmalloc (unsigned int size)
565 long *result = (long *) malloc (size);
567 fatal ("virtual memory exhausted", 0);
571 /* This is the guts of the interface to the Post Office Protocol. */
576 #include <sys/socket.h>
577 #include <netinet/in.h>
586 #define POP_ERROR (-1)
587 #define POP_RETRIEVED (0)
593 char ibuffer[BUFSIZ];
594 char obuffer[BUFSIZ];
598 popmail (char *user, char *outfile, char *password)
603 short* retrieved_list;
607 VERBOSE(("opening server\r"));
608 server = pop_open (0, user, password, POP_NO_GETPASS);
611 error (pop_error, NULL, NULL);
615 VERBOSE(("stat'ing messages\r"));
616 if (pop_stat (server, &nmsgs, &nbytes))
618 error (pop_error, NULL, NULL);
624 VERBOSE(("closing server\n"));
629 /* build a retrieved table */
630 retrieved_list = (short*) xmalloc (sizeof (short) * (nmsgs+1));
631 memset (retrieved_list, 0, sizeof (short) * (nmsgs+1));
633 mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
637 error ("Error in open: %s, %s", strerror (errno), outfile);
640 #if !defined(__CYGWIN32__) && !defined(WINDOWSNT)
641 fchown (mbfi, getuid (), -1);
644 if ((mbf = fdopen (mbfi, "wb")) == NULL)
647 error ("Error in fdopen: %s", strerror (errno), NULL);
653 for (idx = 0; idx < nmsgs; idx++)
655 i = reverse ? nmsgs - idx : idx + 1;
656 VERBOSE(("checking message %d \r", i));
660 pop_search_top (server, i, match_lines, regexp_pattern) == POP_RETRIEVED)
662 VERBOSE(("retrieving message %d \r", i));
663 mbx_delimit_begin (mbf);
664 if (pop_retr (server, i, mbx_write, mbf) != POP_RETRIEVED)
666 error (Errmsg, NULL, NULL);
673 mbx_delimit_end (mbf);
677 error ("Error in fflush: %s", strerror (errno), NULL);
685 /* On AFS, a call to write only modifies the file in the local
686 * workstation's AFS cache. The changes are not written to the server
687 * until a call to fsync or close is made. Users with AFS home
688 * directories have lost mail when over quota because these checks were
689 * not made in previous versions of movemail. */
692 if (fsync (mbfi) < 0)
694 error ("Error in fsync: %s", strerror (errno), NULL);
699 if (close (mbfi) == -1)
701 error ("Error in close: %s", strerror (errno), NULL);
707 for (i = 1; i <= nmsgs; i++)
709 if (retrieved_list[i] == 1)
711 VERBOSE(("deleting message %d \r", i));
712 if (pop_delete (server, i))
714 error (pop_error, NULL, NULL);
722 VERBOSE(("closing server \n"));
723 if (pop_quit (server))
725 error (pop_error, NULL, NULL);
733 pop_retr (popserver server, int msgno, int (*action)(), void *arg)
738 if (pop_retrieve_first (server, msgno, &line))
740 strncpy (Errmsg, pop_error, sizeof (Errmsg));
741 Errmsg[sizeof (Errmsg)-1] = '\0';
745 while (! (ret = pop_retrieve_next (server, &line)))
750 if ((*action)(line, arg) != POP_RETRIEVED)
752 strcpy (Errmsg, strerror (errno));
760 strncpy (Errmsg, pop_error, sizeof (Errmsg));
761 Errmsg[sizeof (Errmsg)-1] = '\0';
765 return (POP_RETRIEVED);
768 /* search the top lines of each message looking for a match */
770 pop_search_top (popserver server, int msgno, int lines, struct re_pattern_buffer* regexp)
774 int match = POP_DONE;
776 if (pop_top_first (server, msgno, lines, &line))
778 strncpy (Errmsg, pop_error, sizeof (Errmsg));
779 Errmsg[sizeof (Errmsg)-1] = '\0';
783 while (! (ret = pop_top_next (server, &line)))
788 /* VERBOSE (("checking %s\n", line));*/
789 if (match != POP_RETRIEVED)
791 if ((ret = re_match (regexp, line, strlen (line), 0, 0)) == -2 )
793 strcpy (Errmsg, "error in regular expression");
799 match = POP_RETRIEVED;
806 strncpy (Errmsg, pop_error, sizeof (Errmsg));
807 Errmsg[sizeof (Errmsg)-1] = '\0';
814 /* Do this as a macro instead of using strcmp to save on execution time. */
815 #define IS_FROM_LINE(a) ((a[0] == 'F') \
822 mbx_write (char *line, FILE *mbf)
824 if (IS_FROM_LINE (line))
826 if (fputc ('>', mbf) == EOF)
829 if (fputs (line, mbf) == EOF)
831 if (fputc (0x0a, mbf) == EOF)
833 return (POP_RETRIEVED);
837 mbx_delimit_begin (FILE *mbf)
839 if (fputs ("\f\n0, unseen,,\n", mbf) == EOF)
841 return (POP_RETRIEVED);
845 mbx_delimit_end (FILE *mbf)
847 if (putc ('\037', mbf) == EOF)
849 return (POP_RETRIEVED);
852 /* Turn a name, which is an ed-style (but Emacs syntax) regular
853 expression, into a real regular expression by compiling it. */
854 static struct re_pattern_buffer*
855 compile_regex (char* pattern)
858 struct re_pattern_buffer *patbuf=0;
860 patbuf = (struct re_pattern_buffer*) xmalloc (sizeof (struct re_pattern_buffer));
861 patbuf->translate = NULL;
862 patbuf->fastmap = NULL;
863 patbuf->buffer = NULL;
864 patbuf->allocated = 0;
866 err = (char*) re_compile_pattern (pattern, strlen (pattern), patbuf);
869 error ("%s while compiling pattern", err, NULL);
878 #endif /* MAIL_USE_POP */
880 #ifndef HAVE_STRERROR
882 strerror (int errnum)
884 extern char *sys_errlist[];
887 if (errnum >= 0 && errnum < sys_nerr)
888 return sys_errlist[errnum];
889 return (char *) "Unknown error";
892 #endif /* ! HAVE_STRERROR */