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