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 *), 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);
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 static void lock_dot(char *);
208 static void unlock_dot(char *);
209 static int parse_lock_method(char *);
210 static char *unparse_lock_method(int);
213 main (int argc, char *argv[])
215 char *inname=0, *outname=0, *poppass=0;
216 #ifndef DISABLE_DIRECT_ACCESS
222 int lock_method = DEFAULT_LOCKING;
224 char *maybe_lock_env;
226 maybe_lock_env = getenv("EMACSLOCKMETHOD");
229 printf("maybe-lock_env: %s\n", maybe_lock_env);
230 lock_method = parse_lock_method(maybe_lock_env);
236 char* optstring = "i:o:m:p:l:r:xvhk";
238 char* optstring = "i:o:m:vh";
240 int opt = getopt_long (argc, argv, optstring, longopts, 0);
249 case 1: /* one of the standard arguments seen */
258 case 'i': /* infile */
262 case 'o': /* outfile */
266 case 'p': /* pop password */
269 case 'k': keep_messages=1; break;
270 case 'x': reverse = 1; break;
271 case 'l': /* lines to match */
272 match_lines = atoi (optarg);
275 case 'r': /* regular expression */
276 regexp_pattern = compile_regex (optarg);
281 lock_method = parse_lock_method(optarg);
292 while (optind < argc)
295 inname = argv[optind];
297 outname = argv[optind];
299 poppass = argv[optind];
303 if (!inname || !outname)
310 if (lock_method == MMDF)
315 fatal ("Destination file name is empty", 0);
317 VERBOSE(("checking access to output file\n"));
318 /* Check access to output file. */
319 if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
320 pfatal_with_name (outname);
322 /* Also check that outname's directory is writable to the real uid. */
324 char *buf = (char *) xmalloc (strlen (outname) + 1);
326 strcpy (buf, outname);
327 cp = buf + strlen (buf);
328 while (cp > buf && !IS_DIRECTORY_SEP (cp[-1]))
332 if (access (buf, W_OK) != 0)
333 pfatal_with_name (buf);
338 if (!strncmp (inname, "po:", 3))
340 int retcode = popmail (inname + 3, outname, poppass);
347 #endif /* MAIL_USE_POP */
349 #ifndef DISABLE_DIRECT_ACCESS
351 /* Check access to input file. */
352 if (access (inname, R_OK | W_OK) != 0)
353 pfatal_with_name (inname);
360 VERBOSE(("opening input file\n"));
365 indesc = open (inname, O_RDONLY);
369 indesc = open (inname, O_RDWR);
374 indesc = open (inname, O_RDWR);
379 indesc = open (inname, O_RDWR);
384 indesc = lk_open (inname, O_RDONLY, 0, 0, 10);
391 pfatal_with_name (inname);
394 /* In case movemail is setuid to root, make sure the user can
395 read the output file. */
396 umask (umask (0) & 0333);
399 outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
401 pfatal_with_name (outname);
403 VERBOSE(("locking input file\n"));
409 if (lockf (indesc, F_LOCK, 0) < 0)
410 pfatal_with_name (inname);
415 if (flock (indesc, LOCK_EX) < 0)
416 pfatal_with_name (inname);
421 if (locking (indesc, LK_RLCK, -1L) < 0)
422 pfatal_with_name (inname);
430 VERBOSE(("copying input file to output file\n"));
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 VERBOSE(("deleting or truncating input file\n"));
466 #ifdef HAVE_FTRUNCATE
467 ftruncate (indesc, 0L);
469 close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
475 lk_close (indesc, 0, 0, 0);
479 creat (inname, 0600);
487 if (!WIFEXITED (status))
489 else if (WEXITSTATUS (status) != 0)
490 exit (WEXITSTATUS (status));
492 if (lock_method == DOTLOCKING)
495 #endif /* not DISABLE_DIRECT_ACCESS */
501 usage(int lock_method)
503 printf ("Usage: movemail [-rvxkh] [-l lines ] [-m method ] [-i] inbox [-o] destfile [[-p] POP-password]\n");
504 printf("where method is one of: dot");
517 printf("\nDefault is: %s\n", unparse_lock_method(lock_method));
522 unparse_lock_method(int lock_method)
526 case DOTLOCKING: return "dot";
527 case FLOCKING: return "flock";
528 case LOCKFING: return "lockf";
529 case LOCKING: return "locking";
530 case MMDF: return "mmdf";
531 default: abort();return 0;
536 parse_lock_method(char *method_name)
538 if (!strcmp("dot", method_name) || !strcmp("file", method_name))
541 else if (!strcmp("lockf", method_name))
545 else if (!strcmp("flock", method_name))
549 else if (!strcmp("mmdf", method_name))
553 else if (!strcmp("locking", method_name))
557 fatal("invalid lock method: %s", method_name);
558 return 0; /* unreached */
562 dot_filename(char *filename)
564 return concat (filename, ".lock", "");
567 static char *dotlock_filename = NULL;
570 lock_dot(char *filename)
579 dotlock_filename = (char *) xmalloc(strlen(filename) + 1);
581 /* Use a lock file named after our first argument with .lock appended:
582 If it exists, the mail file is locked. */
584 lockname = dot_filename(filename);
585 tempname = (char *) xmalloc (strlen (filename) + strlen ("EXXXXXX") + 1);
586 strcpy (tempname, filename);
587 p = tempname + strlen (tempname);
588 while (p != tempname && !IS_DIRECTORY_SEP (p[-1]))
591 strcpy (p, "EXXXXXX");
597 /* Create the lock file, but not under the lock file name. */
598 /* Give up if cannot do that. */
599 desc = open (tempname, O_WRONLY | O_CREAT | O_EXCL, 0666);
602 char *message = (char *) xmalloc (strlen (tempname) + 50);
603 sprintf (message, "%s--see source file lib-src/movemail.c",
605 pfatal_with_name (message);
609 tem = link (tempname, lockname);
615 /* If lock file is five minutes old, unlock it.
616 Five minutes should be good enough to cope with crashes
617 and wedgitude, and long enough to avoid being fooled
618 by time differences between machines. */
619 if (stat (lockname, &st) >= 0)
622 if (st.st_ctime < now - 300)
626 strcpy(dotlock_filename, filename);
630 unlock_dot(char *filename)
632 unlink(dot_filename(filename));
636 maybe_unlock_dot(void)
638 if (dotlock_filename)
639 unlock_dot(dotlock_filename);
642 /* Print error message and exit. */
645 fatal (char *s1, char *s2)
648 error (s1, s2, NULL);
652 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
655 error (char *s1, char *s2, char *s3)
657 fprintf (stderr, "movemail: ");
658 fprintf (stderr, s1, s2, s3);
659 fprintf (stderr, "\n");
663 pfatal_with_name (char *name)
665 char *s = concat ("", strerror (errno), " for %s");
670 pfatal_and_delete (char *name)
672 char *s = concat ("", strerror (errno), " for %s");
677 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
680 concat (char *s1, char *s2, char *s3)
682 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
683 char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
686 strcpy (result + len1, s2);
687 strcpy (result + len1 + len2, s3);
688 *(result + len1 + len2 + len3) = 0;
693 /* Like malloc but get fatal error if memory is exhausted. */
696 xmalloc (unsigned int size)
698 long *result = (long *) malloc (size);
700 fatal ("virtual memory exhausted", 0);
704 /* This is the guts of the interface to the Post Office Protocol. */
709 #include <sys/socket.h>
710 #include <netinet/in.h>
719 #define POP_ERROR (-1)
720 #define POP_RETRIEVED (0)
726 char ibuffer[BUFSIZ];
727 char obuffer[BUFSIZ];
731 popmail (char *user, char *outfile, char *password)
736 short* retrieved_list;
740 VERBOSE(("opening server\n"));
741 server = pop_open (0, user, password, POP_NO_GETPASS);
744 error (pop_error, NULL, NULL);
748 VERBOSE(("stat'ing messages\n"));
749 if (pop_stat (server, &nmsgs, &nbytes))
751 error (pop_error, NULL, NULL);
757 VERBOSE(("closing server\n"));
762 /* build a retrieved table */
763 retrieved_list = (short*) xmalloc (sizeof (short) * (nmsgs+1));
764 memset (retrieved_list, 0, sizeof (short) * (nmsgs+1));
766 mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
770 error ("Error in open: %s, %s", strerror (errno), outfile);
773 #if !defined(__CYGWIN32__) && !defined(WINDOWSNT)
774 fchown (mbfi, getuid (), -1);
777 if ((mbf = fdopen (mbfi, "wb")) == NULL)
780 error ("Error in fdopen: %s", strerror (errno), NULL);
786 for (idx = 0; idx < nmsgs; idx++)
788 i = reverse ? nmsgs - idx : idx + 1;
789 VERBOSE(("checking message %d \n", i));
793 pop_search_top (server, i, match_lines, regexp_pattern) == POP_RETRIEVED)
795 VERBOSE(("retrieving message %d \n", i));
796 mbx_delimit_begin (mbf);
797 if (pop_retr (server, i, mbx_write, mbf) != POP_RETRIEVED)
799 error (Errmsg, NULL, NULL);
806 mbx_delimit_end (mbf);
810 error ("Error in fflush: %s", strerror (errno), NULL);
818 /* On AFS, a call to write only modifies the file in the local
819 * workstation's AFS cache. The changes are not written to the server
820 * until a call to fsync or close is made. Users with AFS home
821 * directories have lost mail when over quota because these checks were
822 * not made in previous versions of movemail. */
825 if (fsync (mbfi) < 0)
827 error ("Error in fsync: %s", strerror (errno), NULL);
832 if (close (mbfi) == -1)
834 error ("Error in close: %s", strerror (errno), NULL);
840 for (i = 1; i <= nmsgs; i++)
842 if (retrieved_list[i] == 1)
844 VERBOSE(("deleting message %d \n", i));
845 if (pop_delete (server, i))
847 error (pop_error, NULL, NULL);
855 VERBOSE(("closing server \n"));
856 if (pop_quit (server))
858 error (pop_error, NULL, NULL);
866 pop_retr (popserver server, int msgno, int (*action)(char *, FILE *), void *arg)
871 if (pop_retrieve_first (server, msgno, &line))
873 strncpy (Errmsg, pop_error, sizeof (Errmsg));
874 Errmsg[sizeof (Errmsg)-1] = '\0';
878 while (! (ret = pop_retrieve_next (server, &line)))
883 if ((*action)(line, arg) != POP_RETRIEVED)
885 strcpy (Errmsg, strerror (errno));
893 strncpy (Errmsg, pop_error, sizeof (Errmsg));
894 Errmsg[sizeof (Errmsg)-1] = '\0';
898 return (POP_RETRIEVED);
901 /* search the top lines of each message looking for a match */
903 pop_search_top (popserver server, int msgno, int lines, struct re_pattern_buffer* regexp)
907 int match = POP_DONE;
909 if (pop_top_first (server, msgno, lines, &line))
911 strncpy (Errmsg, pop_error, sizeof (Errmsg));
912 Errmsg[sizeof (Errmsg)-1] = '\0';
916 while (! (ret = pop_top_next (server, &line)))
921 /* VERBOSE (("checking %s\n", line));*/
922 if (match != POP_RETRIEVED)
924 if ((ret = re_match (regexp, line, strlen (line), 0, 0)) == -2 )
926 strcpy (Errmsg, "error in regular expression");
932 match = POP_RETRIEVED;
939 strncpy (Errmsg, pop_error, sizeof (Errmsg));
940 Errmsg[sizeof (Errmsg)-1] = '\0';
947 /* Do this as a macro instead of using strcmp to save on execution time. */
948 #define IS_FROM_LINE(a) ((a[0] == 'F') \
955 mbx_write (char *line, FILE *mbf)
957 if (IS_FROM_LINE (line))
959 if (fputc ('>', mbf) == EOF)
962 if (fputs (line, mbf) == EOF)
964 if (fputc (0x0a, mbf) == EOF)
966 return (POP_RETRIEVED);
970 mbx_delimit_begin (FILE *mbf)
972 if (fputs ("\f\n0, unseen,,\n", mbf) == EOF)
974 return (POP_RETRIEVED);
978 mbx_delimit_end (FILE *mbf)
980 if (putc ('\037', mbf) == EOF)
982 return (POP_RETRIEVED);
985 /* Turn a name, which is an ed-style (but Emacs syntax) regular
986 expression, into a real regular expression by compiling it. */
987 static struct re_pattern_buffer*
988 compile_regex (char* pattern)
991 struct re_pattern_buffer *patbuf=0;
993 patbuf = (struct re_pattern_buffer*) xmalloc (sizeof (struct re_pattern_buffer));
994 patbuf->translate = NULL;
995 patbuf->fastmap = NULL;
996 patbuf->buffer = NULL;
997 patbuf->allocated = 0;
999 err = (char*) re_compile_pattern (pattern, strlen (pattern), patbuf);
1002 error ("%s while compiling pattern", err, NULL);
1011 #endif /* MAIL_USE_POP */
1013 #ifndef HAVE_STRERROR
1015 strerror (int errnum)
1017 extern char *sys_errlist[];
1018 extern int sys_nerr;
1020 if (errnum >= 0 && errnum < sys_nerr)
1021 return sys_errlist[errnum];
1022 return (char *) "Unknown error";
1025 #endif /* ! HAVE_STRERROR */