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