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