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