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