(malloc): Don't declare it.
[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 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 1, 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 contents. */
307 #ifdef MAIL_UNLINK_SPOOL
308 /* This is generally bad to do, because it destroys the permissions
309 that were set on the file. Better to just empty the file. */
310 if (unlink (inname) < 0 && errno != ENOENT)
311 #endif /* MAIL_UNLINK_SPOOL */
312 creat (inname, 0600);
313
314 exit (0);
315 }
316
317 wait (&status);
318 if (!WIFEXITED (status))
319 exit (1);
320 else if (WRETCODE (status) != 0)
321 exit (WRETCODE (status));
322
323 #ifndef MAIL_USE_MMDF
324 unlink (lockname);
325 #endif /* not MAIL_USE_MMDF */
326 #endif /* not MAIL_USE_FLOCK */
327 exit (0);
328 }
329 \f
330 /* Print error message and exit. */
331
332 fatal (s1, s2)
333 char *s1, *s2;
334 {
335 if (delete_lockname)
336 unlink (delete_lockname);
337 error (s1, s2);
338 exit (1);
339 }
340
341 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
342
343 error (s1, s2, s3)
344 char *s1, *s2, *s3;
345 {
346 printf ("movemail: ");
347 printf (s1, s2, s3);
348 printf ("\n");
349 }
350
351 pfatal_with_name (name)
352 char *name;
353 {
354 extern int errno, sys_nerr;
355 extern char *sys_errlist[];
356 char *s;
357
358 if (errno < sys_nerr)
359 s = concat ("", sys_errlist[errno], " for %s");
360 else
361 s = "cannot open %s";
362 fatal (s, name);
363 }
364
365 pfatal_and_delete (name)
366 char *name;
367 {
368 extern int errno, sys_nerr;
369 extern char *sys_errlist[];
370 char *s;
371
372 if (errno < sys_nerr)
373 s = concat ("", sys_errlist[errno], " for %s");
374 else
375 s = "cannot open %s";
376
377 unlink (name);
378 fatal (s, name);
379 }
380
381 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
382
383 char *
384 concat (s1, s2, s3)
385 char *s1, *s2, *s3;
386 {
387 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
388 char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
389
390 strcpy (result, s1);
391 strcpy (result + len1, s2);
392 strcpy (result + len1 + len2, s3);
393 *(result + len1 + len2 + len3) = 0;
394
395 return result;
396 }
397
398 /* Like malloc but get fatal error if memory is exhausted. */
399
400 char *
401 xmalloc (size)
402 unsigned size;
403 {
404 char *result = (char *) malloc (size);
405 if (!result)
406 fatal ("virtual memory exhausted", 0);
407 return result;
408 }
409 \f
410 /* This is the guts of the interface to the Post Office Protocol. */
411
412 #ifdef MAIL_USE_POP
413
414 #include <sys/socket.h>
415 #include <netinet/in.h>
416 #include <netdb.h>
417 #include <stdio.h>
418 #include <pwd.h>
419
420 #ifdef USG
421 #include <fcntl.h>
422 /* Cancel substitutions made by config.h for Emacs. */
423 #undef open
424 #undef read
425 #undef write
426 #undef close
427 #endif /* USG */
428
429 #define NOTOK (-1)
430 #define OK 0
431 #define DONE 1
432
433 char *progname;
434 FILE *sfi;
435 FILE *sfo;
436 char Errmsg[80];
437
438 static int debug = 0;
439
440 char *get_errmsg ();
441 char *getenv ();
442 int mbx_write ();
443
444 popmail (user, outfile)
445 char *user;
446 char *outfile;
447 {
448 char *host;
449 int nmsgs, nbytes;
450 char response[128];
451 register int i;
452 int mbfi;
453 FILE *mbf;
454 struct passwd *pw = (struct passwd *) getpwuid (getuid ());
455 if (pw == NULL)
456 fatal ("cannot determine user name");
457
458 host = getenv ("MAILHOST");
459 if (host == NULL)
460 {
461 fatal ("no MAILHOST defined");
462 }
463
464 if (pop_init (host) == NOTOK)
465 {
466 fatal (Errmsg);
467 }
468
469 if (getline (response, sizeof response, sfi) != OK)
470 {
471 fatal (response);
472 }
473
474 if (pop_command ("USER %s", user) == NOTOK
475 || pop_command ("RPOP %s", pw->pw_name) == NOTOK)
476 {
477 pop_command ("QUIT");
478 fatal (Errmsg);
479 }
480
481 if (pop_stat (&nmsgs, &nbytes) == NOTOK)
482 {
483 pop_command ("QUIT");
484 fatal (Errmsg);
485 }
486
487 if (!nmsgs)
488 {
489 pop_command ("QUIT");
490 return 0;
491 }
492
493 mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
494 if (mbfi < 0)
495 {
496 pop_command ("QUIT");
497 pfatal_and_delete (outfile);
498 }
499 fchown (mbfi, getuid (), -1);
500
501 if ((mbf = fdopen (mbfi, "w")) == NULL)
502 {
503 pop_command ("QUIT");
504 pfatal_and_delete (outfile);
505 }
506
507 for (i = 1; i <= nmsgs; i++)
508 {
509 mbx_delimit_begin (mbf);
510 if (pop_retr (i, mbx_write, mbf) != OK)
511 {
512 pop_command ("QUIT");
513 close (mbfi);
514 unlink (outfile);
515 fatal (Errmsg);
516 }
517 mbx_delimit_end (mbf);
518 fflush (mbf);
519 }
520
521 if (fsync (mbfi) < 0)
522 {
523 pop_command ("QUIT");
524 pfatal_and_delete (outfile);
525 }
526
527 if (close (mbfi) == -1)
528 {
529 pop_command ("QUIT");
530 pfatal_and_delete (outfile);
531 }
532
533 for (i = 1; i <= nmsgs; i++)
534 {
535 if (pop_command ("DELE %d", i) == NOTOK)
536 {
537 /* Better to ignore this failure. */
538 }
539 }
540
541 pop_command ("QUIT");
542 return (0);
543 }
544
545 pop_init (host)
546 char *host;
547 {
548 register struct hostent *hp;
549 register struct servent *sp;
550 int lport = IPPORT_RESERVED - 1;
551 struct sockaddr_in sin;
552 register int s;
553
554 hp = gethostbyname (host);
555 if (hp == NULL)
556 {
557 sprintf (Errmsg, "MAILHOST unknown: %s", host);
558 return NOTOK;
559 }
560
561 sp = getservbyname ("pop", "tcp");
562 if (sp == 0)
563 {
564 strcpy (Errmsg, "tcp/pop: unknown service");
565 return NOTOK;
566 }
567
568 sin.sin_family = hp->h_addrtype;
569 bcopy (hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
570 sin.sin_port = sp->s_port;
571 s = rresvport (&lport);
572 if (s < 0)
573 {
574 sprintf (Errmsg, "error creating socket: %s", get_errmsg ());
575 return NOTOK;
576 }
577
578 if (connect (s, (char *)&sin, sizeof sin) < 0)
579 {
580 sprintf (Errmsg, "error during connect: %s", get_errmsg ());
581 close (s);
582 return NOTOK;
583 }
584
585 sfi = fdopen (s, "r");
586 sfo = fdopen (s, "w");
587 if (sfi == NULL || sfo == NULL)
588 {
589 sprintf (Errmsg, "error in fdopen: %s", get_errmsg ());
590 close (s);
591 return NOTOK;
592 }
593
594 return OK;
595 }
596
597 pop_command (fmt, a, b, c, d)
598 char *fmt;
599 {
600 char buf[128];
601 char errmsg[64];
602
603 sprintf (buf, fmt, a, b, c, d);
604
605 if (debug) fprintf (stderr, "---> %s\n", buf);
606 if (putline (buf, Errmsg, sfo) == NOTOK) return NOTOK;
607
608 if (getline (buf, sizeof buf, sfi) != OK)
609 {
610 strcpy (Errmsg, buf);
611 return NOTOK;
612 }
613
614 if (debug)
615 fprintf (stderr, "<--- %s\n", buf);
616 if (*buf != '+')
617 {
618 strcpy (Errmsg, buf);
619 return NOTOK;
620 }
621 else
622 {
623 return OK;
624 }
625 }
626
627
628 pop_stat (nmsgs, nbytes)
629 int *nmsgs, *nbytes;
630 {
631 char buf[128];
632
633 if (debug)
634 fprintf (stderr, "---> STAT\n");
635 if (putline ("STAT", Errmsg, sfo) == NOTOK)
636 return NOTOK;
637
638 if (getline (buf, sizeof buf, sfi) != OK)
639 {
640 strcpy (Errmsg, buf);
641 return NOTOK;
642 }
643
644 if (debug) fprintf (stderr, "<--- %s\n", buf);
645 if (*buf != '+')
646 {
647 strcpy (Errmsg, buf);
648 return NOTOK;
649 }
650 else
651 {
652 sscanf (buf, "+OK %d %d", nmsgs, nbytes);
653 return OK;
654 }
655 }
656
657 pop_retr (msgno, action, arg)
658 int (*action)();
659 {
660 char buf[128];
661
662 sprintf (buf, "RETR %d", msgno);
663 if (debug) fprintf (stderr, "%s\n", buf);
664 if (putline (buf, Errmsg, sfo) == NOTOK) return NOTOK;
665
666 if (getline (buf, sizeof buf, sfi) != OK)
667 {
668 strcpy (Errmsg, buf);
669 return NOTOK;
670 }
671
672 while (1)
673 {
674 switch (multiline (buf, sizeof buf, sfi))
675 {
676 case OK:
677 (*action)(buf, arg);
678 break;
679 case DONE:
680 return OK;
681 case NOTOK:
682 strcpy (Errmsg, buf);
683 return NOTOK;
684 }
685 }
686 }
687
688 getline (buf, n, f)
689 char *buf;
690 register int n;
691 FILE *f;
692 {
693 register char *p;
694 int c;
695
696 p = buf;
697 while (--n > 0 && (c = fgetc (f)) != EOF)
698 if ((*p++ = c) == '\n') break;
699
700 if (ferror (f))
701 {
702 strcpy (buf, "error on connection");
703 return NOTOK;
704 }
705
706 if (c == EOF && p == buf)
707 {
708 strcpy (buf, "connection closed by foreign host");
709 return DONE;
710 }
711
712 *p = NULL;
713 if (*--p == '\n') *p = NULL;
714 if (*--p == '\r') *p = NULL;
715 return OK;
716 }
717
718 multiline (buf, n, f)
719 char *buf;
720 register int n;
721 FILE *f;
722 {
723 if (getline (buf, n, f) != OK)
724 return NOTOK;
725 if (*buf == '.')
726 {
727 if (*(buf+1) == NULL)
728 return DONE;
729 else
730 strcpy (buf, buf+1);
731 }
732 return OK;
733 }
734
735 char *
736 get_errmsg ()
737 {
738 extern int errno, sys_nerr;
739 extern char *sys_errlist[];
740 char *s;
741
742 if (errno < sys_nerr)
743 s = sys_errlist[errno];
744 else
745 s = "unknown error";
746 return (s);
747 }
748
749 putline (buf, err, f)
750 char *buf;
751 char *err;
752 FILE *f;
753 {
754 fprintf (f, "%s\r\n", buf);
755 fflush (f);
756 if (ferror (f))
757 {
758 strcpy (err, "lost connection");
759 return NOTOK;
760 }
761 return OK;
762 }
763
764 mbx_write (line, mbf)
765 char *line;
766 FILE *mbf;
767 {
768 fputs (line, mbf);
769 fputc (0x0a, mbf);
770 }
771
772 mbx_delimit_begin (mbf)
773 FILE *mbf;
774 {
775 fputs ("\f\n0, unseen,,\n", mbf);
776 }
777
778 mbx_delimit_end (mbf)
779 FILE *mbf;
780 {
781 putc ('\037', mbf);
782 }
783
784 #endif /* MAIL_USE_POP */