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 */
91 #define DIRECTORY_SEP '/'
93 #ifndef IS_DIRECTORY_SEP
94 #define IS_DIRECTORY_SEP(_c_) ((_c_) == DIRECTORY_SEP)
101 #define sys_wait(var) (*(var) = 0)
102 /* Unfortunately, Samba doesn't seem to properly lock Unix files even
103 though the locking call succeeds (and indeed blocks local access from
104 other NT programs). If you have direct file access using an NFS
105 client or something other than Samba, the locking call might work
106 properly - make sure it does before you enable this! */
107 #define DISABLE_DIRECT_ACCESS
109 #endif /* WIN32_NATIVE */
111 #if defined (HAVE_UNISTD_H)
113 #endif /* unistd.h */
121 #if defined (HAVE_FCNTL_H)
126 #include <sys/locking.h>
130 extern int lk_open (), lk_close ();
133 /* Cancel substitutions made by config.h for Emacs. */
139 static void fatal (char *, char*);
140 static void error (char *, char *, char *);
141 static void usage(int);
142 static void pfatal_with_name (char *);
143 static void pfatal_and_delete (char *);
144 static char *concat (char *, char *, char *);
145 static long *xmalloc (unsigned int);
147 static int popmail (char *, char *, char *);
148 static int pop_retr (popserver server, int msgno,
149 int (*action)(char *, FILE *), FILE *arg);
150 static int mbx_write (char *, FILE *);
151 static int mbx_delimit_begin (FILE *);
152 static int mbx_delimit_end (FILE *);
153 static struct re_pattern_buffer* compile_regex (char* regexp_pattern);
154 static int pop_search_top (popserver server, int msgno, int lines,
155 struct re_pattern_buffer* regexp);
162 struct re_pattern_buffer* regexp_pattern=0;
166 #define VERBOSE(x) if (verbose) { printf x; fflush(stdout); }
168 struct option longopts[] =
170 { "inbox", required_argument, NULL, 'i' },
171 { "outfile", required_argument, NULL, 'o' },
173 { "password", required_argument, NULL, 'p' },
174 { "reverse-pop-order", no_argument, NULL, 'x' },
175 { "keep-messages", no_argument, NULL, 'k' },
176 { "regex", required_argument, NULL, 'r' },
177 { "match-lines", required_argument, NULL, 'l' },
179 { "lock-method", required_argument, NULL, 'm' },
180 { "help", no_argument, NULL, 'h' },
181 { "verbose", no_argument, NULL, 'v' },
191 #if defined(MAIL_LOCK_FLOCK) && defined(HAVE_FLOCK)
192 #define DEFAULT_LOCKING FLOCKING
193 #elif defined(MAIL_LOCK_LOCKF) && defined(HAVE_LOCKF)
194 #define DEFAULT_LOCKING LOCKFING
195 #elif defined(MAIL_LOCK_MMDF) && defined(HAVE_MMDF)
196 #define DEFAULT_LOCKING MMDF
197 #elif defined(MAIL_LOCK_LOCKING) && defined(HAVE_LOCKING)
198 #define DEFAULT_LOCKING LOCKING
200 #define DEFAULT_LOCKING DOTLOCKING
203 #ifndef DISABLE_DIRECT_ACCESS
204 static void lock_dot(char *);
206 static void unlock_dot(char *);
207 static int parse_lock_method(char *);
208 static char *unparse_lock_method(int);
211 main (int argc, char *argv[])
213 char *inname=0, *outname=0, *poppass=0;
214 #ifndef DISABLE_DIRECT_ACCESS
220 int lock_method = DEFAULT_LOCKING;
222 char *maybe_lock_env;
224 maybe_lock_env = getenv("EMACSLOCKMETHOD");
227 printf("maybe-lock_env: %s\n", maybe_lock_env);
228 lock_method = parse_lock_method(maybe_lock_env);
234 char* optstring = "i:o:m:p:l:r:xvhk";
236 char* optstring = "i:o:m:vh";
238 int opt = getopt_long (argc, argv, optstring, longopts, 0);
247 case 1: /* one of the standard arguments seen */
256 case 'i': /* infile */
260 case 'o': /* outfile */
264 case 'p': /* pop password */
267 case 'k': keep_messages=1; break;
268 case 'x': reverse = 1; break;
269 case 'l': /* lines to match */
270 match_lines = atoi (optarg);
273 case 'r': /* regular expression */
274 regexp_pattern = compile_regex (optarg);
279 lock_method = parse_lock_method(optarg);
290 while (optind < argc)
293 inname = argv[optind];
295 outname = argv[optind];
297 poppass = argv[optind];
301 if (!inname || !outname)
308 if (lock_method == MMDF)
313 fatal ("Destination file name is empty", 0);
315 VERBOSE(("checking access to output file\n"));
316 /* Check access to output file. */
317 if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
318 pfatal_with_name (outname);
320 /* Also check that outname's directory is writable to the real uid. */
322 char *buf = (char *) xmalloc (strlen (outname) + 1);
324 strcpy (buf, outname);
325 cp = buf + strlen (buf);
326 while (cp > buf && !IS_DIRECTORY_SEP (cp[-1]))
330 if (access (buf, W_OK) != 0)
331 pfatal_with_name (buf);
336 if (!strncmp (inname, "po:", 3))
338 int retcode = popmail (inname + 3, outname, poppass);
345 #endif /* MAIL_USE_POP */
347 #ifndef DISABLE_DIRECT_ACCESS
349 /* Check access to input file. */
350 if (access (inname, R_OK | W_OK) != 0)
351 pfatal_with_name (inname);
358 VERBOSE(("opening input file\n"));
363 indesc = open (inname, O_RDONLY);
367 indesc = open (inname, O_RDWR);
372 indesc = open (inname, O_RDWR);
377 indesc = open (inname, O_RDWR);
382 indesc = lk_open (inname, O_RDONLY, 0, 0, 10);
389 pfatal_with_name (inname);
392 /* In case movemail is setuid to root, make sure the user can
393 read the output file. */
394 umask (umask (0) & 0333);
397 outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
399 pfatal_with_name (outname);
401 VERBOSE(("locking input file\n"));
407 if (lockf (indesc, F_LOCK, 0) < 0)
408 pfatal_with_name (inname);
413 if (flock (indesc, LOCK_EX) < 0)
414 pfatal_with_name (inname);
419 if (locking (indesc, LK_RLCK, -1L) < 0)
420 pfatal_with_name (inname);
428 VERBOSE(("copying input file to output file\n"));
435 nread = read (indesc, buf, sizeof buf);
436 if (nread != write (outdesc, buf, nread))
438 int saved_errno = errno;
441 pfatal_with_name (outname);
443 if (nread < (int) sizeof buf)
449 if (fsync (outdesc) < 0)
450 pfatal_and_delete (outname);
453 /* Check to make sure no errors before we zap the inbox. */
454 if (close (outdesc) != 0)
455 pfatal_and_delete (outname);
457 VERBOSE(("deleting or truncating input file\n"));
464 #ifdef HAVE_FTRUNCATE
465 ftruncate (indesc, 0L);
467 close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
473 lk_close (indesc, 0, 0, 0);
477 creat (inname, 0600);
485 if (!WIFEXITED (status))
487 else if (WEXITSTATUS (status) != 0)
488 exit (WEXITSTATUS (status));
490 if (lock_method == DOTLOCKING)
493 #endif /* not DISABLE_DIRECT_ACCESS */
499 usage(int lock_method)
501 printf ("Usage: movemail [-rvxkh] [-l lines ] [-m method ] [-i] inbox [-o] destfile [[-p] POP-password]\n");
502 printf("where method is one of: dot");
515 printf("\nDefault is: %s\n", unparse_lock_method(lock_method));
520 unparse_lock_method(int lock_method)
524 case DOTLOCKING: return "dot";
525 case FLOCKING: return "flock";
526 case LOCKFING: return "lockf";
527 case LOCKING: return "locking";
528 case MMDF: return "mmdf";
529 default: abort();return 0;
534 parse_lock_method(char *method_name)
536 if (!strcmp("dot", method_name) || !strcmp("file", method_name))
539 else if (!strcmp("lockf", method_name))
543 else if (!strcmp("flock", method_name))
547 else if (!strcmp("mmdf", method_name))
551 else if (!strcmp("locking", method_name))
555 fatal("invalid lock method: %s", method_name);
556 return 0; /* unreached */
560 dot_filename(char *filename)
562 return concat (filename, ".lock", "");
565 static char *dotlock_filename = NULL;
567 #ifndef DISABLE_DIRECT_ACCESS
569 lock_dot(char *filename)
578 dotlock_filename = (char *) xmalloc(strlen(filename) + 1);
580 /* Use a lock file named after our first argument with .lock appended:
581 If it exists, the mail file is locked. */
583 lockname = dot_filename(filename);
584 tempname = (char *) xmalloc (strlen (filename) + strlen ("EXXXXXX") + 1);
585 strcpy (tempname, filename);
586 p = tempname + strlen (tempname);
587 while (p != tempname && !IS_DIRECTORY_SEP (p[-1]))
590 strcpy (p, "EXXXXXX");
596 /* Create the lock file, but not under the lock file name. */
597 /* Give up if cannot do that. */
598 desc = open (tempname, O_WRONLY | O_CREAT | O_EXCL, 0666);
601 char *message = (char *) xmalloc (strlen (tempname) + 50);
602 sprintf (message, "%s--see source file lib-src/movemail.c",
604 pfatal_with_name (message);
608 tem = link (tempname, lockname);
614 /* If lock file is five minutes old, unlock it.
615 Five minutes should be good enough to cope with crashes
616 and wedgitude, and long enough to avoid being fooled
617 by time differences between machines. */
618 if (stat (lockname, &st) >= 0)
621 if (st.st_ctime < now - 300)
625 strcpy(dotlock_filename, filename);
627 #endif /* not DISABLE_DIRECT_ACCESS */
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>
717 #include "../src/syspwd.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 ("%s", pop_error, NULL);
748 VERBOSE(("stat'ing messages\n"));
749 if (pop_stat (server, &nmsgs, &nbytes))
751 error ("%s", pop_error, 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(CYGWIN) && !defined(WIN32_NATIVE)
774 fchown (mbfi, getuid (), (gid_t) -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 ("%s", Errmsg, 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 ("%s", pop_error, NULL);
855 VERBOSE(("closing server \n"));
856 if (pop_quit (server))
858 error ("%s", pop_error, NULL);
866 pop_retr (popserver server, int msgno, int (*action)(char *, FILE *), FILE *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 */