Update FSF's address in the preamble.
[bpt/emacs.git] / lib-src / movemail.c
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.
4
5 This file is part of GNU Emacs.
6
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)
10 any later version.
11
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.
16
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, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
21
22 /* Important notice: defining MAIL_USE_FLOCK or MAIL_USE_LOCKF *will
23 cause loss of mail* if you do it on a system that does not normally
24 use flock as its way of interlocking access to inbox files. The
25 setting of MAIL_USE_FLOCK and MAIL_USE_LOCKF *must agree* with the
26 system's own conventions. It is not a choice that is up to you.
27
28 So, if your system uses lock files rather than flock, then the only way
29 you can get proper operation is to enable movemail to write lockfiles there.
30 This means you must either give that directory access modes
31 that permit everyone to write lockfiles in it, or you must make movemail
32 a setuid or setgid program. */
33
34 /*
35 * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
36 *
37 * Added POP (Post Office Protocol) service. When compiled -DPOP
38 * movemail will accept input filename arguments of the form
39 * "po:username". This will cause movemail to open a connection to
40 * a pop server running on $MAILHOST (environment variable). Movemail
41 * must be setuid to root in order to work with POP.
42 *
43 * New module: popmail.c
44 * Modified routines:
45 * main - added code within #ifdef MAIL_USE_POP; added setuid (getuid ())
46 * after POP code.
47 * New routines in movemail.c:
48 * get_errmsg - return pointer to system error message
49 *
50 * Modified August, 1993 by Jonathan Kamens (OpenVision Technologies)
51 *
52 * Move all of the POP code into a separate file, "pop.c".
53 * Use strerror instead of get_errmsg.
54 *
55 */
56
57 #define NO_SHORTNAMES /* Tell config not to load remap.h */
58 #include <../src/config.h>
59 #include <sys/types.h>
60 #include <sys/stat.h>
61 #include <sys/file.h>
62 #include <stdio.h>
63 #include <errno.h>
64 #include <../src/syswait.h>
65 #ifdef MAIL_USE_POP
66 #include "pop.h"
67 #endif
68
69 #ifdef MSDOS
70 #undef access
71 #endif /* MSDOS */
72
73 #ifdef USG
74 #include <fcntl.h>
75 #include <unistd.h>
76 #ifndef F_OK
77 #define F_OK 0
78 #define X_OK 1
79 #define W_OK 2
80 #define R_OK 4
81 #endif
82 #endif /* USG */
83
84 #ifdef HAVE_UNISTD_H
85 #include <unistd.h>
86 #endif
87
88 #ifdef XENIX
89 #include <sys/locking.h>
90 #endif
91
92 #ifdef MAIL_USE_LOCKF
93 #define MAIL_USE_SYSTEM_LOCK
94 #endif
95
96 #ifdef MAIL_USE_FLOCK
97 #define MAIL_USE_SYSTEM_LOCK
98 #endif
99
100 #ifdef MAIL_USE_MMDF
101 extern int lk_open (), lk_close ();
102 #endif
103
104 /* Cancel substitutions made by config.h for Emacs. */
105 #undef open
106 #undef read
107 #undef write
108 #undef close
109
110 #ifndef errno
111 extern int errno;
112 #endif
113 char *strerror ();
114
115 void fatal ();
116 void error ();
117 void pfatal_with_name ();
118 void pfatal_and_delete ();
119 char *concat ();
120 long *xmalloc ();
121 int popmail ();
122 int pop_retr ();
123 int mbx_write ();
124 int mbx_delimit_begin ();
125 int mbx_delimit_end ();
126
127 /* Nonzero means this is name of a lock file to delete on fatal error. */
128 char *delete_lockname;
129
130 int
131 main (argc, argv)
132 int argc;
133 char **argv;
134 {
135 char *inname, *outname;
136 int indesc, outdesc;
137 int nread;
138 WAITTYPE status;
139
140 #ifndef MAIL_USE_SYSTEM_LOCK
141 struct stat st;
142 long now;
143 int tem;
144 char *lockname, *p;
145 char *tempname;
146 int desc;
147 #endif /* not MAIL_USE_SYSTEM_LOCK */
148
149 delete_lockname = 0;
150
151 if (argc < 3)
152 {
153 fprintf (stderr, "Usage: movemail inbox destfile\n");
154 exit(1);
155 }
156
157 inname = argv[1];
158 outname = argv[2];
159
160 #ifdef MAIL_USE_MMDF
161 mmdf_init (argv[0]);
162 #endif
163
164 if (*outname == 0)
165 fatal ("Destination file name is empty", 0);
166
167 /* Check access to output file. */
168 if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
169 pfatal_with_name (outname);
170
171 /* Also check that outname's directory is writable to the real uid. */
172 {
173 char *buf = (char *) xmalloc (strlen (outname) + 1);
174 char *p;
175 strcpy (buf, outname);
176 p = buf + strlen (buf);
177 while (p > buf && p[-1] != '/')
178 *--p = 0;
179 if (p == buf)
180 *p++ = '.';
181 if (access (buf, W_OK) != 0)
182 pfatal_with_name (buf);
183 free (buf);
184 }
185
186 #ifdef MAIL_USE_POP
187 if (!strncmp (inname, "po:", 3))
188 {
189 int status;
190
191 status = popmail (inname + 3, outname);
192 exit (status);
193 }
194
195 setuid (getuid ());
196 #endif /* MAIL_USE_POP */
197
198 /* Check access to input file. */
199 if (access (inname, R_OK | W_OK) != 0)
200 pfatal_with_name (inname);
201
202 #ifndef MAIL_USE_MMDF
203 #ifndef MAIL_USE_SYSTEM_LOCK
204 /* Use a lock file named after our first argument with .lock appended:
205 If it exists, the mail file is locked. */
206 /* Note: this locking mechanism is *required* by the mailer
207 (on systems which use it) to prevent loss of mail.
208
209 On systems that use a lock file, extracting the mail without locking
210 WILL occasionally cause loss of mail due to timing errors!
211
212 So, if creation of the lock file fails
213 due to access permission on the mail spool directory,
214 you simply MUST change the permission
215 and/or make movemail a setgid program
216 so it can create lock files properly.
217
218 You might also wish to verify that your system is one
219 which uses lock files for this purpose. Some systems use other methods.
220
221 If your system uses the `flock' system call for mail locking,
222 define MAIL_USE_SYSTEM_LOCK in config.h or the s-*.h file
223 and recompile movemail. If the s- file for your system
224 should define MAIL_USE_SYSTEM_LOCK but does not, send a bug report
225 to bug-gnu-emacs@prep.ai.mit.edu so we can fix it. */
226
227 lockname = concat (inname, ".lock", "");
228 tempname = (char *) xmalloc (strlen (inname) + strlen ("EXXXXXX") + 1);
229 strcpy (tempname, inname);
230 p = tempname + strlen (tempname);
231 while (p != tempname && p[-1] != '/')
232 p--;
233 *p = 0;
234 strcpy (p, "EXXXXXX");
235 mktemp (tempname);
236 unlink (tempname);
237
238 while (1)
239 {
240 /* Create the lock file, but not under the lock file name. */
241 /* Give up if cannot do that. */
242 desc = open (tempname, O_WRONLY | O_CREAT | O_EXCL, 0666);
243 if (desc < 0)
244 {
245 char *message = (char *) xmalloc (strlen (tempname) + 50);
246 sprintf (message, "%s--see source file lib-src/movemail.c",
247 tempname);
248 pfatal_with_name (message);
249 }
250 close (desc);
251
252 tem = link (tempname, lockname);
253 unlink (tempname);
254 if (tem >= 0)
255 break;
256 sleep (1);
257
258 /* If lock file is five minutes old, unlock it.
259 Five minutes should be good enough to cope with crashes
260 and wedgitude, and long enough to avoid being fooled
261 by time differences between machines. */
262 if (stat (lockname, &st) >= 0)
263 {
264 now = time (0);
265 if (st.st_ctime < now - 300)
266 unlink (lockname);
267 }
268 }
269
270 delete_lockname = lockname;
271 #endif /* not MAIL_USE_SYSTEM_LOCK */
272 #endif /* not MAIL_USE_MMDF */
273
274 if (fork () == 0)
275 {
276 setuid (getuid ());
277
278 #ifndef MAIL_USE_MMDF
279 #ifdef MAIL_USE_SYSTEM_LOCK
280 indesc = open (inname, O_RDWR);
281 #else /* if not MAIL_USE_SYSTEM_LOCK */
282 indesc = open (inname, O_RDONLY);
283 #endif /* not MAIL_USE_SYSTEM_LOCK */
284 #else /* MAIL_USE_MMDF */
285 indesc = lk_open (inname, O_RDONLY, 0, 0, 10);
286 #endif /* MAIL_USE_MMDF */
287
288 if (indesc < 0)
289 pfatal_with_name (inname);
290
291 #if defined (BSD) || defined (XENIX)
292 /* In case movemail is setuid to root, make sure the user can
293 read the output file. */
294 /* This is desirable for all systems
295 but I don't want to assume all have the umask system call */
296 umask (umask (0) & 0333);
297 #endif /* BSD or Xenix */
298 outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
299 if (outdesc < 0)
300 pfatal_with_name (outname);
301 #ifdef MAIL_USE_SYSTEM_LOCK
302 #ifdef MAIL_USE_LOCKF
303 if (lockf (indesc, F_LOCK, 0) < 0) pfatal_with_name (inname);
304 #else /* not MAIL_USE_LOCKF */
305 #ifdef XENIX
306 if (locking (indesc, LK_RLCK, 0L) < 0) pfatal_with_name (inname);
307 #else
308 if (flock (indesc, LOCK_EX) < 0) pfatal_with_name (inname);
309 #endif
310 #endif /* not MAIL_USE_LOCKF */
311 #endif /* MAIL_USE_SYSTEM_LOCK */
312
313 {
314 char buf[1024];
315
316 while (1)
317 {
318 nread = read (indesc, buf, sizeof buf);
319 if (nread != write (outdesc, buf, nread))
320 {
321 int saved_errno = errno;
322 unlink (outname);
323 errno = saved_errno;
324 pfatal_with_name (outname);
325 }
326 if (nread < sizeof buf)
327 break;
328 }
329 }
330
331 #ifdef BSD
332 if (fsync (outdesc) < 0)
333 pfatal_and_delete (outname);
334 #endif
335
336 /* Check to make sure no errors before we zap the inbox. */
337 if (close (outdesc) != 0)
338 pfatal_and_delete (outname);
339
340 #ifdef MAIL_USE_SYSTEM_LOCK
341 #if defined (STRIDE) || defined (XENIX)
342 /* Stride, xenix have file locking, but no ftruncate. This mess will do. */
343 close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
344 #else
345 ftruncate (indesc, 0L);
346 #endif /* STRIDE or XENIX */
347 #endif /* MAIL_USE_SYSTEM_LOCK */
348
349 #ifdef MAIL_USE_MMDF
350 lk_close (indesc, 0, 0, 0);
351 #else
352 close (indesc);
353 #endif
354
355 #ifndef MAIL_USE_SYSTEM_LOCK
356 /* Delete the input file; if we can't, at least get rid of its
357 contents. */
358 #ifdef MAIL_UNLINK_SPOOL
359 /* This is generally bad to do, because it destroys the permissions
360 that were set on the file. Better to just empty the file. */
361 if (unlink (inname) < 0 && errno != ENOENT)
362 #endif /* MAIL_UNLINK_SPOOL */
363 creat (inname, 0600);
364 #endif /* not MAIL_USE_SYSTEM_LOCK */
365
366 exit (0);
367 }
368
369 wait (&status);
370 if (!WIFEXITED (status))
371 exit (1);
372 else if (WRETCODE (status) != 0)
373 exit (WRETCODE (status));
374
375 #if !defined (MAIL_USE_MMDF) && !defined (MAIL_USE_SYSTEM_LOCK)
376 unlink (lockname);
377 #endif /* not MAIL_USE_MMDF and not MAIL_USE_SYSTEM_LOCK */
378 return 0;
379 }
380 \f
381 /* Print error message and exit. */
382
383 void
384 fatal (s1, s2)
385 char *s1, *s2;
386 {
387 if (delete_lockname)
388 unlink (delete_lockname);
389 error (s1, s2);
390 exit (1);
391 }
392
393 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
394
395 void
396 error (s1, s2, s3)
397 char *s1, *s2, *s3;
398 {
399 fprintf (stderr, "movemail: ");
400 fprintf (stderr, s1, s2, s3);
401 fprintf (stderr, "\n");
402 }
403
404 void
405 pfatal_with_name (name)
406 char *name;
407 {
408 char *s = concat ("", strerror (errno), " for %s");
409 fatal (s, name);
410 }
411
412 void
413 pfatal_and_delete (name)
414 char *name;
415 {
416 char *s = concat ("", strerror (errno), " for %s");
417 unlink (name);
418 fatal (s, name);
419 }
420
421 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
422
423 char *
424 concat (s1, s2, s3)
425 char *s1, *s2, *s3;
426 {
427 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
428 char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
429
430 strcpy (result, s1);
431 strcpy (result + len1, s2);
432 strcpy (result + len1 + len2, s3);
433 *(result + len1 + len2 + len3) = 0;
434
435 return result;
436 }
437
438 /* Like malloc but get fatal error if memory is exhausted. */
439
440 long *
441 xmalloc (size)
442 unsigned size;
443 {
444 long *result = (long *) malloc (size);
445 if (!result)
446 fatal ("virtual memory exhausted", 0);
447 return result;
448 }
449 \f
450 /* This is the guts of the interface to the Post Office Protocol. */
451
452 #ifdef MAIL_USE_POP
453
454 #include <sys/socket.h>
455 #include <netinet/in.h>
456 #include <netdb.h>
457 #include <stdio.h>
458 #include <pwd.h>
459
460 #ifdef USG
461 #include <fcntl.h>
462 /* Cancel substitutions made by config.h for Emacs. */
463 #undef open
464 #undef read
465 #undef write
466 #undef close
467 #endif /* USG */
468
469 #define NOTOK (-1)
470 #define OK 0
471 #define DONE 1
472
473 char *progname;
474 FILE *sfi;
475 FILE *sfo;
476 char ibuffer[BUFSIZ];
477 char obuffer[BUFSIZ];
478 char Errmsg[80];
479
480 popmail (user, outfile)
481 char *user;
482 char *outfile;
483 {
484 int nmsgs, nbytes;
485 register int i;
486 int mbfi;
487 FILE *mbf;
488 char *getenv ();
489 int mbx_write ();
490 popserver server;
491 extern char *strerror ();
492
493 server = pop_open (0, user, 0, POP_NO_GETPASS);
494 if (! server)
495 {
496 error (pop_error);
497 return (1);
498 }
499
500 if (pop_stat (server, &nmsgs, &nbytes))
501 {
502 error (pop_error);
503 return (1);
504 }
505
506 if (!nmsgs)
507 {
508 pop_close (server);
509 return (0);
510 }
511
512 mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
513 if (mbfi < 0)
514 {
515 pop_close (server);
516 error ("Error in open: %s, %s", strerror (errno), outfile);
517 return (1);
518 }
519 fchown (mbfi, getuid (), -1);
520
521 if ((mbf = fdopen (mbfi, "w")) == NULL)
522 {
523 pop_close (server);
524 error ("Error in fdopen: %s", strerror (errno));
525 close (mbfi);
526 unlink (outfile);
527 return (1);
528 }
529
530 for (i = 1; i <= nmsgs; i++)
531 {
532 mbx_delimit_begin (mbf);
533 if (pop_retr (server, i, mbx_write, mbf) != OK)
534 {
535 error (Errmsg);
536 close (mbfi);
537 return (1);
538 }
539 mbx_delimit_end (mbf);
540 fflush (mbf);
541 if (ferror (mbf))
542 {
543 error ("Error in fflush: %s", strerror (errno));
544 pop_close (server);
545 close (mbfi);
546 return (1);
547 }
548 }
549
550 /* On AFS, a call to write only modifies the file in the local
551 * workstation's AFS cache. The changes are not written to the server
552 * until a call to fsync or close is made. Users with AFS home
553 * directories have lost mail when over quota because these checks were
554 * not made in previous versions of movemail. */
555
556 #ifdef BSD
557 if (fsync (mbfi) < 0)
558 {
559 error ("Error in fsync: %s", strerror (errno));
560 return (1);
561 }
562 #endif
563
564 if (close (mbfi) == -1)
565 {
566 error ("Error in close: %s", strerror (errno));
567 return (1);
568 }
569
570 for (i = 1; i <= nmsgs; i++)
571 {
572 if (pop_delete (server, i))
573 {
574 error (pop_error);
575 pop_close (server);
576 return (1);
577 }
578 }
579
580 if (pop_quit (server))
581 {
582 error (pop_error);
583 return (1);
584 }
585
586 return (0);
587 }
588
589 pop_retr (server, msgno, action, arg)
590 popserver server;
591 int (*action)();
592 {
593 extern char *strerror ();
594 char *line;
595 int ret;
596
597 if (pop_retrieve_first (server, msgno, &line))
598 {
599 strncpy (Errmsg, pop_error, sizeof (Errmsg));
600 Errmsg[sizeof (Errmsg)-1] = '\0';
601 return (NOTOK);
602 }
603
604 while (! (ret = pop_retrieve_next (server, &line)))
605 {
606 if (! line)
607 break;
608
609 if ((*action)(line, arg) != OK)
610 {
611 strcpy (Errmsg, strerror (errno));
612 pop_close (server);
613 return (NOTOK);
614 }
615 }
616
617 if (ret)
618 {
619 strncpy (Errmsg, pop_error, sizeof (Errmsg));
620 Errmsg[sizeof (Errmsg)-1] = '\0';
621 return (NOTOK);
622 }
623
624 return (OK);
625 }
626
627 /* Do this as a macro instead of using strcmp to save on execution time. */
628 #define IS_FROM_LINE(a) ((a[0] == 'F') \
629 && (a[1] == 'r') \
630 && (a[2] == 'o') \
631 && (a[3] == 'm') \
632 && (a[4] == ' '))
633
634 int
635 mbx_write (line, mbf)
636 char *line;
637 FILE *mbf;
638 {
639 if (IS_FROM_LINE (line))
640 {
641 if (fputc ('>', mbf) == EOF)
642 return (NOTOK);
643 }
644 if (fputs (line, mbf) == EOF)
645 return (NOTOK);
646 if (fputc (0x0a, mbf) == EOF)
647 return (NOTOK);
648 return (OK);
649 }
650
651 int
652 mbx_delimit_begin (mbf)
653 FILE *mbf;
654 {
655 if (fputs ("\f\n0, unseen,,\n", mbf) == EOF)
656 return (NOTOK);
657 return (OK);
658 }
659
660 mbx_delimit_end (mbf)
661 FILE *mbf;
662 {
663 if (putc ('\037', mbf) == EOF)
664 return (NOTOK);
665 return (OK);
666 }
667
668 #endif /* MAIL_USE_POP */
669 \f
670 #ifndef HAVE_STRERROR
671 char *
672 strerror (errnum)
673 int errnum;
674 {
675 extern char *sys_errlist[];
676 extern int sys_nerr;
677
678 if (errnum >= 0 && errnum < sys_nerr)
679 return sys_errlist[errnum];
680 return (char *) "Unknown error";
681 }
682
683 #endif /* ! HAVE_STRERROR */