|
|
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 distributed in the hope that it will be useful,
8: but without any warranty. No author or distributor
9: accepts responsibility to anyone for the consequences of using it
10: or for whether it serves any particular purpose or works at all,
11: unless he says so in writing.
12:
13: Everyone is granted permission to copy, modify and redistribute
14: GNU Emacs, but only under the conditions described in the
15: document "GNU Emacs copying permission notice". An exact copy
16: of the document is supposed to have been given to you along with
17: GNU Emacs so that you can know how you may redistribute it all.
18: It should be in a file named COPYING. Among other things, the
19: copyright notice and this notice must be preserved on all copies. */
20:
21: /*
22: * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
23: *
24: * Added POP (Post Office Protocol) service. When compiled -DPOP
25: * movemail will accept input filename arguments of the form
26: * "po:username". This will cause movemail to open a connection to
27: * a pop server running on $MAILHOST (environment variable). Movemail
28: * must be setuid to root in order to work with POP.
29: *
30: * New module: popmail.c
31: * Modified routines:
32: * main - added code within #ifdef MAIL_USE_POP; added setuid(getuid())
33: * after POP code.
34: * New routines in movemail.c:
35: * get_errmsg - return pointer to system error message
36: *
37: */
38:
39: #include <sys/types.h>
40: #include <sys/stat.h>
41: #include <sys/file.h>
42: #include <errno.h>
43: #define NO_SHORTNAMES /* Tell config not to load remap.h */
44: #include "../src/config.h"
45:
46: #ifdef USG
47: #include <fcntl.h>
48: #include <unistd.h>
49: #ifndef F_OK
50: #define F_OK 0
51: #define X_OK 1
52: #define W_OK 2
53: #define R_OK 4
54: #endif
55: #endif /* USG */
56:
57: #ifdef XENIX
58: #include <sys/locking.h>
59: #endif
60:
61: /* Cancel substitutions made by config.h for Emacs. */
62: #undef open
63: #undef read
64: #undef write
65: #undef close
66:
67: char *concat ();
68: extern int errno;
69:
70: /* Nonzero means this is name of a lock file to delete on fatal error. */
71: char *delete_lockname;
72:
73: main (argc, argv)
74: int argc;
75: char **argv;
76: {
77: char *inname, *outname;
78: int indesc, outdesc;
79: char buf[1024];
80: int nread;
81:
82: #ifndef MAIL_USE_FLOCK
83: struct stat st;
84: long now;
85: int tem;
86: char *lockname, *p;
87: char tempname[40];
88: int desc;
89: #endif /* not MAIL_USE_FLOCK */
90:
91: delete_lockname = 0;
92:
93: if (argc < 3)
94: fatal ("two arguments required");
95:
96: inname = argv[1];
97: outname = argv[2];
98:
99: /* Check access to input and output file. */
100: if (access (inname, R_OK | W_OK) != 0)
101: pfatal_with_name (inname);
102: if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
103: pfatal_with_name (outname);
104:
105: /* Also check that outname's directory is writeable to the real uid. */
106: {
107: char *buf = (char *) malloc (strlen (outname) + 1);
108: char *p, q;
109: strcpy (buf, outname);
110: p = buf + strlen (buf);
111: while (p > buf && p[-1] != '/')
112: *--p = 0;
113: if (p == buf)
114: *p++ = '.';
115: if (access (buf, W_OK) != 0)
116: pfatal_with_name (buf);
117: free (buf);
118: }
119:
120: #ifdef MAIL_USE_POP
121: if (!bcmp (inname, "po:", 3))
122: {
123: int status; char *user;
124:
125: user = (char *) rindex (inname, ':') + 1;
126: status = popmail (user, outname);
127: exit (status);
128: }
129:
130: setuid (getuid());
131: #endif /* MAIL_USE_POP */
132:
133: #ifndef MAIL_USE_FLOCK
134: /* Use a lock file named /usr/spool/mail/$USER.lock:
135: If it exists, the mail file is locked. */
136: lockname = concat (inname, ".lock", "");
137: strcpy (tempname, inname);
138: p = tempname + strlen (tempname);
139: while (p != tempname && p[-1] != '/')
140: p--;
141: *p = 0;
142: strcpy (p, "EXXXXXX");
143: mktemp (tempname);
144: (void) unlink (tempname);
145:
146: while (1)
147: {
148: /* Create the lock file, but not under the lock file name. */
149: /* Give up if cannot do that. */
150: desc = open (tempname, O_WRONLY | O_CREAT, 0666);
151: if (desc < 0)
152: pfatal_with_name (concat ("temporary file \"", tempname, "\""));
153: close (desc);
154:
155: tem = link (tempname, lockname);
156: (void) unlink (tempname);
157: if (tem >= 0)
158: break;
159: sleep (1);
160:
161: /* If lock file is a minute old, unlock it. */
162: if (stat (lockname, &st) >= 0)
163: {
164: now = time (0);
165: if (st.st_ctime < now - 60)
166: (void) unlink (lockname);
167: }
168: }
169:
170: delete_lockname = lockname;
171: #endif /* not MAIL_USE_FLOCK */
172:
173: #ifdef MAIL_USE_FLOCK
174: indesc = open (inname, O_RDWR);
175: #else /* if not MAIL_USE_FLOCK */
176: indesc = open (inname, O_RDONLY);
177: #endif /* not MAIL_USE_FLOCK */
178: if (indesc < 0)
179: pfatal_with_name (inname);
180:
181: #if defined(BSD) || defined(XENIX)
182: /* In case movemail is setuid to root, make sure the user can
183: read the output file. */
184: /* This is desirable for all systems
185: but I don't want to assume all have the umask system call */
186: umask (umask (0) & 0333);
187: #endif /* BSD or Xenix */
188: outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
189: if (outdesc < 0)
190: pfatal_with_name (outname);
191: #ifdef MAIL_USE_FLOCK
192: #ifdef XENIX
193: if (locking (indesc, LK_RLCK, 0L) < 0) pfatal_with_name (inname);
194: #else
195: flock (indesc, LOCK_EX);
196: #endif
197: #endif /* MAIL_USE_FLOCK */
198:
199: while (1)
200: {
201: nread = read (indesc, buf, sizeof buf);
202: if (nread != write (outdesc, buf, nread))
203: {
204: int saved_errno = errno;
205: (void) unlink (outname);
206: errno = saved_errno;
207: pfatal_with_name (outname);
208: }
209: if (nread < sizeof buf)
210: break;
211: }
212:
213: #ifdef BSD
214: fsync (outdesc);
215: #endif
216:
217: /* Check to make sure no errors before we zap the inbox. */
218: if (close (outdesc) != 0)
219: {
220: int saved_errno = errno;
221: (void) unlink (outname);
222: errno = saved_errno;
223: pfatal_with_name (outname);
224: }
225:
226: #ifdef MAIL_USE_FLOCK
227: #if defined(STRIDE) || defined(XENIX)
228: /* Stride, xenix have file locking, but no ftruncate. This mess will do. */
229: (void) close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
230: #else
231: (void) ftruncate (indesc, 0L);
232: #endif /* STRIDE or XENIX */
233: #endif /* MAIL_USE_FLOCK */
234: close (indesc);
235:
236: #ifndef MAIL_USE_FLOCK
237: /* Delete the input file; if we can't, at least get rid of its contents. */
238: if (unlink (inname) < 0)
239: if (errno != ENOENT)
240: creat (inname, 0666);
241: (void) unlink (lockname);
242: #endif /* not MAIL_USE_FLOCK */
243: exit (0);
244: }
245:
246: /* Print error message and exit. */
247:
248: fatal (s1, s2)
249: char *s1, *s2;
250: {
251: if (delete_lockname)
252: unlink (delete_lockname);
253: error (s1, s2);
254: exit (1);
255: }
256:
257: /* Print error message. `s1' is printf control string, `s2' is arg for it. */
258:
259: error (s1, s2)
260: char *s1, *s2;
261: {
262: printf ("movemail: ");
263: printf (s1, s2);
264: printf ("\n");
265: }
266:
267: pfatal_with_name (name)
268: char *name;
269: {
270: extern int errno, sys_nerr;
271: extern char *sys_errlist[];
272: char *s;
273:
274: if (errno < sys_nerr)
275: s = concat ("", sys_errlist[errno], " for %s");
276: else
277: s = "cannot open %s";
278: fatal (s, name);
279: }
280:
281: /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
282:
283: char *
284: concat (s1, s2, s3)
285: char *s1, *s2, *s3;
286: {
287: int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
288: char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
289:
290: strcpy (result, s1);
291: strcpy (result + len1, s2);
292: strcpy (result + len1 + len2, s3);
293: *(result + len1 + len2 + len3) = 0;
294:
295: return result;
296: }
297:
298: /* Like malloc but get fatal error if memory is exhausted. */
299:
300: int
301: xmalloc (size)
302: int size;
303: {
304: int result = malloc (size);
305: if (!result)
306: fatal ("virtual memory exhausted", 0);
307: return result;
308: }
309:
310: /* This is the guts of the interface to the Post Office Protocol. */
311:
312: #ifdef MAIL_USE_POP
313:
314: #include <sys/socket.h>
315: #include <netinet/in.h>
316: #include <netdb.h>
317: #include <stdio.h>
318:
319: #ifdef USG
320: #include <fcntl.h>
321: /* Cancel substitutions made by config.h for Emacs. */
322: #undef open
323: #undef read
324: #undef write
325: #undef close
326: #endif /* USG */
327:
328: #define NOTOK (-1)
329: #define OK 0
330: #define DONE 1
331:
332: char *progname;
333: FILE *sfi;
334: FILE *sfo;
335: char Errmsg[80];
336:
337: static int debug = 0;
338:
339: popmail(user, outfile)
340: char *user;
341: char *outfile;
342: {
343: char *host;
344: int nmsgs, nbytes;
345: char response[128];
346: register int i;
347: int mbfi;
348: FILE *mbf;
349: char *getenv();
350: int mbx_write();
351: char *get_errmsg();
352:
353: host = getenv("MAILHOST");
354: if (host == NULL) {
355: fatal("no MAILHOST defined");
356: }
357:
358: if (pop_init(host) == NOTOK) {
359: error(Errmsg);
360: return(1);
361: }
362:
363: if (getline(response, sizeof response, sfi) != OK) {
364: error(response);
365: return(1);
366: }
367:
368: if (pop_command("USER %s", user) == NOTOK ||
369: pop_command("RPOP %s", user) == NOTOK) {
370: error(Errmsg);
371: pop_command("QUIT");
372: return(1);
373: }
374:
375: if (pop_stat(&nmsgs, &nbytes) == NOTOK) {
376: error(Errmsg);
377: pop_command("QUIT");
378: return(1);
379: }
380:
381: if (!nmsgs)
382: {
383: pop_command("QUIT");
384: return(0);
385: }
386:
387: mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
388: if (mbfi < 0)
389: {
390: pop_command("QUIT");
391: error("Error in open: %s, %s", get_errmsg(), outfile);
392: return(1);
393: }
394: fchown(mbfi, getuid(), -1);
395:
396: if ((mbf = fdopen(mbfi, "w")) == NULL)
397: {
398: pop_command("QUIT");
399: error("Error in fdopen: %s", get_errmsg());
400: close(mbfi);
401: unlink(outfile);
402: return(1);
403: }
404:
405: for (i = 1; i <= nmsgs; i++) {
406: mbx_delimit_begin(mbf);
407: if (pop_retr(i, mbx_write, mbf) != OK) {
408: error(Errmsg);
409: pop_command("QUIT");
410: close(mbfi);
411: return(1);
412: }
413: mbx_delimit_end(mbf);
414: fflush(mbf);
415: }
416:
417: for (i = 1; i <= nmsgs; i++) {
418: if (pop_command("DELE %d", i) == NOTOK) {
419: error(Errmsg);
420: pop_command("QUIT");
421: close(mbfi);
422: return(1);
423: }
424: }
425:
426: pop_command("QUIT");
427: close(mbfi);
428: return(0);
429: }
430:
431: pop_init(host)
432: char *host;
433: {
434: register struct hostent *hp;
435: register struct servent *sp;
436: int lport = IPPORT_RESERVED - 1;
437: struct sockaddr_in sin;
438: register int s;
439: char *get_errmsg();
440:
441: hp = gethostbyname(host);
442: if (hp == NULL) {
443: sprintf(Errmsg, "MAILHOST unknown: %s", host);
444: return(NOTOK);
445: }
446:
447: sp = getservbyname("pop", "tcp");
448: if (sp == 0) {
449: strcpy(Errmsg, "tcp/pop: unknown service");
450: return(NOTOK);
451: }
452:
453: sin.sin_family = hp->h_addrtype;
454: bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
455: sin.sin_port = sp->s_port;
456: s = rresvport(&lport);
457: if (s < 0) {
458: sprintf(Errmsg, "error creating socket: %s", get_errmsg());
459: return(NOTOK);
460: }
461:
462: if (connect(s, (char *)&sin, sizeof sin) < 0) {
463: sprintf(Errmsg, "error during connect: %s", get_errmsg());
464: close(s);
465: return(NOTOK);
466: }
467:
468: sfi = fdopen(s, "r");
469: sfo = fdopen(s, "w");
470: if (sfi == NULL || sfo == NULL) {
471: sprintf(Errmsg, "error in fdopen: %s", get_errmsg());
472: close(s);
473: return(NOTOK);
474: }
475:
476: return(OK);
477: }
478:
479: pop_command(fmt, a, b, c, d)
480: char *fmt;
481: {
482: char buf[128];
483: char errmsg[64];
484:
485: sprintf(buf, fmt, a, b, c, d);
486:
487: if (debug) fprintf(stderr, "---> %s\n", buf);
488: if (putline(buf, Errmsg, sfo) == NOTOK) return(NOTOK);
489:
490: if (getline(buf, sizeof buf, sfi) != OK) {
491: strcpy(Errmsg, buf);
492: return(NOTOK);
493: }
494:
495: if (debug) fprintf(stderr, "<--- %s\n", buf);
496: if (*buf != '+') {
497: strcpy(Errmsg, buf);
498: return(NOTOK);
499: } else {
500: return(OK);
501: }
502: }
503:
504:
505: pop_stat(nmsgs, nbytes)
506: int *nmsgs, *nbytes;
507: {
508: char buf[128];
509:
510: if (debug) fprintf(stderr, "---> STAT\n");
511: if (putline("STAT", Errmsg, sfo) == NOTOK) return(NOTOK);
512:
513: if (getline(buf, sizeof buf, sfi) != OK) {
514: strcpy(Errmsg, buf);
515: return(NOTOK);
516: }
517:
518: if (debug) fprintf(stderr, "<--- %s\n", buf);
519: if (*buf != '+') {
520: strcpy(Errmsg, buf);
521: return(NOTOK);
522: } else {
523: sscanf(buf, "+OK %d %d", nmsgs, nbytes);
524: return(OK);
525: }
526: }
527:
528: pop_retr(msgno, action, arg)
529: int (*action)();
530: {
531: char buf[128];
532:
533: sprintf(buf, "RETR %d", msgno);
534: if (debug) fprintf(stderr, "%s\n", buf);
535: if (putline(buf, Errmsg, sfo) == NOTOK) return(NOTOK);
536:
537: if (getline(buf, sizeof buf, sfi) != OK) {
538: strcpy(Errmsg, buf);
539: return(NOTOK);
540: }
541:
542: while (1) {
543: switch (multiline(buf, sizeof buf, sfi)) {
544: case OK:
545: (*action)(buf, arg);
546: break;
547: case DONE:
548: return (OK);
549: case NOTOK:
550: strcpy(Errmsg, buf);
551: return (NOTOK);
552: }
553: }
554: }
555:
556: getline(buf, n, f)
557: char *buf;
558: register int n;
559: FILE *f;
560: {
561: register char *p;
562: int c;
563:
564: p = buf;
565: while (--n > 0 && (c = fgetc(f)) != EOF)
566: if ((*p++ = c) == '\n') break;
567:
568: if (ferror(f)) {
569: strcpy(buf, "error on connection");
570: return (NOTOK);
571: }
572:
573: if (c == EOF && p == buf) {
574: strcpy(buf, "connection closed by foreign host");
575: return (DONE);
576: }
577:
578: *p = NULL;
579: if (*--p == '\n') *p = NULL;
580: if (*--p == '\r') *p = NULL;
581: return(OK);
582: }
583:
584: multiline(buf, n, f)
585: char *buf;
586: register int n;
587: FILE *f;
588: {
589: if (getline(buf, n, f) != OK) return (NOTOK);
590: if (*buf == '.') {
591: if (*(buf+1) == NULL) {
592: return (DONE);
593: } else {
594: strcpy(buf, buf+1);
595: }
596: }
597: return(OK);
598: }
599:
600: char *
601: get_errmsg()
602: {
603: extern int errno, sys_nerr;
604: extern char *sys_errlist[];
605: char *s;
606:
607: if (errno < sys_nerr)
608: s = sys_errlist[errno];
609: else
610: s = "unknown error";
611: return(s);
612: }
613:
614: putline(buf, err, f)
615: char *buf;
616: char *err;
617: FILE *f;
618: {
619: fprintf(f, "%s\r\n", buf);
620: fflush(f);
621: if (ferror(f)) {
622: strcpy(err, "lost connection");
623: return(NOTOK);
624: }
625: return(OK);
626: }
627:
628: mbx_write(line, mbf)
629: char *line;
630: FILE *mbf;
631: {
632: fputs(line, mbf);
633: fputc(0x0a, mbf);
634: }
635:
636: mbx_delimit_begin(mbf)
637: FILE *mbf;
638: {
639: fputs("\f\n0,unseen,,\n", mbf);
640: }
641:
642: mbx_delimit_end(mbf)
643: FILE *mbf;
644: {
645: putc('\037', mbf);
646: }
647:
648: #endif /* MAIL_USE_POP */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.