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