1 /* movemail foo bar -- move file foo to file bar,
3 Copyright (C) 1986, 1992, 1993, 1994, 1996 Free Software Foundation, Inc.
5 This file is part of XEmacs.
7 XEmacs is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
12 XEmacs is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 You should have received a copy of the GNU General Public License
18 along with XEmacs; 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 Please mail bugs and suggestions to the XEmacs maintainer.
27 * You *must* coordinate the locking method used by movemail with that
28 * used by your mail delivery agent, as well as that of the other mail
29 * user agents on your system. movemail allows you to do this at run
30 * time via the -m flag. Moreover, it uses a default determined by
31 * the MAIL_LOCK_DOT, MAIL_LOCK_LOCKF, MAIL_LOCK_FLOCK,
32 * MAIL_LOCK_LOCKING, and MAIL_LOCK_MMDF preprocessor settings.
36 * Mike Sperber <sperber@informatik.uni-tuebingen.de> reorganized
37 * everything that has to with locking in December 1999.
41 * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
43 * Added POP (Post Office Protocol) service. When compiled -DMAIL_USE_POP
44 * movemail will accept input filename arguments of the form
45 * "po:username". This will cause movemail to open a connection to
46 * a pop server running on $MAILHOST (environment variable). Movemail
47 * must be setuid to root in order to work with POP.
49 * New module: popmail.c
51 * main - added code within #ifdef MAIL_USE_POP; added setuid (getuid ())
53 * New routines in movemail.c:
54 * get_errmsg - return pointer to system error message
56 * Modified August, 1993 by Jonathan Kamens (OpenVision Technologies)
58 * Move all of the POP code into a separate file, "pop.c".
59 * Use strerror instead of get_errmsg.
63 #define NO_SHORTNAMES /* Tell config not to load remap.h */
64 #define DONT_ENCAPSULATE
66 #include <sys/types.h>
70 #include "../src/sysfile.h"
71 #include "../src/syswait.h"
73 #include "../src/systime.h"
80 #include "../src/regex.h"
84 extern int optind, opterr;
87 char * strerror (int errnum);
88 #endif /* HAVE_STRERROR */
95 #define DIRECTORY_SEP '/'
97 #ifndef IS_DIRECTORY_SEP
98 #define IS_DIRECTORY_SEP(_c_) ((_c_) == DIRECTORY_SEP)
105 #define sys_wait(var) (*(var) = 0)
106 /* Unfortunately, Samba doesn't seem to properly lock Unix files even
107 though the locking call succeeds (and indeed blocks local access from
108 other NT programs). If you have direct file access using an NFS
109 client or something other than Samba, the locking call might work
110 properly - make sure it does before you enable this! */
111 #define DISABLE_DIRECT_ACCESS
113 #endif /* WINDOWSNT */
115 #if defined (HAVE_UNISTD_H)
117 #endif /* unistd.h */
125 #if defined (HAVE_FCNTL_H)
130 #include <sys/locking.h>
134 extern int lk_open (), lk_close ();
137 /* Cancel substitutions made by config.h for Emacs. */
143 static void fatal (char *, char*);
144 static void error (char *, char *, char *);
145 static void usage(int);
146 static void pfatal_with_name (char *);
147 static void pfatal_and_delete (char *);
148 static char *concat (char *, char *, char *);
149 static long *xmalloc (unsigned int);
151 static int popmail (char *, char *, char *);
152 static int pop_retr (popserver server, int msgno,
153 int (*action)(char *, FILE *), FILE *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);
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 { "lock-method", required_argument, NULL, 'm' },
184 { "help", no_argument, NULL, 'h' },
185 { "verbose", no_argument, NULL, 'v' },
195 #if defined(MAIL_LOCK_FLOCK) && defined(HAVE_FLOCK)
196 #define DEFAULT_LOCKING FLOCKING
197 #elif defined(MAIL_LOCK_LOCKF) && defined(HAVE_LOCKF)
198 #define DEFAULT_LOCKING LOCKFING
199 #elif defined(MAIL_LOCK_MMDF) && defined(HAVE_MMDF)
200 #define DEFAULT_LOCKING MMDF
201 #elif defined(MAIL_LOCK_LOCKING) && defined(HAVE_LOCKING)
202 #define DEFAULT_LOCKING LOCKING
204 #define DEFAULT_LOCKING DOTLOCKING
207 #ifndef DISABLE_DIRECT_ACCESS
208 static void lock_dot(char *);
210 static void unlock_dot(char *);
211 static int parse_lock_method(char *);
212 static char *unparse_lock_method(int);
215 main (int argc, char *argv[])
217 char *inname=0, *outname=0, *poppass=0;
218 #ifndef DISABLE_DIRECT_ACCESS
224 int lock_method = DEFAULT_LOCKING;
226 char *maybe_lock_env;
228 maybe_lock_env = getenv("EMACSLOCKMETHOD");
231 printf("maybe-lock_env: %s\n", maybe_lock_env);
232 lock_method = parse_lock_method(maybe_lock_env);
238 char* optstring = "i:o:m:p:l:r:xvhk";
240 char* optstring = "i:o:m:vh";
242 int opt = getopt_long (argc, argv, optstring, longopts, 0);
251 case 1: /* one of the standard arguments seen */
260 case 'i': /* infile */
264 case 'o': /* outfile */
268 case 'p': /* pop password */
271 case 'k': keep_messages=1; break;
272 case 'x': reverse = 1; break;
273 case 'l': /* lines to match */
274 match_lines = atoi (optarg);
277 case 'r': /* regular expression */
278 regexp_pattern = compile_regex (optarg);
283 lock_method = parse_lock_method(optarg);
294 while (optind < argc)
297 inname = argv[optind];
299 outname = argv[optind];
301 poppass = argv[optind];
305 if (!inname || !outname)
312 if (lock_method == MMDF)
317 fatal ("Destination file name is empty", 0);
319 VERBOSE(("checking access to output file\n"));
320 /* Check access to output file. */
321 if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
322 pfatal_with_name (outname);
324 /* Also check that outname's directory is writable to the real uid. */
326 char *buf = (char *) xmalloc (strlen (outname) + 1);
328 strcpy (buf, outname);
329 cp = buf + strlen (buf);
330 while (cp > buf && !IS_DIRECTORY_SEP (cp[-1]))
334 if (access (buf, W_OK) != 0)
335 pfatal_with_name (buf);
340 if (!strncmp (inname, "po:", 3))
342 int retcode = popmail (inname + 3, outname, poppass);
349 #endif /* MAIL_USE_POP */
351 #ifndef DISABLE_DIRECT_ACCESS
353 /* Check access to input file. */
354 if (access (inname, R_OK | W_OK) != 0)
355 pfatal_with_name (inname);
362 VERBOSE(("opening input file\n"));
367 indesc = open (inname, O_RDONLY);
371 indesc = open (inname, O_RDWR);
376 indesc = open (inname, O_RDWR);
381 indesc = open (inname, O_RDWR);
386 indesc = lk_open (inname, O_RDONLY, 0, 0, 10);
393 pfatal_with_name (inname);
396 /* In case movemail is setuid to root, make sure the user can
397 read the output file. */
398 umask (umask (0) & 0333);
401 outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
403 pfatal_with_name (outname);
405 VERBOSE(("locking input file\n"));
411 if (lockf (indesc, F_LOCK, 0) < 0)
412 pfatal_with_name (inname);
417 if (flock (indesc, LOCK_EX) < 0)
418 pfatal_with_name (inname);
423 if (locking (indesc, LK_RLCK, -1L) < 0)
424 pfatal_with_name (inname);
432 VERBOSE(("copying input file to output file\n"));
439 nread = read (indesc, buf, sizeof buf);
440 if (nread != write (outdesc, buf, nread))
442 int saved_errno = errno;
445 pfatal_with_name (outname);
447 if (nread < sizeof buf)
453 if (fsync (outdesc) < 0)
454 pfatal_and_delete (outname);
457 /* Check to make sure no errors before we zap the inbox. */
458 if (close (outdesc) != 0)
459 pfatal_and_delete (outname);
461 VERBOSE(("deleting or truncating input file\n"));
468 #ifdef HAVE_FTRUNCATE
469 ftruncate (indesc, 0L);
471 close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
477 lk_close (indesc, 0, 0, 0);
481 creat (inname, 0600);
489 if (!WIFEXITED (status))
491 else if (WEXITSTATUS (status) != 0)
492 exit (WEXITSTATUS (status));
494 if (lock_method == DOTLOCKING)
497 #endif /* not DISABLE_DIRECT_ACCESS */
503 usage(int lock_method)
505 printf ("Usage: movemail [-rvxkh] [-l lines ] [-m method ] [-i] inbox [-o] destfile [[-p] POP-password]\n");
506 printf("where method is one of: dot");
519 printf("\nDefault is: %s\n", unparse_lock_method(lock_method));
524 unparse_lock_method(int lock_method)
528 case DOTLOCKING: return "dot";
529 case FLOCKING: return "flock";
530 case LOCKFING: return "lockf";
531 case LOCKING: return "locking";
532 case MMDF: return "mmdf";
533 default: abort();return 0;
538 parse_lock_method(char *method_name)
540 if (!strcmp("dot", method_name) || !strcmp("file", method_name))
543 else if (!strcmp("lockf", method_name))
547 else if (!strcmp("flock", method_name))
551 else if (!strcmp("mmdf", method_name))
555 else if (!strcmp("locking", method_name))
559 fatal("invalid lock method: %s", method_name);
560 return 0; /* unreached */
564 dot_filename(char *filename)
566 return concat (filename, ".lock", "");
569 static char *dotlock_filename = NULL;
571 #ifndef DISABLE_DIRECT_ACCESS
573 lock_dot(char *filename)
582 dotlock_filename = (char *) xmalloc(strlen(filename) + 1);
584 /* Use a lock file named after our first argument with .lock appended:
585 If it exists, the mail file is locked. */
587 lockname = dot_filename(filename);
588 tempname = (char *) xmalloc (strlen (filename) + strlen ("EXXXXXX") + 1);
589 strcpy (tempname, filename);
590 p = tempname + strlen (tempname);
591 while (p != tempname && !IS_DIRECTORY_SEP (p[-1]))
594 strcpy (p, "EXXXXXX");
600 /* Create the lock file, but not under the lock file name. */
601 /* Give up if cannot do that. */
602 desc = open (tempname, O_WRONLY | O_CREAT | O_EXCL, 0666);
605 char *message = (char *) xmalloc (strlen (tempname) + 50);
606 sprintf (message, "%s--see source file lib-src/movemail.c",
608 pfatal_with_name (message);
612 tem = link (tempname, lockname);
618 /* If lock file is five minutes old, unlock it.
619 Five minutes should be good enough to cope with crashes
620 and wedgitude, and long enough to avoid being fooled
621 by time differences between machines. */
622 if (stat (lockname, &st) >= 0)
625 if (st.st_ctime < now - 300)
629 strcpy(dotlock_filename, filename);
631 #endif /* not DISABLE_DIRECT_ACCESS */
634 unlock_dot(char *filename)
636 unlink(dot_filename(filename));
640 maybe_unlock_dot(void)
642 if (dotlock_filename)
643 unlock_dot(dotlock_filename);
646 /* Print error message and exit. */
649 fatal (char *s1, char *s2)
652 error (s1, s2, NULL);
656 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
659 error (char *s1, char *s2, char *s3)
661 fprintf (stderr, "movemail: ");
662 fprintf (stderr, s1, s2, s3);
663 fprintf (stderr, "\n");
667 pfatal_with_name (char *name)
669 char *s = concat ("", strerror (errno), " for %s");
674 pfatal_and_delete (char *name)
676 char *s = concat ("", strerror (errno), " for %s");
681 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
684 concat (char *s1, char *s2, char *s3)
686 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
687 char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
690 strcpy (result + len1, s2);
691 strcpy (result + len1 + len2, s3);
692 *(result + len1 + len2 + len3) = 0;
697 /* Like malloc but get fatal error if memory is exhausted. */
700 xmalloc (unsigned int size)
702 long *result = (long *) malloc (size);
704 fatal ("virtual memory exhausted", 0);
708 /* This is the guts of the interface to the Post Office Protocol. */
713 #include <sys/socket.h>
714 #include <netinet/in.h>
723 #define POP_ERROR (-1)
724 #define POP_RETRIEVED (0)
730 char ibuffer[BUFSIZ];
731 char obuffer[BUFSIZ];
735 popmail (char *user, char *outfile, char *password)
740 short* retrieved_list;
744 VERBOSE(("opening server\n"));
745 server = pop_open (0, user, password, POP_NO_GETPASS);
748 error (pop_error, NULL, NULL);
752 VERBOSE(("stat'ing messages\n"));
753 if (pop_stat (server, &nmsgs, &nbytes))
755 error (pop_error, NULL, NULL);
761 VERBOSE(("closing server\n"));
766 /* build a retrieved table */
767 retrieved_list = (short*) xmalloc (sizeof (short) * (nmsgs+1));
768 memset (retrieved_list, 0, sizeof (short) * (nmsgs+1));
770 mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
774 error ("Error in open: %s, %s", strerror (errno), outfile);
777 #if !defined(__CYGWIN32__) && !defined(WINDOWSNT)
778 fchown (mbfi, getuid (), (gid_t) -1);
781 if ((mbf = fdopen (mbfi, "wb")) == NULL)
784 error ("Error in fdopen: %s", strerror (errno), NULL);
790 for (idx = 0; idx < nmsgs; idx++)
792 i = reverse ? nmsgs - idx : idx + 1;
793 VERBOSE(("checking message %d \n", i));
797 pop_search_top (server, i, match_lines, regexp_pattern) == POP_RETRIEVED)
799 VERBOSE(("retrieving message %d \n", i));
800 mbx_delimit_begin (mbf);
801 if (pop_retr (server, i, mbx_write, mbf) != POP_RETRIEVED)
803 error (Errmsg, NULL, NULL);
810 mbx_delimit_end (mbf);
814 error ("Error in fflush: %s", strerror (errno), NULL);
822 /* On AFS, a call to write only modifies the file in the local
823 * workstation's AFS cache. The changes are not written to the server
824 * until a call to fsync or close is made. Users with AFS home
825 * directories have lost mail when over quota because these checks were
826 * not made in previous versions of movemail. */
829 if (fsync (mbfi) < 0)
831 error ("Error in fsync: %s", strerror (errno), NULL);
836 if (close (mbfi) == -1)
838 error ("Error in close: %s", strerror (errno), NULL);
844 for (i = 1; i <= nmsgs; i++)
846 if (retrieved_list[i] == 1)
848 VERBOSE(("deleting message %d \n", i));
849 if (pop_delete (server, i))
851 error (pop_error, NULL, NULL);
859 VERBOSE(("closing server \n"));
860 if (pop_quit (server))
862 error (pop_error, NULL, NULL);
870 pop_retr (popserver server, int msgno, int (*action)(char *, FILE *), FILE *arg)
875 if (pop_retrieve_first (server, msgno, &line))
877 strncpy (Errmsg, pop_error, sizeof (Errmsg));
878 Errmsg[sizeof (Errmsg)-1] = '\0';
882 while (! (ret = pop_retrieve_next (server, &line)))
887 if ((*action)(line, arg) != POP_RETRIEVED)
889 strcpy (Errmsg, strerror (errno));
897 strncpy (Errmsg, pop_error, sizeof (Errmsg));
898 Errmsg[sizeof (Errmsg)-1] = '\0';
902 return (POP_RETRIEVED);
905 /* search the top lines of each message looking for a match */
907 pop_search_top (popserver server, int msgno, int lines, struct re_pattern_buffer* regexp)
911 int match = POP_DONE;
913 if (pop_top_first (server, msgno, lines, &line))
915 strncpy (Errmsg, pop_error, sizeof (Errmsg));
916 Errmsg[sizeof (Errmsg)-1] = '\0';
920 while (! (ret = pop_top_next (server, &line)))
925 /* VERBOSE (("checking %s\n", line));*/
926 if (match != POP_RETRIEVED)
928 if ((ret = re_match (regexp, line, strlen (line), 0, 0)) == -2 )
930 strcpy (Errmsg, "error in regular expression");
936 match = POP_RETRIEVED;
943 strncpy (Errmsg, pop_error, sizeof (Errmsg));
944 Errmsg[sizeof (Errmsg)-1] = '\0';
951 /* Do this as a macro instead of using strcmp to save on execution time. */
952 #define IS_FROM_LINE(a) ((a[0] == 'F') \
959 mbx_write (char *line, FILE *mbf)
961 if (IS_FROM_LINE (line))
963 if (fputc ('>', mbf) == EOF)
966 if (fputs (line, mbf) == EOF)
968 if (fputc (0x0a, mbf) == EOF)
970 return (POP_RETRIEVED);
974 mbx_delimit_begin (FILE *mbf)
976 if (fputs ("\f\n0, unseen,,\n", mbf) == EOF)
978 return (POP_RETRIEVED);
982 mbx_delimit_end (FILE *mbf)
984 if (putc ('\037', mbf) == EOF)
986 return (POP_RETRIEVED);
989 /* Turn a name, which is an ed-style (but Emacs syntax) regular
990 expression, into a real regular expression by compiling it. */
991 static struct re_pattern_buffer*
992 compile_regex (char* pattern)
995 struct re_pattern_buffer *patbuf=0;
997 patbuf = (struct re_pattern_buffer*) xmalloc (sizeof (struct re_pattern_buffer));
998 patbuf->translate = NULL;
999 patbuf->fastmap = NULL;
1000 patbuf->buffer = NULL;
1001 patbuf->allocated = 0;
1003 err = (char*) re_compile_pattern (pattern, strlen (pattern), patbuf);
1006 error ("%s while compiling pattern", err, NULL);
1015 #endif /* MAIL_USE_POP */
1017 #ifndef HAVE_STRERROR
1019 strerror (int errnum)
1021 extern char *sys_errlist[];
1022 extern int sys_nerr;
1024 if (errnum >= 0 && errnum < sys_nerr)
1025 return sys_errlist[errnum];
1026 return (char *) "Unknown error";
1029 #endif /* ! HAVE_STRERROR */