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 #include <../src/config.h>
59 #include <sys/types.h>
64 #include "../src/syswait.h"
65 #include "../src/systime.h"
75 extern int optind, opterr;
78 static char * strerror (int errnum);
79 #endif /* HAVE_STRERROR */
86 #define DIRECTORY_SEP '/'
88 #ifndef IS_DIRECTORY_SEP
89 #define IS_DIRECTORY_SEP(_c_) ((_c_) == DIRECTORY_SEP)
96 #define sys_wait(var) (*(var) = 0)
97 /* Unfortunately, Samba doesn't seem to properly lock Unix files even
98 though the locking call succeeds (and indeed blocks local access from
99 other NT programs). If you have direct file access using an NFS
100 client or something other than Samba, the locking call might work
101 properly - make sure it does before you enable this! */
102 #define DISABLE_DIRECT_ACCESS
104 #endif /* WINDOWSNT */
106 #if defined (HAVE_UNISTD_H) || defined (USG)
108 #endif /* unistd.h */
116 #if defined (HAVE_FCNTL_H) || defined (USG)
120 #if defined (XENIX) || defined (WINDOWSNT)
121 #include <sys/locking.h>
124 #ifdef MAIL_USE_LOCKF
125 #define MAIL_USE_SYSTEM_LOCK
128 #ifdef MAIL_USE_FLOCK
129 #define MAIL_USE_SYSTEM_LOCK
133 extern int lk_open (), lk_close ();
136 /* Cancel substitutions made by config.h for Emacs. */
142 static void fatal (char *, char*);
143 static void error (char *, char *, char *);
144 static void pfatal_with_name (char *);
145 static void pfatal_and_delete (char *);
146 static char *concat (char *, char *, char *);
147 static long *xmalloc (unsigned int);
149 static int popmail (char *, char *, char *);
150 static int pop_retr (popserver server, int msgno, int (*action)(), void *arg);
151 static int mbx_write (char *, FILE *);
152 static int mbx_delimit_begin (FILE *);
153 static int mbx_delimit_end (FILE *);
154 static struct re_pattern_buffer* compile_regex (char* regexp_pattern);
155 static int pop_search_top (popserver server, int msgno, int lines,
156 struct re_pattern_buffer* regexp);
159 /* Nonzero means this is name of a lock file to delete on fatal error. */
160 char *delete_lockname;
166 struct re_pattern_buffer* regexp_pattern=0;
170 #define VERBOSE(x) if (verbose) { printf x; fflush(stdout); }
172 struct option longopts[] =
174 { "inbox", required_argument, NULL, 'i' },
175 { "outfile", required_argument, NULL, 'o' },
177 { "password", required_argument, NULL, 'p' },
178 { "reverse-pop-order", no_argument, NULL, 'x' },
179 { "keep-messages", no_argument, NULL, 'k' },
180 { "regex", required_argument, NULL, 'r' },
181 { "match-lines", required_argument, NULL, 'l' },
183 { "verbose", no_argument, NULL, 'v' },
188 main (int argc, char *argv[])
190 char *inname=0, *outname=0, *poppass=0;
191 #ifndef DISABLE_DIRECT_ACCESS
197 #ifndef MAIL_USE_SYSTEM_LOCK
204 #endif /* not MAIL_USE_SYSTEM_LOCK */
211 char* optstring = "i:o:p:l:r:xvk";
213 char* optstring = "i:o:v";
215 int opt = getopt_long (argc, argv, optstring, longopts, 0);
224 case 1: /* one of the standard arguments seen */
233 case 'i': /* infile */
237 case 'o': /* outfile */
241 case 'p': /* pop password */
244 case 'k': keep_messages=1; break;
245 case 'x': reverse = 1; break;
246 case 'l': /* lines to match */
247 match_lines = atoi (optarg);
250 case 'r': /* regular expression */
251 regexp_pattern = compile_regex (optarg);
254 case 'v': verbose = 1; break;
258 while (optind < argc)
261 inname = argv[optind];
263 outname = argv[optind];
265 poppass = argv[optind];
269 if (!inname || !outname)
271 fprintf (stderr, "Usage: movemail [-rvxk] [-l lines ] [-i] inbox [-o] destfile [[-p] POP-password]\n");
280 fatal ("Destination file name is empty", 0);
282 /* Check access to output file. */
283 if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
284 pfatal_with_name (outname);
286 /* Also check that outname's directory is writable to the real uid. */
288 char *buf = (char *) xmalloc (strlen (outname) + 1);
290 strcpy (buf, outname);
291 cp = buf + strlen (buf);
292 while (cp > buf && !IS_DIRECTORY_SEP (cp[-1]))
296 if (access (buf, W_OK) != 0)
297 pfatal_with_name (buf);
302 if (!strncmp (inname, "po:", 3))
304 int retcode = popmail (inname + 3, outname, poppass);
309 #endif /* MAIL_USE_POP */
311 #ifndef DISABLE_DIRECT_ACCESS
313 /* Check access to input file. */
314 if (access (inname, R_OK | W_OK) != 0)
315 pfatal_with_name (inname);
317 #ifndef MAIL_USE_MMDF
318 #ifndef MAIL_USE_SYSTEM_LOCK
319 /* Use a lock file named after our first argument with .lock appended:
320 If it exists, the mail file is locked. */
321 /* Note: this locking mechanism is *required* by the mailer
322 (on systems which use it) to prevent loss of mail.
324 On systems that use a lock file, extracting the mail without locking
325 WILL occasionally cause loss of mail due to timing errors!
327 So, if creation of the lock file fails
328 due to access permission on the mail spool directory,
329 you simply MUST change the permission
330 and/or make movemail a setgid program
331 so it can create lock files properly.
333 You might also wish to verify that your system is one
334 which uses lock files for this purpose. Some systems use other methods.
336 If your system uses the `flock' system call for mail locking,
337 define MAIL_USE_SYSTEM_LOCK in config.h or the s-*.h file
338 and recompile movemail. If the s- file for your system
339 should define MAIL_USE_SYSTEM_LOCK but does not, send a bug report
340 to bug-gnu-emacs@prep.ai.mit.edu so we can fix it. */
342 lockname = concat (inname, ".lock", "");
343 tempname = (char *) xmalloc (strlen (inname) + strlen ("EXXXXXX") + 1);
344 strcpy (tempname, inname);
345 p = tempname + strlen (tempname);
346 while (p != tempname && !IS_DIRECTORY_SEP (p[-1]))
349 strcpy (p, "EXXXXXX");
355 /* Create the lock file, but not under the lock file name. */
356 /* Give up if cannot do that. */
357 desc = open (tempname, O_WRONLY | O_CREAT | O_EXCL, 0666);
360 char *message = (char *) xmalloc (strlen (tempname) + 50);
361 sprintf (message, "%s--see source file lib-src/movemail.c",
363 pfatal_with_name (message);
367 tem = link (tempname, lockname);
373 /* If lock file is five minutes old, unlock it.
374 Five minutes should be good enough to cope with crashes
375 and wedgitude, and long enough to avoid being fooled
376 by time differences between machines. */
377 if (stat (lockname, &st) >= 0)
380 if (st.st_ctime < now - 300)
385 delete_lockname = lockname;
386 #endif /* not MAIL_USE_SYSTEM_LOCK */
387 #endif /* not MAIL_USE_MMDF */
393 #ifndef MAIL_USE_MMDF
394 #ifdef MAIL_USE_SYSTEM_LOCK
395 indesc = open (inname, O_RDWR);
396 #else /* if not MAIL_USE_SYSTEM_LOCK */
397 indesc = open (inname, O_RDONLY);
398 #endif /* not MAIL_USE_SYSTEM_LOCK */
399 #else /* MAIL_USE_MMDF */
400 indesc = lk_open (inname, O_RDONLY, 0, 0, 10);
401 #endif /* MAIL_USE_MMDF */
404 pfatal_with_name (inname);
406 #if defined (BSD) || defined (XENIX)
407 /* In case movemail is setuid to root, make sure the user can
408 read the output file. */
409 /* This is desirable for all systems
410 but I don't want to assume all have the umask system call */
411 umask (umask (0) & 0333);
412 #endif /* BSD or Xenix */
413 outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
415 pfatal_with_name (outname);
416 #ifdef MAIL_USE_SYSTEM_LOCK
417 #ifdef MAIL_USE_LOCKF
418 if (lockf (indesc, F_LOCK, 0) < 0) pfatal_with_name (inname);
419 #else /* not MAIL_USE_LOCKF */
421 if (locking (indesc, LK_RLCK, 0L) < 0) pfatal_with_name (inname);
424 if (locking (indesc, LK_RLCK, -1L) < 0) pfatal_with_name (inname);
426 if (flock (indesc, LOCK_EX) < 0) pfatal_with_name (inname);
429 #endif /* not MAIL_USE_LOCKF */
430 #endif /* MAIL_USE_SYSTEM_LOCK */
437 nread = read (indesc, buf, sizeof buf);
438 if (nread != write (outdesc, buf, nread))
440 int saved_errno = errno;
443 pfatal_with_name (outname);
445 if (nread < sizeof buf)
451 if (fsync (outdesc) < 0)
452 pfatal_and_delete (outname);
455 /* Check to make sure no errors before we zap the inbox. */
456 if (close (outdesc) != 0)
457 pfatal_and_delete (outname);
459 #ifdef MAIL_USE_SYSTEM_LOCK
460 #if defined (STRIDE) || defined (XENIX) || defined (WINDOWSNT)
461 /* Stride, xenix have file locking, but no ftruncate. This mess will do. */
462 close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
464 ftruncate (indesc, 0L);
465 #endif /* STRIDE or XENIX */
466 #endif /* MAIL_USE_SYSTEM_LOCK */
469 lk_close (indesc, 0, 0, 0);
474 #ifndef MAIL_USE_SYSTEM_LOCK
475 /* Delete the input file; if we can't, at least get rid of its
477 #ifdef MAIL_UNLINK_SPOOL
478 /* This is generally bad to do, because it destroys the permissions
479 that were set on the file. Better to just empty the file. */
480 if (unlink (inname) < 0 && errno != ENOENT)
481 #endif /* MAIL_UNLINK_SPOOL */
482 creat (inname, 0600);
483 #endif /* not MAIL_USE_SYSTEM_LOCK */
489 if (!WIFEXITED (status))
491 else if (WEXITSTATUS (status) != 0)
492 exit (WEXITSTATUS (status));
494 #if !defined (MAIL_USE_MMDF) && !defined (MAIL_USE_SYSTEM_LOCK)
496 #endif /* not MAIL_USE_MMDF and not MAIL_USE_SYSTEM_LOCK */
498 #endif /* ! DISABLE_DIRECT_ACCESS */
503 /* Print error message and exit. */
506 fatal (char *s1, char *s2)
509 unlink (delete_lockname);
510 error (s1, s2, NULL);
514 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
517 error (char *s1, char *s2, char *s3)
519 fprintf (stderr, "movemail: ");
520 fprintf (stderr, s1, s2, s3);
521 fprintf (stderr, "\n");
525 pfatal_with_name (char *name)
527 char *s = concat ("", strerror (errno), " for %s");
532 pfatal_and_delete (char *name)
534 char *s = concat ("", strerror (errno), " for %s");
539 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
542 concat (char *s1, char *s2, char *s3)
544 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
545 char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
548 strcpy (result + len1, s2);
549 strcpy (result + len1 + len2, s3);
550 *(result + len1 + len2 + len3) = 0;
555 /* Like malloc but get fatal error if memory is exhausted. */
558 xmalloc (unsigned int size)
560 long *result = (long *) malloc (size);
562 fatal ("virtual memory exhausted", 0);
566 /* This is the guts of the interface to the Post Office Protocol. */
571 #include <sys/socket.h>
572 #include <netinet/in.h>
581 #define POP_ERROR (-1)
582 #define POP_RETRIEVED (0)
588 char ibuffer[BUFSIZ];
589 char obuffer[BUFSIZ];
593 popmail (char *user, char *outfile, char *password)
598 short* retrieved_list;
602 VERBOSE(("opening server\r"));
603 server = pop_open (0, user, password, POP_NO_GETPASS);
606 error (pop_error, NULL, NULL);
610 VERBOSE(("stat'ing messages\r"));
611 if (pop_stat (server, &nmsgs, &nbytes))
613 error (pop_error, NULL, NULL);
619 VERBOSE(("closing server\n"));
624 /* build a retrieved table */
625 retrieved_list = (short*) xmalloc (sizeof (short) * (nmsgs+1));
626 memset (retrieved_list, 0, sizeof (short) * (nmsgs+1));
628 mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
632 error ("Error in open: %s, %s", strerror (errno), outfile);
636 fchown (mbfi, getuid (), -1);
639 if ((mbf = fdopen (mbfi, "wb")) == NULL)
642 error ("Error in fdopen: %s", strerror (errno), NULL);
648 for (idx = 0; idx < nmsgs; idx++)
650 i = reverse ? nmsgs - idx : idx + 1;
651 VERBOSE(("checking message %d \r", i));
655 pop_search_top (server, i, match_lines, regexp_pattern) == POP_RETRIEVED)
657 VERBOSE(("retrieving message %d \r", i));
658 mbx_delimit_begin (mbf);
659 if (pop_retr (server, i, mbx_write, mbf) != POP_RETRIEVED)
661 error (Errmsg, NULL, NULL);
668 mbx_delimit_end (mbf);
672 error ("Error in fflush: %s", strerror (errno), NULL);
680 /* On AFS, a call to write only modifies the file in the local
681 * workstation's AFS cache. The changes are not written to the server
682 * until a call to fsync or close is made. Users with AFS home
683 * directories have lost mail when over quota because these checks were
684 * not made in previous versions of movemail. */
687 if (fsync (mbfi) < 0)
689 error ("Error in fsync: %s", strerror (errno), NULL);
694 if (close (mbfi) == -1)
696 error ("Error in close: %s", strerror (errno), NULL);
702 for (i = 1; i <= nmsgs; i++)
704 if (retrieved_list[i] == 1)
706 VERBOSE(("deleting message %d \r", i));
707 if (pop_delete (server, i))
709 error (pop_error, NULL, NULL);
717 VERBOSE(("closing server \n"));
718 if (pop_quit (server))
720 error (pop_error, NULL, NULL);
728 pop_retr (popserver server, int msgno, int (*action)(), void *arg)
733 if (pop_retrieve_first (server, msgno, &line))
735 strncpy (Errmsg, pop_error, sizeof (Errmsg));
736 Errmsg[sizeof (Errmsg)-1] = '\0';
740 while (! (ret = pop_retrieve_next (server, &line)))
745 if ((*action)(line, arg) != POP_RETRIEVED)
747 strcpy (Errmsg, strerror (errno));
755 strncpy (Errmsg, pop_error, sizeof (Errmsg));
756 Errmsg[sizeof (Errmsg)-1] = '\0';
760 return (POP_RETRIEVED);
763 /* search the top lines of each message looking for a match */
765 pop_search_top (popserver server, int msgno, int lines, struct re_pattern_buffer* regexp)
769 int match = POP_DONE;
771 if (pop_top_first (server, msgno, lines, &line))
773 strncpy (Errmsg, pop_error, sizeof (Errmsg));
774 Errmsg[sizeof (Errmsg)-1] = '\0';
778 while (! (ret = pop_top_next (server, &line)))
783 /* VERBOSE (("checking %s\n", line));*/
784 if (match != POP_RETRIEVED)
786 if ((ret = re_match (regexp, line, strlen (line), 0, 0)) == -2 )
788 strcpy (Errmsg, "error in regular expression");
794 match = POP_RETRIEVED;
801 strncpy (Errmsg, pop_error, sizeof (Errmsg));
802 Errmsg[sizeof (Errmsg)-1] = '\0';
809 /* Do this as a macro instead of using strcmp to save on execution time. */
810 #define IS_FROM_LINE(a) ((a[0] == 'F') \
817 mbx_write (char *line, FILE *mbf)
819 if (IS_FROM_LINE (line))
821 if (fputc ('>', mbf) == EOF)
824 if (fputs (line, mbf) == EOF)
826 if (fputc (0x0a, mbf) == EOF)
828 return (POP_RETRIEVED);
832 mbx_delimit_begin (FILE *mbf)
834 if (fputs ("\f\n0, unseen,,\n", mbf) == EOF)
836 return (POP_RETRIEVED);
840 mbx_delimit_end (FILE *mbf)
842 if (putc ('\037', mbf) == EOF)
844 return (POP_RETRIEVED);
847 /* Turn a name, which is an ed-style (but Emacs syntax) regular
848 expression, into a real regular expression by compiling it. */
849 static struct re_pattern_buffer*
850 compile_regex (char* regexp_pattern)
853 struct re_pattern_buffer *patbuf=0;
855 patbuf = (struct re_pattern_buffer*) xmalloc (sizeof (struct re_pattern_buffer));
856 patbuf->translate = NULL;
857 patbuf->fastmap = NULL;
858 patbuf->buffer = NULL;
859 patbuf->allocated = 0;
861 err = (char*) re_compile_pattern (regexp_pattern, strlen (regexp_pattern), patbuf);
864 error ("%s while compiling pattern", err, NULL);
873 #endif /* MAIL_USE_POP */
875 #ifndef HAVE_STRERROR
877 strerror (int errnum)
879 extern char *sys_errlist[];
882 if (errnum >= 0 && errnum < sys_nerr)
883 return sys_errlist[errnum];
884 return (char *) "Unknown error";
887 #endif /* ! HAVE_STRERROR */