comparison lib-src/movemail.c @ 0:376386a54a3c r19-14

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