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 /* Important notice: defining MAIL_USE_FLOCK or MAIL_USE_LOCKF *will
22 cause loss of mail* if you do it on a system that does not normally
23 use flock as its way of interlocking access to inbox files. The
24 setting of MAIL_USE_FLOCK and MAIL_USE_LOCKF *must agree* with the
25 system's own conventions. It is not a choice that is up to you.
27 So, if your system uses lock files rather than flock, then the only way
28 you can get proper operation is to enable movemail to write lockfiles there.
29 This means you must either give that directory access modes
30 that permit everyone to write lockfiles in it, or you must make movemail
31 a setuid or setgid program. */
34 * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
36 * Added POP (Post Office Protocol) service. When compiled -DPOP
37 * movemail will accept input filename arguments of the form
38 * "po:username". This will cause movemail to open a connection to
39 * a pop server running on $MAILHOST (environment variable). Movemail
40 * must be setuid to root in order to work with POP.
42 * New module: popmail.c
44 * main - added code within #ifdef MAIL_USE_POP; added setuid (getuid ())
46 * New routines in movemail.c:
47 * get_errmsg - return pointer to system error message
51 #include <sys/types.h>
55 #define NO_SHORTNAMES /* Tell config not to load remap.h */
56 #include <../src/config.h>
57 #include <../src/syswait.h>
79 #include <sys/locking.h>
83 #define MAIL_USE_SYSTEM_LOCK
87 #define MAIL_USE_SYSTEM_LOCK
91 extern int lk_open (), lk_close ();
94 /* Cancel substitutions made by config.h for Emacs. */
106 /* Nonzero means this is name of a lock file to delete on fatal error. */
107 char *delete_lockname
;
113 char *inname
, *outname
;
118 #ifndef MAIL_USE_SYSTEM_LOCK
125 #endif /* not MAIL_USE_SYSTEM_LOCK */
130 fatal ("two arguments required");
139 /* Check access to output file. */
140 if (access (outname
, F_OK
) == 0 && access (outname
, W_OK
) != 0)
141 pfatal_with_name (outname
);
143 /* Also check that outname's directory is writeable to the real uid. */
145 char *buf
= (char *) xmalloc (strlen (outname
) + 1);
147 strcpy (buf
, outname
);
148 p
= buf
+ strlen (buf
);
149 while (p
> buf
&& p
[-1] != '/')
153 if (access (buf
, W_OK
) != 0)
154 pfatal_with_name (buf
);
159 if (!strncmp (inname
, "po:", 3))
161 int status
; char *user
;
163 for (user
= &inname
[strlen (inname
) - 1]; user
>= inname
; user
--)
167 status
= popmail (user
, outname
);
172 #endif /* MAIL_USE_POP */
174 /* Check access to input file. */
175 if (access (inname
, R_OK
| W_OK
) != 0)
176 pfatal_with_name (inname
);
178 #ifndef MAIL_USE_MMDF
179 #ifndef MAIL_USE_SYSTEM_LOCK
180 /* Use a lock file named /usr/spool/mail/$USER.lock:
181 If it exists, the mail file is locked. */
182 /* Note: this locking mechanism is *required* by the mailer
183 (on systems which use it) to prevent loss of mail.
185 On systems that use a lock file, extracting the mail without locking
186 WILL occasionally cause loss of mail due to timing errors!
188 So, if creation of the lock file fails
189 due to access permission on /usr/spool/mail,
190 you simply MUST change the permission
191 and/or make movemail a setgid program
192 so it can create lock files properly.
194 You might also wish to verify that your system is one
195 which uses lock files for this purpose. Some systems use other methods.
197 If your system uses the `flock' system call for mail locking,
198 define MAIL_USE_SYSTEM_LOCK in config.h or the s-*.h file
199 and recompile movemail. If the s- file for your system
200 should define MAIL_USE_SYSTEM_LOCK but does not, send a bug report
201 to bug-gnu-emacs@prep.ai.mit.edu so we can fix it. */
203 lockname
= concat (inname
, ".lock", "");
204 tempname
= (char *) xmalloc (strlen (inname
) + strlen ("EXXXXXX") + 1);
205 strcpy (tempname
, inname
);
206 p
= tempname
+ strlen (tempname
);
207 while (p
!= tempname
&& p
[-1] != '/')
210 strcpy (p
, "EXXXXXX");
216 /* Create the lock file, but not under the lock file name. */
217 /* Give up if cannot do that. */
218 desc
= open (tempname
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666);
220 pfatal_with_name ("lock file--see source file lib-src/movemail.c");
223 tem
= link (tempname
, lockname
);
229 /* If lock file is a minute old, unlock it. */
230 if (stat (lockname
, &st
) >= 0)
233 if (st
.st_ctime
< now
- 60)
238 delete_lockname
= lockname
;
239 #endif /* not MAIL_USE_SYSTEM_LOCK */
240 #endif /* not MAIL_USE_MMDF */
246 #ifndef MAIL_USE_MMDF
247 #ifdef MAIL_USE_SYSTEM_LOCK
248 indesc
= open (inname
, O_RDWR
);
249 #else /* if not MAIL_USE_SYSTEM_LOCK */
250 indesc
= open (inname
, O_RDONLY
);
251 #endif /* not MAIL_USE_SYSTEM_LOCK */
252 #else /* MAIL_USE_MMDF */
253 indesc
= lk_open (inname
, O_RDONLY
, 0, 0, 10);
254 #endif /* MAIL_USE_MMDF */
257 pfatal_with_name (inname
);
259 #if defined (BSD) || defined (XENIX)
260 /* In case movemail is setuid to root, make sure the user can
261 read the output file. */
262 /* This is desirable for all systems
263 but I don't want to assume all have the umask system call */
264 umask (umask (0) & 0333);
265 #endif /* BSD or Xenix */
266 outdesc
= open (outname
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666);
268 pfatal_with_name (outname
);
269 #ifdef MAIL_USE_SYSTEM_LOCK
270 #ifdef MAIL_USE_LOCKF
271 if (lockf (indesc
, F_LOCK
, 0) < 0) pfatal_with_name (inname
);
272 #else /* not MAIL_USE_LOCKF */
274 if (locking (indesc
, LK_RLCK
, 0L) < 0) pfatal_with_name (inname
);
276 if (flock (indesc
, LOCK_EX
) < 0) pfatal_with_name (inname
);
278 #endif /* not MAIL_USE_LOCKF */
279 #endif /* MAIL_USE_SYSTEM_LOCK */
286 nread
= read (indesc
, buf
, sizeof buf
);
287 if (nread
!= write (outdesc
, buf
, nread
))
289 int saved_errno
= errno
;
292 pfatal_with_name (outname
);
294 if (nread
< sizeof buf
)
300 if (fsync (outdesc
) < 0)
301 pfatal_and_delete (outname
);
304 /* Check to make sure no errors before we zap the inbox. */
305 if (close (outdesc
) != 0)
306 pfatal_and_delete (outname
);
308 #ifdef MAIL_USE_SYSTEM_LOCK
309 #if defined (STRIDE) || defined (XENIX)
310 /* Stride, xenix have file locking, but no ftruncate. This mess will do. */
311 close (open (inname
, O_CREAT
| O_TRUNC
| O_RDWR
, 0666));
313 ftruncate (indesc
, 0L);
314 #endif /* STRIDE or XENIX */
315 #endif /* MAIL_USE_SYSTEM_LOCK */
318 lk_close (indesc
, 0, 0, 0);
323 #ifndef MAIL_USE_SYSTEM_LOCK
324 /* Delete the input file; if we can't, at least get rid of its
326 #ifdef MAIL_UNLINK_SPOOL
327 /* This is generally bad to do, because it destroys the permissions
328 that were set on the file. Better to just empty the file. */
329 if (unlink (inname
) < 0 && errno
!= ENOENT
)
330 #endif /* MAIL_UNLINK_SPOOL */
331 creat (inname
, 0600);
332 #endif /* not MAIL_USE_SYSTEM_LOCK */
338 if (!WIFEXITED (status
))
340 else if (WRETCODE (status
) != 0)
341 exit (WRETCODE (status
));
343 #if !defined (MAIL_USE_MMDF) && !defined (MAIL_USE_SYSTEM_LOCK)
345 #endif /* not MAIL_USE_MMDF and not MAIL_USE_SYSTEM_LOCK */
349 /* Print error message and exit. */
355 unlink (delete_lockname
);
360 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
365 printf ("movemail: ");
370 pfatal_with_name (name
)
374 extern char *strerror ();
377 s
= concat ("", strerror (errno
), " for %s");
381 pfatal_and_delete (name
)
385 extern char *strerror ();
388 s
= concat ("", strerror (errno
), " for %s");
393 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
399 int len1
= strlen (s1
), len2
= strlen (s2
), len3
= strlen (s3
);
400 char *result
= (char *) xmalloc (len1
+ len2
+ len3
+ 1);
403 strcpy (result
+ len1
, s2
);
404 strcpy (result
+ len1
+ len2
, s3
);
405 *(result
+ len1
+ len2
+ len3
) = 0;
410 /* Like malloc but get fatal error if memory is exhausted. */
416 char *result
= (char *) malloc (size
);
418 fatal ("virtual memory exhausted", 0);
422 /* This is the guts of the interface to the Post Office Protocol. */
426 #include <sys/socket.h>
427 #include <netinet/in.h>
434 /* Cancel substitutions made by config.h for Emacs. */
450 static int debug
= 0;
456 popmail (user
, outfile
)
466 struct passwd
*pw
= (struct passwd
*) getpwuid (getuid ());
468 fatal ("cannot determine user name");
470 host
= getenv ("MAILHOST");
473 fatal ("no MAILHOST defined");
476 if (pop_init (host
) == NOTOK
)
481 if (getline (response
, sizeof response
, sfi
) != OK
)
486 if (pop_command ("USER %s", user
) == NOTOK
487 || pop_command ("RPOP %s", pw
->pw_name
) == NOTOK
)
489 pop_command ("QUIT");
493 if (pop_stat (&nmsgs
, &nbytes
) == NOTOK
)
495 pop_command ("QUIT");
501 pop_command ("QUIT");
505 mbfi
= open (outfile
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666);
508 pop_command ("QUIT");
509 pfatal_and_delete (outfile
);
511 fchown (mbfi
, getuid (), -1);
513 if ((mbf
= fdopen (mbfi
, "w")) == NULL
)
515 pop_command ("QUIT");
516 pfatal_and_delete (outfile
);
519 for (i
= 1; i
<= nmsgs
; i
++)
521 mbx_delimit_begin (mbf
);
522 if (pop_retr (i
, mbx_write
, mbf
) != OK
)
524 pop_command ("QUIT");
529 mbx_delimit_end (mbf
);
533 if (fsync (mbfi
) < 0)
535 pop_command ("QUIT");
536 pfatal_and_delete (outfile
);
539 if (close (mbfi
) == -1)
541 pop_command ("QUIT");
542 pfatal_and_delete (outfile
);
545 for (i
= 1; i
<= nmsgs
; i
++)
547 if (pop_command ("DELE %d", i
) == NOTOK
)
549 /* Better to ignore this failure. */
553 pop_command ("QUIT");
560 register struct hostent
*hp
;
561 register struct servent
*sp
;
562 int lport
= IPPORT_RESERVED
- 1;
563 struct sockaddr_in sin
;
566 hp
= gethostbyname (host
);
569 sprintf (Errmsg
, "MAILHOST unknown: %s", host
);
573 sp
= getservbyname ("pop", "tcp");
576 strcpy (Errmsg
, "tcp/pop: unknown service");
580 sin
.sin_family
= hp
->h_addrtype
;
581 bcopy (hp
->h_addr
, (char *)&sin
.sin_addr
, hp
->h_length
);
582 sin
.sin_port
= sp
->s_port
;
583 s
= rresvport (&lport
);
586 sprintf (Errmsg
, "error creating socket: %s", get_errmsg ());
590 if (connect (s
, (char *)&sin
, sizeof sin
) < 0)
592 sprintf (Errmsg
, "error during connect: %s", get_errmsg ());
597 sfi
= fdopen (s
, "r");
598 sfo
= fdopen (s
, "w");
599 if (sfi
== NULL
|| sfo
== NULL
)
601 sprintf (Errmsg
, "error in fdopen: %s", get_errmsg ());
609 pop_command (fmt
, a
, b
, c
, d
)
615 sprintf (buf
, fmt
, a
, b
, c
, d
);
617 if (debug
) fprintf (stderr
, "---> %s\n", buf
);
618 if (putline (buf
, Errmsg
, sfo
) == NOTOK
) return NOTOK
;
620 if (getline (buf
, sizeof buf
, sfi
) != OK
)
622 strcpy (Errmsg
, buf
);
627 fprintf (stderr
, "<--- %s\n", buf
);
630 strcpy (Errmsg
, buf
);
640 pop_stat (nmsgs
, nbytes
)
646 fprintf (stderr
, "---> STAT\n");
647 if (putline ("STAT", Errmsg
, sfo
) == NOTOK
)
650 if (getline (buf
, sizeof buf
, sfi
) != OK
)
652 strcpy (Errmsg
, buf
);
656 if (debug
) fprintf (stderr
, "<--- %s\n", buf
);
659 strcpy (Errmsg
, buf
);
664 sscanf (buf
, "+OK %d %d", nmsgs
, nbytes
);
669 pop_retr (msgno
, action
, arg
)
674 sprintf (buf
, "RETR %d", msgno
);
675 if (debug
) fprintf (stderr
, "%s\n", buf
);
676 if (putline (buf
, Errmsg
, sfo
) == NOTOK
) return NOTOK
;
678 if (getline (buf
, sizeof buf
, sfi
) != OK
)
680 strcpy (Errmsg
, buf
);
686 switch (multiline (buf
, sizeof buf
, sfi
))
694 strcpy (Errmsg
, buf
);
709 while (--n
> 0 && (c
= fgetc (f
)) != EOF
)
710 if ((*p
++ = c
) == '\n') break;
714 strcpy (buf
, "error on connection");
718 if (c
== EOF
&& p
== buf
)
720 strcpy (buf
, "connection closed by foreign host");
725 if (*--p
== '\n') *p
= NULL
;
726 if (*--p
== '\r') *p
= NULL
;
730 multiline (buf
, n
, f
)
735 if (getline (buf
, n
, f
) != OK
)
739 if (*(buf
+1) == NULL
)
751 extern char *strerror ();
752 return strerror (errno
);
755 putline (buf
, err
, f
)
760 fprintf (f
, "%s\r\n", buf
);
764 strcpy (err
, "lost connection");
770 mbx_write (line
, mbf
)
778 mbx_delimit_begin (mbf
)
781 fputs ("\f\n0, unseen,,\n", mbf
);
784 mbx_delimit_end (mbf
)
790 #endif /* MAIL_USE_POP */
792 #ifndef HAVE_STRERROR
797 extern char *sys_errlist
[];
800 if (errnum
>= 0 && errnum
< sys_nerr
)
801 return sys_errlist
[errnum
];
802 return (char *) "Unknown error";
805 #endif /* ! HAVE_STRERROR */