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