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 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, 675 Mass Ave, Cambridge, MA 02139, USA. */
21 /* Extracted from XEmacs 19.13, which said: "synched up with: FSF 19.28." */
23 /* Important notice: defining MAIL_USE_FLOCK or MAIL_USE_LOCKF *will
24 cause loss of mail* if you do it on a system that does not normally
25 use flock as its way of interlocking access to inbox files. The
26 setting of MAIL_USE_FLOCK and MAIL_USE_LOCKF *must agree* with the
27 system's own conventions. It is not a choice that is up to you.
29 So, if your system uses lock files rather than flock, then the only way
30 you can get proper operation is to enable movemail to write lockfiles there.
31 This means you must either give that directory access modes
32 that permit everyone to write lockfiles in it, or you must make movemail
33 a setuid or setgid program. */
36 * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
38 * Added POP (Post Office Protocol) service. When compiled -DPOP
39 * movemail will accept input filename arguments of the form
40 * "po:username". This will cause movemail to open a connection to
41 * a pop server running on $MAILHOST (environment variable). Movemail
42 * must be setuid to root in order to work with POP.
44 * New module: popmail.c
46 * main - added code within #ifdef MAIL_USE_POP; added setuid (getuid ())
48 * New routines in movemail.c:
49 * get_errmsg - return pointer to system error message
53 #define NO_SHORTNAMES /* Tell config not to load remap.h */
56 #include <time.h> /* for time() */
57 #include <stdio.h> /* for printf() */
58 #include <string.h> /* strcpy() */
60 #include <sys/types.h>
67 #endif /* !WAITTYPE */
71 #endif /* !WIFEXITED */
74 #define WRETCODE(w) WEXITSTATUS(w)
75 #endif /* !WRETCODE */
101 #include <sys/locking.h>
104 #ifdef MAIL_USE_LOCKF
105 #define MAIL_USE_SYSTEM_LOCK
108 #ifdef MAIL_USE_FLOCK
109 #define MAIL_USE_SYSTEM_LOCK
113 extern int lk_open (), lk_close ();
116 /* Cancel substitutions made by config.h for Emacs. */
122 static char *concat (char *s1, char *s2, char *s3);
123 static void *xmalloc (unsigned int size);
128 static void error (char *s1, char *s2, char *s3);
129 static void fatal (char *s1, char *s2);
130 static void pfatal_with_name (char *name);
131 static void pfatal_and_delete (char *name);
133 #if defined(linux) && defined(__GLIBC__)
135 #if !defined(HAVE_STRERROR)
136 #define HAVE_STRERROR 1
137 #endif /* HAVE_STRERROR */
139 #endif /* LINUX && __GLIBC__ */
141 #ifndef HAVE_STRERROR
142 char *strerror (int);
145 /* Nonzero means this is name of a lock file to delete on fatal error. */
146 char *delete_lockname;
153 char *inname, *outname;
158 #ifndef MAIL_USE_SYSTEM_LOCK
165 #endif /* not MAIL_USE_SYSTEM_LOCK */
170 fatal ("two arguments required", "");
179 /* Check access to output file. */
180 if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
181 pfatal_with_name (outname);
183 /* Also check that outname's directory is writeable to the real uid. */
185 char *buf = (char *) xmalloc (strlen (outname) + 1);
187 strcpy (buf, outname);
188 p = buf + strlen (buf);
189 while (p > buf && p[-1] != '/')
193 if (access (buf, W_OK) != 0)
194 pfatal_with_name (buf);
199 if (!memcmp (inname, "po:", 3))
201 int status; char *user;
203 for (user = &inname[strlen (inname) - 1]; user >= inname; user--)
207 status = popmail (user, outname);
212 #endif /* MAIL_USE_POP */
214 /* Check access to input file. */
215 if (access (inname, R_OK | W_OK) != 0)
216 pfatal_with_name (inname);
218 #ifndef MAIL_USE_MMDF
219 #ifndef MAIL_USE_SYSTEM_LOCK
220 /* Use a lock file named /usr/spool/mail/$USER.lock:
221 If it exists, the mail file is locked. */
222 /* Note: this locking mechanism is *required* by the mailer
223 (on systems which use it) to prevent loss of mail.
225 On systems that use a lock file, extracting the mail without locking
226 WILL occasionally cause loss of mail due to timing errors!
228 So, if creation of the lock file fails
229 due to access permission on /usr/spool/mail,
230 you simply MUST change the permission
231 and/or make movemail a setgid program
232 so it can create lock files properly.
234 You might also wish to verify that your system is one
235 which uses lock files for this purpose. Some systems use other methods.
237 If your system uses the `flock' system call for mail locking,
238 define MAIL_USE_SYSTEM_LOCK in config.h or the s-*.h file
239 and recompile movemail. If the s- file for your system
240 should define MAIL_USE_SYSTEM_LOCK but does not, send a bug report
241 to bug-gnu-emacs@prep.ai.mit.edu so we can fix it. */
243 lockname = concat (inname, ".lock", "");
244 tempname = (char *) xmalloc (strlen (inname) + strlen ("EXXXXXX") + 1);
245 strcpy (tempname, inname);
246 p = tempname + strlen (tempname);
247 while (p != tempname && p[-1] != '/')
250 strcpy (p, "EXXXXXX");
256 /* Create the lock file, but not under the lock file name. */
257 /* Give up if cannot do that. */
258 desc = open (tempname, O_WRONLY | O_CREAT | O_EXCL, 0666);
260 pfatal_with_name ("lock file--see source file lib-src/movemail.c");
263 tem = link (tempname, lockname);
269 /* If lock file is a minute old, unlock it. */
270 if (stat (lockname, &st) >= 0)
273 if (st.st_ctime < now - 60)
278 delete_lockname = lockname;
279 #endif /* not MAIL_USE_SYSTEM_LOCK */
280 #endif /* not MAIL_USE_MMDF */
286 #ifndef MAIL_USE_MMDF
287 #ifdef MAIL_USE_SYSTEM_LOCK
288 indesc = open (inname, O_RDWR);
289 #else /* if not MAIL_USE_SYSTEM_LOCK */
290 indesc = open (inname, O_RDONLY);
291 #endif /* not MAIL_USE_SYSTEM_LOCK */
292 #else /* MAIL_USE_MMDF */
293 indesc = lk_open (inname, O_RDONLY, 0, 0, 10);
294 #endif /* MAIL_USE_MMDF */
297 pfatal_with_name (inname);
299 #if defined (BSD) || defined (XENIX)
300 /* In case movemail is setuid to root, make sure the user can
301 read the output file. */
302 /* This is desirable for all systems
303 but I don't want to assume all have the umask system call */
304 umask (umask (0) & 0333);
305 #endif /* BSD or Xenix */
306 outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
308 pfatal_with_name (outname);
309 #ifdef MAIL_USE_SYSTEM_LOCK
310 #ifdef MAIL_USE_LOCKF
311 if (lockf (indesc, F_LOCK, 0) < 0) pfatal_with_name (inname);
312 #else /* not MAIL_USE_LOCKF */
314 if (locking (indesc, LK_RLCK, 0L) < 0) pfatal_with_name (inname);
316 if (flock (indesc, LOCK_EX) < 0) pfatal_with_name (inname);
318 #endif /* not MAIL_USE_LOCKF */
319 #endif /* MAIL_USE_SYSTEM_LOCK */
326 nread = read (indesc, buf, sizeof buf);
327 if (nread != write (outdesc, buf, nread))
329 int saved_errno = errno;
332 pfatal_with_name (outname);
334 if (nread < sizeof buf)
340 if (fsync (outdesc) < 0)
341 pfatal_and_delete (outname);
344 /* Check to make sure no errors before we zap the inbox. */
345 if (close (outdesc) != 0)
346 pfatal_and_delete (outname);
348 #ifdef MAIL_USE_SYSTEM_LOCK
349 #if defined (STRIDE) || defined (XENIX)
350 /* Stride, xenix have file locking, but no ftruncate. This mess will do. */
351 close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
353 ftruncate (indesc, 0L);
354 #endif /* STRIDE or XENIX */
355 #endif /* MAIL_USE_SYSTEM_LOCK */
358 lk_close (indesc, 0, 0, 0);
363 #ifndef MAIL_USE_SYSTEM_LOCK
364 /* Delete the input file; if we can't, at least get rid of its
366 #ifdef MAIL_UNLINK_SPOOL
367 /* This is generally bad to do, because it destroys the permissions
368 that were set on the file. Better to just empty the file. */
369 if (unlink (inname) < 0 && errno != ENOENT)
370 #endif /* MAIL_UNLINK_SPOOL */
371 creat (inname, 0600);
372 #endif /* not MAIL_USE_SYSTEM_LOCK */
378 if (!WIFEXITED (status))
380 else if (WRETCODE (status) != 0)
381 exit (WRETCODE (status));
383 #if !defined (MAIL_USE_MMDF) && !defined (MAIL_USE_SYSTEM_LOCK)
385 #endif /* not MAIL_USE_MMDF and not MAIL_USE_SYSTEM_LOCK */
389 /* Print error message and exit. */
396 unlink (delete_lockname);
401 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
407 printf ("movemail: ");
413 pfatal_with_name (name)
418 s = concat ("", strerror (errno), " for %s");
423 pfatal_and_delete (name)
428 s = concat ("", strerror (errno), " for %s");
433 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
439 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
440 char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
443 strcpy (result + len1, s2);
444 strcpy (result + len1 + len2, s3);
445 *(result + len1 + len2 + len3) = 0;
450 /* Like malloc but get fatal error if memory is exhausted. */
456 void *result = (void *) malloc (size);
458 fatal ("virtual memory exhausted", (char *) 0);
462 /* This is the guts of the interface to the Post Office Protocol. */
466 #include <sys/socket.h>
467 #include <netinet/in.h>
474 /* Cancel substitutions made by config.h for Emacs. */
490 static int debug = 0;
497 popmail (user, outfile)
507 struct passwd *pw = (struct passwd *) getpwuid (getuid ());
509 fatal ("cannot determine user name");
511 host = getenv ("MAILHOST");
514 fatal ("no MAILHOST defined");
517 if (pop_init (host) == NOTOK)
522 if (getline (response, sizeof response, sfi) != OK)
527 if (pop_command ("USER %s", user) == NOTOK
528 || pop_command ("RPOP %s", pw->pw_name) == NOTOK)
530 pop_command ("QUIT");
534 if (pop_stat (&nmsgs, &nbytes) == NOTOK)
536 pop_command ("QUIT");
542 pop_command ("QUIT");
546 mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
549 pop_command ("QUIT");
550 pfatal_and_delete (outfile);
552 fchown (mbfi, getuid (), -1);
554 if ((mbf = fdopen (mbfi, "w")) == NULL)
556 pop_command ("QUIT");
557 pfatal_and_delete (outfile);
560 for (i = 1; i <= nmsgs; i++)
562 mbx_delimit_begin (mbf);
563 if (pop_retr (i, mbx_write, mbf) != OK)
565 pop_command ("QUIT");
570 mbx_delimit_end (mbf);
574 if (fsync (mbfi) < 0)
576 pop_command ("QUIT");
577 pfatal_and_delete (outfile);
580 if (close (mbfi) == -1)
582 pop_command ("QUIT");
583 pfatal_and_delete (outfile);
586 for (i = 1; i <= nmsgs; i++)
588 if (pop_command ("DELE %d", i) == NOTOK)
590 /* Better to ignore this failure. */
594 pop_command ("QUIT");
602 register struct hostent *hp;
603 register struct servent *sp;
604 int lport = IPPORT_RESERVED - 1;
605 struct sockaddr_in sin;
608 hp = gethostbyname (host);
611 sprintf (Errmsg, "MAILHOST unknown: %s", host);
612 /* Reviewed 4.51 safe use of sprintf */
616 sp = getservbyname ("pop", "tcp");
619 strcpy (Errmsg, "tcp/pop: unknown service");
623 sin.sin_family = hp->h_addrtype;
624 memcpy ((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
625 sin.sin_port = sp->s_port;
626 s = rresvport (&lport);
629 sprintf (Errmsg, "error creating socket: %s", get_errmsg ());
630 /* Reviewed 4.51 safe use of sprintf */
634 if (connect (s, (char *)&sin, sizeof sin) < 0)
636 sprintf (Errmsg, "error during connect: %s", get_errmsg ());
637 /* Reviewed 4.51 safe use of sprintf */
642 sfi = fdopen (s, "r");
643 sfo = fdopen (s, "w");
644 if (sfi == NULL || sfo == NULL)
646 sprintf (Errmsg, "error in fdopen: %s", get_errmsg ());
647 /* Reviewed 4.51 safe use of sprintf */
656 pop_command (fmt, a, b, c, d)
662 sprintf (buf, fmt, a, b, c, d); /* Reviewed 4.51 safe use of sprintf */
664 if (debug) fprintf (stderr, "---> %s\n", buf);
665 if (putline (buf, Errmsg, sfo) == NOTOK) return NOTOK;
667 if (getline (buf, sizeof buf, sfi) != OK)
669 strcpy (Errmsg, buf);
673 if (debug) fprintf (stderr, "<--- %s\n", buf);
676 strcpy (Errmsg, buf);
686 pop_stat (nmsgs, nbytes)
691 if (debug) fprintf (stderr, "---> STAT\n");
692 if (putline ("STAT", Errmsg, sfo) == NOTOK)
695 if (getline (buf, sizeof buf, sfi) != OK)
697 strcpy (Errmsg, buf);
701 if (debug) fprintf (stderr, "<--- %s\n", buf);
704 strcpy (Errmsg, buf);
709 sscanf (buf, "+OK %d %d", nmsgs, nbytes);
714 pop_retr (msgno, action, arg)
719 sprintf (buf, "RETR %d", msgno); /* Reviewed 4.51 safe use of sprintf */
720 if (debug) fprintf (stderr, "%s\n", buf);
721 if (putline (buf, Errmsg, sfo) == NOTOK) return NOTOK;
723 if (getline (buf, sizeof buf, sfi) != OK)
725 strcpy (Errmsg, buf);
731 switch (multiline (buf, sizeof buf, sfi))
739 strcpy (Errmsg, buf);
754 while (--n > 0 && (c = fgetc (f)) != EOF)
755 if ((*p++ = c) == '\n') break;
759 strcpy (buf, "error on connection");
763 if (c == EOF && p == buf)
765 strcpy (buf, "connection closed by foreign host");
770 if (*--p == '\n') *p = NULL;
771 if (*--p == '\r') *p = NULL;
775 multiline (buf, n, f)
780 if (getline (buf, n, f) != OK) return NOTOK;
783 if (*(buf+1) == NULL)
794 return strerror (errno);
797 putline (buf, err, f)
802 fprintf (f, "%s\r\n", buf);
806 strcpy (err, "lost connection");
812 mbx_write (line, mbf)
820 mbx_delimit_begin (mbf)
823 fputs ("\f\n0, unseen,,\n", mbf);
826 mbx_delimit_end (mbf)
832 #endif /* MAIL_USE_POP */
834 #ifndef HAVE_STRERROR
839 extern char *sys_errlist[];
842 if (errnum >= 0 && errnum < sys_nerr)
843 return sys_errlist[errnum];
844 return (char *) "Unknown error";
847 #endif /* ! HAVE_STRERROR */