(main): Fix up MAIL_USE_MMDF conditional where
[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, 675 Mass Ave, Cambridge, MA 02139, USA. */
20
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.
26
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. */
32
33 /*
34 * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
35 *
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.
41 *
42 * New module: popmail.c
43 * Modified routines:
44 * main - added code within #ifdef MAIL_USE_POP; added setuid (getuid ())
45 * after POP code.
46 * New routines in movemail.c:
47 * get_errmsg - return pointer to system error message
48 *
49 */
50
51 #include <sys/types.h>
52 #include <sys/stat.h>
53 #include <sys/file.h>
54 #include <errno.h>
55 #define NO_SHORTNAMES /* Tell config not to load remap.h */
56 #include <../src/config.h>
57 #include <../src/syswait.h>
58
59 #ifdef MSDOS
60 #undef access
61 #endif /* MSDOS */
62
63 #ifdef USG
64 #include <fcntl.h>
65 #include <unistd.h>
66 #ifndef F_OK
67 #define F_OK 0
68 #define X_OK 1
69 #define W_OK 2
70 #define R_OK 4
71 #endif
72 #endif /* USG */
73
74 #ifdef XENIX
75 #include <sys/locking.h>
76 #endif
77
78 #ifdef MAIL_USE_LOCKF
79 #define MAIL_USE_SYSTEM_LOCK
80 #endif
81
82 #ifdef MAIL_USE_FLOCK
83 #define MAIL_USE_SYSTEM_LOCK
84 #endif
85
86 #ifdef MAIL_USE_MMDF
87 extern int lk_open (), lk_close ();
88 #endif
89
90 /* Cancel substitutions made by config.h for Emacs. */
91 #undef open
92 #undef read
93 #undef write
94 #undef close
95
96 char *concat ();
97 char *xmalloc ();
98 #ifndef errno
99 extern int errno;
100 #endif
101
102 /* Nonzero means this is name of a lock file to delete on fatal error. */
103 char *delete_lockname;
104
105 main (argc, argv)
106 int argc;
107 char **argv;
108 {
109 char *inname, *outname;
110 int indesc, outdesc;
111 int nread;
112 WAITTYPE status;
113
114 #ifndef MAIL_USE_SYSTEM_LOCK
115 struct stat st;
116 long now;
117 int tem;
118 char *lockname, *p;
119 char *tempname;
120 int desc;
121 #endif /* not MAIL_USE_SYSTEM_LOCK */
122
123 delete_lockname = 0;
124
125 if (argc < 3)
126 fatal ("two arguments required");
127
128 inname = argv[1];
129 outname = argv[2];
130
131 #ifdef MAIL_USE_MMDF
132 mmdf_init (argv[0]);
133 #endif
134
135 /* Check access to output file. */
136 if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
137 pfatal_with_name (outname);
138
139 /* Also check that outname's directory is writeable to the real uid. */
140 {
141 char *buf = (char *) xmalloc (strlen (outname) + 1);
142 char *p, q;
143 strcpy (buf, outname);
144 p = buf + strlen (buf);
145 while (p > buf && p[-1] != '/')
146 *--p = 0;
147 if (p == buf)
148 *p++ = '.';
149 if (access (buf, W_OK) != 0)
150 pfatal_with_name (buf);
151 free (buf);
152 }
153
154 #ifdef MAIL_USE_POP
155 if (!strncmp (inname, "po:", 3))
156 {
157 int status; char *user;
158
159 for (user = &inname[strlen (inname) - 1]; user >= inname; user--)
160 if (*user == ':')
161 break;
162
163 status = popmail (user, outname);
164 exit (status);
165 }
166
167 setuid (getuid ());
168 #endif /* MAIL_USE_POP */
169
170 /* Check access to input file. */
171 if (access (inname, R_OK | W_OK) != 0)
172 pfatal_with_name (inname);
173
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.
180
181 On systems that use a lock file, extracting the mail without locking
182 WILL occasionally cause loss of mail due to timing errors!
183
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.
189
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.
192
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. */
198
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] != '/')
204 p--;
205 *p = 0;
206 strcpy (p, "EXXXXXX");
207 mktemp (tempname);
208 unlink (tempname);
209
210 while (1)
211 {
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);
215 if (desc < 0)
216 pfatal_with_name ("lock file--see source file lib-src/movemail.c");
217 close (desc);
218
219 tem = link (tempname, lockname);
220 unlink (tempname);
221 if (tem >= 0)
222 break;
223 sleep (1);
224
225 /* If lock file is a minute old, unlock it. */
226 if (stat (lockname, &st) >= 0)
227 {
228 now = time (0);
229 if (st.st_ctime < now - 60)
230 unlink (lockname);
231 }
232 }
233
234 delete_lockname = lockname;
235 #endif /* not MAIL_USE_SYSTEM_LOCK */
236 #endif /* not MAIL_USE_MMDF */
237
238 if (fork () == 0)
239 {
240 seteuid (getuid ());
241
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 */
251
252 if (indesc < 0)
253 pfatal_with_name (inname);
254
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);
263 if (outdesc < 0)
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 */
269 #ifdef XENIX
270 if (locking (indesc, LK_RLCK, 0L) < 0) pfatal_with_name (inname);
271 #else
272 if (flock (indesc, LOCK_EX) < 0) pfatal_with_name (inname);
273 #endif
274 #endif /* not MAIL_USE_LOCKF */
275 #endif /* MAIL_USE_SYSTEM_LOCK */
276
277 {
278 char buf[1024];
279
280 while (1)
281 {
282 nread = read (indesc, buf, sizeof buf);
283 if (nread != write (outdesc, buf, nread))
284 {
285 int saved_errno = errno;
286 unlink (outname);
287 errno = saved_errno;
288 pfatal_with_name (outname);
289 }
290 if (nread < sizeof buf)
291 break;
292 }
293 }
294
295 #ifdef BSD
296 if (fsync (outdesc) < 0)
297 pfatal_and_delete (outname);
298 #endif
299
300 /* Check to make sure no errors before we zap the inbox. */
301 if (close (outdesc) != 0)
302 pfatal_and_delete (outname);
303
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));
308 #else
309 ftruncate (indesc, 0L);
310 #endif /* STRIDE or XENIX */
311 #endif /* MAIL_USE_SYSTEM_LOCK */
312
313 #ifdef MAIL_USE_MMDF
314 lk_close (indesc, 0, 0, 0);
315 #else
316 close (indesc);
317 #endif
318
319 #ifndef MAIL_USE_SYSTEM_LOCK
320 /* Delete the input file; if we can't, at least get rid of its
321 contents. */
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 */
329
330 exit (0);
331 }
332
333 wait (&status);
334 if (!WIFEXITED (status))
335 exit (1);
336 else if (WRETCODE (status) != 0)
337 exit (WRETCODE (status));
338
339 #if !defined (MAIL_USE_MMDF) && !defined (MAIL_USE_SYSTEM_LOCK)
340 unlink (lockname);
341 #endif /* not MAIL_USE_MMDF and not MAIL_USE_SYSTEM_LOCK */
342 exit (0);
343 }
344 \f
345 /* Print error message and exit. */
346
347 fatal (s1, s2)
348 char *s1, *s2;
349 {
350 if (delete_lockname)
351 unlink (delete_lockname);
352 error (s1, s2);
353 exit (1);
354 }
355
356 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
357
358 error (s1, s2, s3)
359 char *s1, *s2, *s3;
360 {
361 printf ("movemail: ");
362 printf (s1, s2, s3);
363 printf ("\n");
364 }
365
366 pfatal_with_name (name)
367 char *name;
368 {
369 extern int errno;
370 extern char *strerror ();
371 char *s;
372
373 s = concat ("", strerror (errno), " for %s");
374 fatal (s, name);
375 }
376
377 pfatal_and_delete (name)
378 char *name;
379 {
380 extern int errno;
381 extern char *strerror ();
382 char *s;
383
384 s = concat ("", strerror (errno), " for %s");
385 unlink (name);
386 fatal (s, name);
387 }
388
389 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
390
391 char *
392 concat (s1, s2, s3)
393 char *s1, *s2, *s3;
394 {
395 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
396 char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
397
398 strcpy (result, s1);
399 strcpy (result + len1, s2);
400 strcpy (result + len1 + len2, s3);
401 *(result + len1 + len2 + len3) = 0;
402
403 return result;
404 }
405
406 /* Like malloc but get fatal error if memory is exhausted. */
407
408 char *
409 xmalloc (size)
410 unsigned size;
411 {
412 char *result = (char *) malloc (size);
413 if (!result)
414 fatal ("virtual memory exhausted", 0);
415 return result;
416 }
417 \f
418 /* This is the guts of the interface to the Post Office Protocol. */
419
420 #ifdef MAIL_USE_POP
421
422 #include <sys/socket.h>
423 #include <netinet/in.h>
424 #include <netdb.h>
425 #include <stdio.h>
426 #include <pwd.h>
427
428 #ifdef USG
429 #include <fcntl.h>
430 /* Cancel substitutions made by config.h for Emacs. */
431 #undef open
432 #undef read
433 #undef write
434 #undef close
435 #endif /* USG */
436
437 #define NOTOK (-1)
438 #define OK 0
439 #define DONE 1
440
441 char *progname;
442 FILE *sfi;
443 FILE *sfo;
444 char Errmsg[80];
445
446 static int debug = 0;
447
448 char *get_errmsg ();
449 char *getenv ();
450 int mbx_write ();
451
452 popmail (user, outfile)
453 char *user;
454 char *outfile;
455 {
456 char *host;
457 int nmsgs, nbytes;
458 char response[128];
459 register int i;
460 int mbfi;
461 FILE *mbf;
462 struct passwd *pw = (struct passwd *) getpwuid (getuid ());
463 if (pw == NULL)
464 fatal ("cannot determine user name");
465
466 host = getenv ("MAILHOST");
467 if (host == NULL)
468 {
469 fatal ("no MAILHOST defined");
470 }
471
472 if (pop_init (host) == NOTOK)
473 {
474 fatal (Errmsg);
475 }
476
477 if (getline (response, sizeof response, sfi) != OK)
478 {
479 fatal (response);
480 }
481
482 if (pop_command ("USER %s", user) == NOTOK
483 || pop_command ("RPOP %s", pw->pw_name) == NOTOK)
484 {
485 pop_command ("QUIT");
486 fatal (Errmsg);
487 }
488
489 if (pop_stat (&nmsgs, &nbytes) == NOTOK)
490 {
491 pop_command ("QUIT");
492 fatal (Errmsg);
493 }
494
495 if (!nmsgs)
496 {
497 pop_command ("QUIT");
498 return 0;
499 }
500
501 mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
502 if (mbfi < 0)
503 {
504 pop_command ("QUIT");
505 pfatal_and_delete (outfile);
506 }
507 fchown (mbfi, getuid (), -1);
508
509 if ((mbf = fdopen (mbfi, "w")) == NULL)
510 {
511 pop_command ("QUIT");
512 pfatal_and_delete (outfile);
513 }
514
515 for (i = 1; i <= nmsgs; i++)
516 {
517 mbx_delimit_begin (mbf);
518 if (pop_retr (i, mbx_write, mbf) != OK)
519 {
520 pop_command ("QUIT");
521 close (mbfi);
522 unlink (outfile);
523 fatal (Errmsg);
524 }
525 mbx_delimit_end (mbf);
526 fflush (mbf);
527 }
528
529 if (fsync (mbfi) < 0)
530 {
531 pop_command ("QUIT");
532 pfatal_and_delete (outfile);
533 }
534
535 if (close (mbfi) == -1)
536 {
537 pop_command ("QUIT");
538 pfatal_and_delete (outfile);
539 }
540
541 for (i = 1; i <= nmsgs; i++)
542 {
543 if (pop_command ("DELE %d", i) == NOTOK)
544 {
545 /* Better to ignore this failure. */
546 }
547 }
548
549 pop_command ("QUIT");
550 return (0);
551 }
552
553 pop_init (host)
554 char *host;
555 {
556 register struct hostent *hp;
557 register struct servent *sp;
558 int lport = IPPORT_RESERVED - 1;
559 struct sockaddr_in sin;
560 register int s;
561
562 hp = gethostbyname (host);
563 if (hp == NULL)
564 {
565 sprintf (Errmsg, "MAILHOST unknown: %s", host);
566 return NOTOK;
567 }
568
569 sp = getservbyname ("pop", "tcp");
570 if (sp == 0)
571 {
572 strcpy (Errmsg, "tcp/pop: unknown service");
573 return NOTOK;
574 }
575
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);
580 if (s < 0)
581 {
582 sprintf (Errmsg, "error creating socket: %s", get_errmsg ());
583 return NOTOK;
584 }
585
586 if (connect (s, (char *)&sin, sizeof sin) < 0)
587 {
588 sprintf (Errmsg, "error during connect: %s", get_errmsg ());
589 close (s);
590 return NOTOK;
591 }
592
593 sfi = fdopen (s, "r");
594 sfo = fdopen (s, "w");
595 if (sfi == NULL || sfo == NULL)
596 {
597 sprintf (Errmsg, "error in fdopen: %s", get_errmsg ());
598 close (s);
599 return NOTOK;
600 }
601
602 return OK;
603 }
604
605 pop_command (fmt, a, b, c, d)
606 char *fmt;
607 {
608 char buf[128];
609 char errmsg[64];
610
611 sprintf (buf, fmt, a, b, c, d);
612
613 if (debug) fprintf (stderr, "---> %s\n", buf);
614 if (putline (buf, Errmsg, sfo) == NOTOK) return NOTOK;
615
616 if (getline (buf, sizeof buf, sfi) != OK)
617 {
618 strcpy (Errmsg, buf);
619 return NOTOK;
620 }
621
622 if (debug)
623 fprintf (stderr, "<--- %s\n", buf);
624 if (*buf != '+')
625 {
626 strcpy (Errmsg, buf);
627 return NOTOK;
628 }
629 else
630 {
631 return OK;
632 }
633 }
634
635
636 pop_stat (nmsgs, nbytes)
637 int *nmsgs, *nbytes;
638 {
639 char buf[128];
640
641 if (debug)
642 fprintf (stderr, "---> STAT\n");
643 if (putline ("STAT", Errmsg, sfo) == NOTOK)
644 return NOTOK;
645
646 if (getline (buf, sizeof buf, sfi) != OK)
647 {
648 strcpy (Errmsg, buf);
649 return NOTOK;
650 }
651
652 if (debug) fprintf (stderr, "<--- %s\n", buf);
653 if (*buf != '+')
654 {
655 strcpy (Errmsg, buf);
656 return NOTOK;
657 }
658 else
659 {
660 sscanf (buf, "+OK %d %d", nmsgs, nbytes);
661 return OK;
662 }
663 }
664
665 pop_retr (msgno, action, arg)
666 int (*action)();
667 {
668 char buf[128];
669
670 sprintf (buf, "RETR %d", msgno);
671 if (debug) fprintf (stderr, "%s\n", buf);
672 if (putline (buf, Errmsg, sfo) == NOTOK) return NOTOK;
673
674 if (getline (buf, sizeof buf, sfi) != OK)
675 {
676 strcpy (Errmsg, buf);
677 return NOTOK;
678 }
679
680 while (1)
681 {
682 switch (multiline (buf, sizeof buf, sfi))
683 {
684 case OK:
685 (*action)(buf, arg);
686 break;
687 case DONE:
688 return OK;
689 case NOTOK:
690 strcpy (Errmsg, buf);
691 return NOTOK;
692 }
693 }
694 }
695
696 getline (buf, n, f)
697 char *buf;
698 register int n;
699 FILE *f;
700 {
701 register char *p;
702 int c;
703
704 p = buf;
705 while (--n > 0 && (c = fgetc (f)) != EOF)
706 if ((*p++ = c) == '\n') break;
707
708 if (ferror (f))
709 {
710 strcpy (buf, "error on connection");
711 return NOTOK;
712 }
713
714 if (c == EOF && p == buf)
715 {
716 strcpy (buf, "connection closed by foreign host");
717 return DONE;
718 }
719
720 *p = NULL;
721 if (*--p == '\n') *p = NULL;
722 if (*--p == '\r') *p = NULL;
723 return OK;
724 }
725
726 multiline (buf, n, f)
727 char *buf;
728 register int n;
729 FILE *f;
730 {
731 if (getline (buf, n, f) != OK)
732 return NOTOK;
733 if (*buf == '.')
734 {
735 if (*(buf+1) == NULL)
736 return DONE;
737 else
738 strcpy (buf, buf+1);
739 }
740 return OK;
741 }
742
743 char *
744 get_errmsg ()
745 {
746 extern int errno;
747 extern char *strerror ();
748 return strerror (errno);
749 }
750
751 putline (buf, err, f)
752 char *buf;
753 char *err;
754 FILE *f;
755 {
756 fprintf (f, "%s\r\n", buf);
757 fflush (f);
758 if (ferror (f))
759 {
760 strcpy (err, "lost connection");
761 return NOTOK;
762 }
763 return OK;
764 }
765
766 mbx_write (line, mbf)
767 char *line;
768 FILE *mbf;
769 {
770 fputs (line, mbf);
771 fputc (0x0a, mbf);
772 }
773
774 mbx_delimit_begin (mbf)
775 FILE *mbf;
776 {
777 fputs ("\f\n0, unseen,,\n", mbf);
778 }
779
780 mbx_delimit_end (mbf)
781 FILE *mbf;
782 {
783 putc ('\037', mbf);
784 }
785
786 #endif /* MAIL_USE_POP */
787 \f
788 #ifndef HAVE_STRERROR
789 char *
790 strerror (errnum)
791 int errnum;
792 {
793 extern char *sys_errlist[];
794 extern int sys_nerr;
795
796 if (errnum >= 0 && errnum < sys_nerr)
797 return sys_errlist[errnum];
798 return (char *) "Unknown error";
799 }
800
801 #endif /* ! HAVE_STRERROR */