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