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