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>
75 #include <sys/locking.h>
79 #define MAIL_USE_SYSTEM_LOCK
83 #define MAIL_USE_SYSTEM_LOCK
87 extern int lk_open (), lk_close ();
90 /* Cancel substitutions made by config.h for Emacs. */
102 /* Nonzero means this is name of a lock file to delete on fatal error. */
103 char *delete_lockname
;
109 char *inname
, *outname
;
114 #ifndef MAIL_USE_SYSTEM_LOCK
121 #endif /* not MAIL_USE_SYSTEM_LOCK */
126 fatal ("two arguments required");
135 /* Check access to output file. */
136 if (access (outname
, F_OK
) == 0 && access (outname
, W_OK
) != 0)
137 pfatal_with_name (outname
);
139 /* Also check that outname's directory is writeable to the real uid. */
141 char *buf
= (char *) xmalloc (strlen (outname
) + 1);
143 strcpy (buf
, outname
);
144 p
= buf
+ strlen (buf
);
145 while (p
> buf
&& p
[-1] != '/')
149 if (access (buf
, W_OK
) != 0)
150 pfatal_with_name (buf
);
155 if (!strncmp (inname
, "po:", 3))
157 int status
; char *user
;
159 for (user
= &inname
[strlen (inname
) - 1]; user
>= inname
; user
--)
163 status
= popmail (user
, outname
);
168 #endif /* MAIL_USE_POP */
170 /* Check access to input file. */
171 if (access (inname
, R_OK
| W_OK
) != 0)
172 pfatal_with_name (inname
);
174 #ifndef MAIL_USE_MMDF
175 #ifndef MAIL_USE_SYSTEM_LOCK
176 /* Use a lock file named /usr/spool/mail/$USER.lock:
177 If it exists, the mail file is locked. */
178 /* Note: this locking mechanism is *required* by the mailer
179 (on systems which use it) to prevent loss of mail.
181 On systems that use a lock file, extracting the mail without locking
182 WILL occasionally cause loss of mail due to timing errors!
184 So, if creation of the lock file fails
185 due to access permission on /usr/spool/mail,
186 you simply MUST change the permission
187 and/or make movemail a setgid program
188 so it can create lock files properly.
190 You might also wish to verify that your system is one
191 which uses lock files for this purpose. Some systems use other methods.
193 If your system uses the `flock' system call for mail locking,
194 define MAIL_USE_SYSTEM_LOCK in config.h or the s-*.h file
195 and recompile movemail. If the s- file for your system
196 should define MAIL_USE_SYSTEM_LOCK but does not, send a bug report
197 to bug-gnu-emacs@prep.ai.mit.edu so we can fix it. */
199 lockname
= concat (inname
, ".lock", "");
200 tempname
= (char *) xmalloc (strlen (inname
) + strlen ("EXXXXXX") + 1);
201 strcpy (tempname
, inname
);
202 p
= tempname
+ strlen (tempname
);
203 while (p
!= tempname
&& p
[-1] != '/')
206 strcpy (p
, "EXXXXXX");
212 /* Create the lock file, but not under the lock file name. */
213 /* Give up if cannot do that. */
214 desc
= open (tempname
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666);
216 pfatal_with_name ("lock file--see source file lib-src/movemail.c");
219 tem
= link (tempname
, lockname
);
225 /* If lock file is a minute old, unlock it. */
226 if (stat (lockname
, &st
) >= 0)
229 if (st
.st_ctime
< now
- 60)
234 delete_lockname
= lockname
;
235 #endif /* not MAIL_USE_SYSTEM_LOCK */
236 #endif /* not MAIL_USE_MMDF */
242 #ifndef MAIL_USE_MMDF
243 #ifdef MAIL_USE_SYSTEM_LOCK
244 indesc
= open (inname
, O_RDWR
);
245 #else /* if not MAIL_USE_SYSTEM_LOCK */
246 indesc
= open (inname
, O_RDONLY
);
247 #endif /* not MAIL_USE_SYSTEM_LOCK */
248 #else /* MAIL_USE_MMDF */
249 indesc
= lk_open (inname
, O_RDONLY
, 0, 0, 10);
250 #endif /* MAIL_USE_MMDF */
253 pfatal_with_name (inname
);
255 #if defined (BSD) || defined (XENIX)
256 /* In case movemail is setuid to root, make sure the user can
257 read the output file. */
258 /* This is desirable for all systems
259 but I don't want to assume all have the umask system call */
260 umask (umask (0) & 0333);
261 #endif /* BSD or Xenix */
262 outdesc
= open (outname
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666);
264 pfatal_with_name (outname
);
265 #ifdef MAIL_USE_SYSTEM_LOCK
266 #ifdef MAIL_USE_LOCKF
267 if (lockf (indesc
, F_LOCK
, 0) < 0) pfatal_with_name (inname
);
268 #else /* not MAIL_USE_LOCKF */
270 if (locking (indesc
, LK_RLCK
, 0L) < 0) pfatal_with_name (inname
);
272 if (flock (indesc
, LOCK_EX
) < 0) pfatal_with_name (inname
);
274 #endif /* not MAIL_USE_LOCKF */
275 #endif /* MAIL_USE_SYSTEM_LOCK */
282 nread
= read (indesc
, buf
, sizeof buf
);
283 if (nread
!= write (outdesc
, buf
, nread
))
285 int saved_errno
= errno
;
288 pfatal_with_name (outname
);
290 if (nread
< sizeof buf
)
296 if (fsync (outdesc
) < 0)
297 pfatal_and_delete (outname
);
300 /* Check to make sure no errors before we zap the inbox. */
301 if (close (outdesc
) != 0)
302 pfatal_and_delete (outname
);
304 #ifdef MAIL_USE_SYSTEM_LOCK
305 #if defined (STRIDE) || defined (XENIX)
306 /* Stride, xenix have file locking, but no ftruncate. This mess will do. */
307 close (open (inname
, O_CREAT
| O_TRUNC
| O_RDWR
, 0666));
309 ftruncate (indesc
, 0L);
310 #endif /* STRIDE or XENIX */
311 #endif /* MAIL_USE_SYSTEM_LOCK */
314 lk_close (indesc
, 0, 0, 0);
319 #ifndef MAIL_USE_SYSTEM_LOCK
320 /* Delete the input file; if we can't, at least get rid of its
322 #ifdef MAIL_UNLINK_SPOOL
323 /* This is generally bad to do, because it destroys the permissions
324 that were set on the file. Better to just empty the file. */
325 if (unlink (inname
) < 0 && errno
!= ENOENT
)
326 #endif /* MAIL_UNLINK_SPOOL */
327 creat (inname
, 0600);
328 #endif /* not MAIL_USE_SYSTEM_LOCK */
334 if (!WIFEXITED (status
))
336 else if (WRETCODE (status
) != 0)
337 exit (WRETCODE (status
));
339 #if !defined (MAIL_USE_MMDF) && !defined (MAIL_USE_SYSTEM_LOCK)
341 #endif /* not MAIL_USE_MMDF and not MAIL_USE_SYSTEM_LOCK */
345 /* Print error message and exit. */
351 unlink (delete_lockname
);
356 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
361 printf ("movemail: ");
366 pfatal_with_name (name
)
370 extern char *strerror ();
373 s
= concat ("", strerror (errno
), " for %s");
377 pfatal_and_delete (name
)
381 extern char *strerror ();
384 s
= concat ("", strerror (errno
), " for %s");
389 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
395 int len1
= strlen (s1
), len2
= strlen (s2
), len3
= strlen (s3
);
396 char *result
= (char *) xmalloc (len1
+ len2
+ len3
+ 1);
399 strcpy (result
+ len1
, s2
);
400 strcpy (result
+ len1
+ len2
, s3
);
401 *(result
+ len1
+ len2
+ len3
) = 0;
406 /* Like malloc but get fatal error if memory is exhausted. */
412 char *result
= (char *) malloc (size
);
414 fatal ("virtual memory exhausted", 0);
418 /* This is the guts of the interface to the Post Office Protocol. */
422 #include <sys/socket.h>
423 #include <netinet/in.h>
430 /* Cancel substitutions made by config.h for Emacs. */
446 static int debug
= 0;
452 popmail (user
, outfile
)
462 struct passwd
*pw
= (struct passwd
*) getpwuid (getuid ());
464 fatal ("cannot determine user name");
466 host
= getenv ("MAILHOST");
469 fatal ("no MAILHOST defined");
472 if (pop_init (host
) == NOTOK
)
477 if (getline (response
, sizeof response
, sfi
) != OK
)
482 if (pop_command ("USER %s", user
) == NOTOK
483 || pop_command ("RPOP %s", pw
->pw_name
) == NOTOK
)
485 pop_command ("QUIT");
489 if (pop_stat (&nmsgs
, &nbytes
) == NOTOK
)
491 pop_command ("QUIT");
497 pop_command ("QUIT");
501 mbfi
= open (outfile
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666);
504 pop_command ("QUIT");
505 pfatal_and_delete (outfile
);
507 fchown (mbfi
, getuid (), -1);
509 if ((mbf
= fdopen (mbfi
, "w")) == NULL
)
511 pop_command ("QUIT");
512 pfatal_and_delete (outfile
);
515 for (i
= 1; i
<= nmsgs
; i
++)
517 mbx_delimit_begin (mbf
);
518 if (pop_retr (i
, mbx_write
, mbf
) != OK
)
520 pop_command ("QUIT");
525 mbx_delimit_end (mbf
);
529 if (fsync (mbfi
) < 0)
531 pop_command ("QUIT");
532 pfatal_and_delete (outfile
);
535 if (close (mbfi
) == -1)
537 pop_command ("QUIT");
538 pfatal_and_delete (outfile
);
541 for (i
= 1; i
<= nmsgs
; i
++)
543 if (pop_command ("DELE %d", i
) == NOTOK
)
545 /* Better to ignore this failure. */
549 pop_command ("QUIT");
556 register struct hostent
*hp
;
557 register struct servent
*sp
;
558 int lport
= IPPORT_RESERVED
- 1;
559 struct sockaddr_in sin
;
562 hp
= gethostbyname (host
);
565 sprintf (Errmsg
, "MAILHOST unknown: %s", host
);
569 sp
= getservbyname ("pop", "tcp");
572 strcpy (Errmsg
, "tcp/pop: unknown service");
576 sin
.sin_family
= hp
->h_addrtype
;
577 bcopy (hp
->h_addr
, (char *)&sin
.sin_addr
, hp
->h_length
);
578 sin
.sin_port
= sp
->s_port
;
579 s
= rresvport (&lport
);
582 sprintf (Errmsg
, "error creating socket: %s", get_errmsg ());
586 if (connect (s
, (char *)&sin
, sizeof sin
) < 0)
588 sprintf (Errmsg
, "error during connect: %s", get_errmsg ());
593 sfi
= fdopen (s
, "r");
594 sfo
= fdopen (s
, "w");
595 if (sfi
== NULL
|| sfo
== NULL
)
597 sprintf (Errmsg
, "error in fdopen: %s", get_errmsg ());
605 pop_command (fmt
, a
, b
, c
, d
)
611 sprintf (buf
, fmt
, a
, b
, c
, d
);
613 if (debug
) fprintf (stderr
, "---> %s\n", buf
);
614 if (putline (buf
, Errmsg
, sfo
) == NOTOK
) return NOTOK
;
616 if (getline (buf
, sizeof buf
, sfi
) != OK
)
618 strcpy (Errmsg
, buf
);
623 fprintf (stderr
, "<--- %s\n", buf
);
626 strcpy (Errmsg
, buf
);
636 pop_stat (nmsgs
, nbytes
)
642 fprintf (stderr
, "---> STAT\n");
643 if (putline ("STAT", Errmsg
, sfo
) == NOTOK
)
646 if (getline (buf
, sizeof buf
, sfi
) != OK
)
648 strcpy (Errmsg
, buf
);
652 if (debug
) fprintf (stderr
, "<--- %s\n", buf
);
655 strcpy (Errmsg
, buf
);
660 sscanf (buf
, "+OK %d %d", nmsgs
, nbytes
);
665 pop_retr (msgno
, action
, arg
)
670 sprintf (buf
, "RETR %d", msgno
);
671 if (debug
) fprintf (stderr
, "%s\n", buf
);
672 if (putline (buf
, Errmsg
, sfo
) == NOTOK
) return NOTOK
;
674 if (getline (buf
, sizeof buf
, sfi
) != OK
)
676 strcpy (Errmsg
, buf
);
682 switch (multiline (buf
, sizeof buf
, sfi
))
690 strcpy (Errmsg
, buf
);
705 while (--n
> 0 && (c
= fgetc (f
)) != EOF
)
706 if ((*p
++ = c
) == '\n') break;
710 strcpy (buf
, "error on connection");
714 if (c
== EOF
&& p
== buf
)
716 strcpy (buf
, "connection closed by foreign host");
721 if (*--p
== '\n') *p
= NULL
;
722 if (*--p
== '\r') *p
= NULL
;
726 multiline (buf
, n
, f
)
731 if (getline (buf
, n
, f
) != OK
)
735 if (*(buf
+1) == NULL
)
747 extern char *strerror ();
748 return strerror (errno
);
751 putline (buf
, err
, f
)
756 fprintf (f
, "%s\r\n", buf
);
760 strcpy (err
, "lost connection");
766 mbx_write (line
, mbf
)
774 mbx_delimit_begin (mbf
)
777 fputs ("\f\n0, unseen,,\n", mbf
);
780 mbx_delimit_end (mbf
)
786 #endif /* MAIL_USE_POP */
788 #ifndef HAVE_STRERROR
793 extern char *sys_errlist
[];
796 if (errnum
>= 0 && errnum
< sys_nerr
)
797 return sys_errlist
[errnum
];
798 return (char *) "Unknown error";
801 #endif /* ! HAVE_STRERROR */