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