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
49 * Modified August, 1993 by Jonathan Kamens (OpenVision Technologies)
51 * Move all of the POP code into a separate file, "pop.c".
52 * Use strerror instead of get_errmsg.
56 #define NO_SHORTNAMES /* Tell config not to load remap.h */
57 #include <../src/config.h>
58 #include <sys/types.h>
63 #include <../src/syswait.h>
88 #include <sys/locking.h>
92 #define MAIL_USE_SYSTEM_LOCK
96 #define MAIL_USE_SYSTEM_LOCK
100 extern int lk_open (), lk_close ();
103 /* Cancel substitutions made by config.h for Emacs. */
116 void pfatal_with_name ();
117 void pfatal_and_delete ();
123 int mbx_delimit_begin ();
124 int mbx_delimit_end ();
126 /* Nonzero means this is name of a lock file to delete on fatal error. */
127 char *delete_lockname
;
134 char *inname
, *outname
;
139 #ifndef MAIL_USE_SYSTEM_LOCK
146 #endif /* not MAIL_USE_SYSTEM_LOCK */
152 fprintf (stderr
, "Usage: movemail inbox destfile\n");
164 fatal ("Destination file name is empty", 0);
166 /* Check access to output file. */
167 if (access (outname
, F_OK
) == 0 && access (outname
, W_OK
) != 0)
168 pfatal_with_name (outname
);
170 /* Also check that outname's directory is writeable to the real uid. */
172 char *buf
= (char *) xmalloc (strlen (outname
) + 1);
174 strcpy (buf
, outname
);
175 p
= buf
+ strlen (buf
);
176 while (p
> buf
&& p
[-1] != '/')
180 if (access (buf
, W_OK
) != 0)
181 pfatal_with_name (buf
);
186 if (!strncmp (inname
, "po:", 3))
190 status
= popmail (inname
+ 3, outname
);
195 #endif /* MAIL_USE_POP */
197 /* Check access to input file. */
198 if (access (inname
, R_OK
| W_OK
) != 0)
199 pfatal_with_name (inname
);
201 #ifndef MAIL_USE_MMDF
202 #ifndef MAIL_USE_SYSTEM_LOCK
203 /* Use a lock file named after our first argument with .lock appended:
204 If it exists, the mail file is locked. */
205 /* Note: this locking mechanism is *required* by the mailer
206 (on systems which use it) to prevent loss of mail.
208 On systems that use a lock file, extracting the mail without locking
209 WILL occasionally cause loss of mail due to timing errors!
211 So, if creation of the lock file fails
212 due to access permission on the mail spool directory,
213 you simply MUST change the permission
214 and/or make movemail a setgid program
215 so it can create lock files properly.
217 You might also wish to verify that your system is one
218 which uses lock files for this purpose. Some systems use other methods.
220 If your system uses the `flock' system call for mail locking,
221 define MAIL_USE_SYSTEM_LOCK in config.h or the s-*.h file
222 and recompile movemail. If the s- file for your system
223 should define MAIL_USE_SYSTEM_LOCK but does not, send a bug report
224 to bug-gnu-emacs@prep.ai.mit.edu so we can fix it. */
226 lockname
= concat (inname
, ".lock", "");
227 tempname
= (char *) xmalloc (strlen (inname
) + strlen ("EXXXXXX") + 1);
228 strcpy (tempname
, inname
);
229 p
= tempname
+ strlen (tempname
);
230 while (p
!= tempname
&& p
[-1] != '/')
233 strcpy (p
, "EXXXXXX");
239 /* Create the lock file, but not under the lock file name. */
240 /* Give up if cannot do that. */
241 desc
= open (tempname
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666);
244 char *message
= (char *) xmalloc (strlen (tempname
) + 50);
245 sprintf (message
, "%s--see source file lib-src/movemail.c",
247 pfatal_with_name (message
);
251 tem
= link (tempname
, lockname
);
257 /* If lock file is five minutes old, unlock it.
258 Five minutes should be good enough to cope with crashes
259 and wedgitude, and long enough to avoid being fooled
260 by time differences between machines. */
261 if (stat (lockname
, &st
) >= 0)
264 if (st
.st_ctime
< now
- 300)
269 delete_lockname
= lockname
;
270 #endif /* not MAIL_USE_SYSTEM_LOCK */
271 #endif /* not MAIL_USE_MMDF */
277 #ifndef MAIL_USE_MMDF
278 #ifdef MAIL_USE_SYSTEM_LOCK
279 indesc
= open (inname
, O_RDWR
);
280 #else /* if not MAIL_USE_SYSTEM_LOCK */
281 indesc
= open (inname
, O_RDONLY
);
282 #endif /* not MAIL_USE_SYSTEM_LOCK */
283 #else /* MAIL_USE_MMDF */
284 indesc
= lk_open (inname
, O_RDONLY
, 0, 0, 10);
285 #endif /* MAIL_USE_MMDF */
288 pfatal_with_name (inname
);
290 #if defined (BSD) || defined (XENIX)
291 /* In case movemail is setuid to root, make sure the user can
292 read the output file. */
293 /* This is desirable for all systems
294 but I don't want to assume all have the umask system call */
295 umask (umask (0) & 0333);
296 #endif /* BSD or Xenix */
297 outdesc
= open (outname
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666);
299 pfatal_with_name (outname
);
300 #ifdef MAIL_USE_SYSTEM_LOCK
301 #ifdef MAIL_USE_LOCKF
302 if (lockf (indesc
, F_LOCK
, 0) < 0) pfatal_with_name (inname
);
303 #else /* not MAIL_USE_LOCKF */
305 if (locking (indesc
, LK_RLCK
, 0L) < 0) pfatal_with_name (inname
);
307 if (flock (indesc
, LOCK_EX
) < 0) pfatal_with_name (inname
);
309 #endif /* not MAIL_USE_LOCKF */
310 #endif /* MAIL_USE_SYSTEM_LOCK */
317 nread
= read (indesc
, buf
, sizeof buf
);
318 if (nread
!= write (outdesc
, buf
, nread
))
320 int saved_errno
= errno
;
323 pfatal_with_name (outname
);
325 if (nread
< sizeof buf
)
331 if (fsync (outdesc
) < 0)
332 pfatal_and_delete (outname
);
335 /* Check to make sure no errors before we zap the inbox. */
336 if (close (outdesc
) != 0)
337 pfatal_and_delete (outname
);
339 #ifdef MAIL_USE_SYSTEM_LOCK
340 #if defined (STRIDE) || defined (XENIX)
341 /* Stride, xenix have file locking, but no ftruncate. This mess will do. */
342 close (open (inname
, O_CREAT
| O_TRUNC
| O_RDWR
, 0666));
344 ftruncate (indesc
, 0L);
345 #endif /* STRIDE or XENIX */
346 #endif /* MAIL_USE_SYSTEM_LOCK */
349 lk_close (indesc
, 0, 0, 0);
354 #ifndef MAIL_USE_SYSTEM_LOCK
355 /* Delete the input file; if we can't, at least get rid of its
357 #ifdef MAIL_UNLINK_SPOOL
358 /* This is generally bad to do, because it destroys the permissions
359 that were set on the file. Better to just empty the file. */
360 if (unlink (inname
) < 0 && errno
!= ENOENT
)
361 #endif /* MAIL_UNLINK_SPOOL */
362 creat (inname
, 0600);
363 #endif /* not MAIL_USE_SYSTEM_LOCK */
369 if (!WIFEXITED (status
))
371 else if (WRETCODE (status
) != 0)
372 exit (WRETCODE (status
));
374 #if !defined (MAIL_USE_MMDF) && !defined (MAIL_USE_SYSTEM_LOCK)
376 #endif /* not MAIL_USE_MMDF and not MAIL_USE_SYSTEM_LOCK */
380 /* Print error message and exit. */
387 unlink (delete_lockname
);
392 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
398 fprintf (stderr
, "movemail: ");
399 fprintf (stderr
, s1
, s2
, s3
);
400 fprintf (stderr
, "\n");
404 pfatal_with_name (name
)
407 char *s
= concat ("", strerror (errno
), " for %s");
412 pfatal_and_delete (name
)
415 char *s
= concat ("", strerror (errno
), " for %s");
420 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
426 int len1
= strlen (s1
), len2
= strlen (s2
), len3
= strlen (s3
);
427 char *result
= (char *) xmalloc (len1
+ len2
+ len3
+ 1);
430 strcpy (result
+ len1
, s2
);
431 strcpy (result
+ len1
+ len2
, s3
);
432 *(result
+ len1
+ len2
+ len3
) = 0;
437 /* Like malloc but get fatal error if memory is exhausted. */
443 long *result
= (long *) malloc (size
);
445 fatal ("virtual memory exhausted", 0);
449 /* This is the guts of the interface to the Post Office Protocol. */
453 #include <sys/socket.h>
454 #include <netinet/in.h>
461 /* Cancel substitutions made by config.h for Emacs. */
475 char ibuffer
[BUFSIZ
];
476 char obuffer
[BUFSIZ
];
479 popmail (user
, outfile
)
490 extern char *strerror ();
492 server
= pop_open (0, user
, 0, POP_NO_GETPASS
);
499 if (pop_stat (server
, &nmsgs
, &nbytes
))
511 mbfi
= open (outfile
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666);
515 error ("Error in open: %s, %s", strerror (errno
), outfile
);
518 fchown (mbfi
, getuid (), -1);
520 if ((mbf
= fdopen (mbfi
, "w")) == NULL
)
523 error ("Error in fdopen: %s", strerror (errno
));
529 for (i
= 1; i
<= nmsgs
; i
++)
531 mbx_delimit_begin (mbf
);
532 if (pop_retr (server
, i
, mbx_write
, mbf
) != OK
)
538 mbx_delimit_end (mbf
);
542 error ("Error in fflush: %s", strerror (errno
));
549 /* On AFS, a call to write only modifies the file in the local
550 * workstation's AFS cache. The changes are not written to the server
551 * until a call to fsync or close is made. Users with AFS home
552 * directories have lost mail when over quota because these checks were
553 * not made in previous versions of movemail. */
556 if (fsync (mbfi
) < 0)
558 error ("Error in fsync: %s", strerror (errno
));
563 if (close (mbfi
) == -1)
565 error ("Error in close: %s", strerror (errno
));
569 for (i
= 1; i
<= nmsgs
; i
++)
571 if (pop_delete (server
, i
))
579 if (pop_quit (server
))
588 pop_retr (server
, msgno
, action
, arg
)
592 extern char *strerror ();
596 if (pop_retrieve_first (server
, msgno
, &line
))
598 strncpy (Errmsg
, pop_error
, sizeof (Errmsg
));
599 Errmsg
[sizeof (Errmsg
)-1] = '\0';
603 while (! (ret
= pop_retrieve_next (server
, &line
)))
608 if ((*action
)(line
, arg
) != OK
)
610 strcpy (Errmsg
, strerror (errno
));
618 strncpy (Errmsg
, pop_error
, sizeof (Errmsg
));
619 Errmsg
[sizeof (Errmsg
)-1] = '\0';
626 /* Do this as a macro instead of using strcmp to save on execution time. */
627 #define IS_FROM_LINE(a) ((a[0] == 'F') \
634 mbx_write (line
, mbf
)
638 if (IS_FROM_LINE (line
))
640 if (fputc ('>', mbf
) == EOF
)
643 if (fputs (line
, mbf
) == EOF
)
645 if (fputc (0x0a, mbf
) == EOF
)
651 mbx_delimit_begin (mbf
)
654 if (fputs ("\f\n0, unseen,,\n", mbf
) == EOF
)
659 mbx_delimit_end (mbf
)
662 if (putc ('\037', mbf
) == EOF
)
667 #endif /* MAIL_USE_POP */
669 #ifndef HAVE_STRERROR
674 extern char *sys_errlist
[];
677 if (errnum
>= 0 && errnum
< sys_nerr
)
678 return sys_errlist
[errnum
];
679 return (char *) "Unknown error";
682 #endif /* ! HAVE_STRERROR */