(rmail-mode-map): Key binding for rmail-sort-by-keywords.
[emacs.git] / lib-src / movemail.c
blob1d63d385a42324b9f0e9f44b4c0e174bbb2cc4b4
1 /* movemail foo bar -- move file foo to file bar,
2 locking file foo the way /bin/mail respects.
3 Copyright (C) 1986, 1992, 1993, 1994 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
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 2, or (at your option)
10 any later version.
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.
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. */
21 /* Important notice: defining MAIL_USE_FLOCK or MAIL_USE_LOCKF *will
22 cause loss of mail* if you do it on a system that does not normally
23 use flock as its way of interlocking access to inbox files. The
24 setting of MAIL_USE_FLOCK and MAIL_USE_LOCKF *must agree* with the
25 system's own conventions. It is not a choice that is up to you.
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. */
34 * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
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.
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
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 #include <../src/syswait.h>
59 #ifdef MSDOS
60 #undef access
61 #endif /* MSDOS */
63 #ifdef USG
64 #include <fcntl.h>
65 #include <unistd.h>
66 #ifndef F_OK
67 #define F_OK 0
68 #define X_OK 1
69 #define W_OK 2
70 #define R_OK 4
71 #endif
72 #endif /* USG */
74 #ifdef XENIX
75 #include <sys/locking.h>
76 #endif
78 #ifdef MAIL_USE_LOCKF
79 #define MAIL_USE_SYSTEM_LOCK
80 #endif
82 #ifdef MAIL_USE_FLOCK
83 #define MAIL_USE_SYSTEM_LOCK
84 #endif
86 #ifdef MAIL_USE_MMDF
87 extern int lk_open (), lk_close ();
88 #endif
90 /* Cancel substitutions made by config.h for Emacs. */
91 #undef open
92 #undef read
93 #undef write
94 #undef close
96 char *concat ();
97 char *xmalloc ();
98 #ifndef errno
99 extern int errno;
100 #endif
102 /* Nonzero means this is name of a lock file to delete on fatal error. */
103 char *delete_lockname;
105 main (argc, argv)
106 int argc;
107 char **argv;
109 char *inname, *outname;
110 int indesc, outdesc;
111 int nread;
112 WAITTYPE status;
114 #ifndef MAIL_USE_SYSTEM_LOCK
115 struct stat st;
116 long now;
117 int tem;
118 char *lockname, *p;
119 char *tempname;
120 int desc;
121 #endif /* not MAIL_USE_SYSTEM_LOCK */
123 delete_lockname = 0;
125 if (argc < 3)
126 fatal ("two arguments required");
128 inname = argv[1];
129 outname = argv[2];
131 #ifdef MAIL_USE_MMDF
132 mmdf_init (argv[0]);
133 #endif
135 /* Check access to output file. */
136 if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
137 pfatal_with_name (outname);
139 /* Also check that outname's directory is writeable to the real uid. */
141 char *buf = (char *) xmalloc (strlen (outname) + 1);
142 char *p, q;
143 strcpy (buf, outname);
144 p = buf + strlen (buf);
145 while (p > buf && p[-1] != '/')
146 *--p = 0;
147 if (p == buf)
148 *p++ = '.';
149 if (access (buf, W_OK) != 0)
150 pfatal_with_name (buf);
151 free (buf);
154 #ifdef MAIL_USE_POP
155 if (!strncmp (inname, "po:", 3))
157 int status; char *user;
159 for (user = &inname[strlen (inname) - 1]; user >= inname; user--)
160 if (*user == ':')
161 break;
163 status = popmail (user, outname);
164 exit (status);
167 setuid (getuid ());
168 #endif /* MAIL_USE_POP */
170 /* Check access to input file. */
171 if (access (inname, R_OK | W_OK) != 0)
172 pfatal_with_name (inname);
174 #ifndef MAIL_USE_MMDF
175 #ifndef MAIL_USE_SYSTEM_LOCK
176 /* Use a lock file named /usr/spool/mail/$USER.lock:
177 If it exists, the mail file is locked. */
178 /* Note: this locking mechanism is *required* by the mailer
179 (on systems which use it) to prevent loss of mail.
181 On systems that use a lock file, extracting the mail without locking
182 WILL occasionally cause loss of mail due to timing errors!
184 So, if creation of the lock file fails
185 due to access permission on /usr/spool/mail,
186 you simply MUST change the permission
187 and/or make movemail a setgid program
188 so it can create lock files properly.
190 You might also wish to verify that your system is one
191 which uses lock files for this purpose. Some systems use other methods.
193 If your system uses the `flock' system call for mail locking,
194 define MAIL_USE_SYSTEM_LOCK in config.h or the s-*.h file
195 and recompile movemail. If the s- file for your system
196 should define MAIL_USE_SYSTEM_LOCK but does not, send a bug report
197 to bug-gnu-emacs@prep.ai.mit.edu so we can fix it. */
199 lockname = concat (inname, ".lock", "");
200 tempname = (char *) xmalloc (strlen (inname) + strlen ("EXXXXXX") + 1);
201 strcpy (tempname, inname);
202 p = tempname + strlen (tempname);
203 while (p != tempname && p[-1] != '/')
204 p--;
205 *p = 0;
206 strcpy (p, "EXXXXXX");
207 mktemp (tempname);
208 unlink (tempname);
210 while (1)
212 /* Create the lock file, but not under the lock file name. */
213 /* Give up if cannot do that. */
214 desc = open (tempname, O_WRONLY | O_CREAT | O_EXCL, 0666);
215 if (desc < 0)
216 pfatal_with_name ("lock file--see source file lib-src/movemail.c");
217 close (desc);
219 tem = link (tempname, lockname);
220 unlink (tempname);
221 if (tem >= 0)
222 break;
223 sleep (1);
225 /* If lock file is a minute old, unlock it. */
226 if (stat (lockname, &st) >= 0)
228 now = time (0);
229 if (st.st_ctime < now - 60)
230 unlink (lockname);
234 delete_lockname = lockname;
235 #endif /* not MAIL_USE_SYSTEM_LOCK */
236 #endif /* not MAIL_USE_MMDF */
238 if (fork () == 0)
240 seteuid (getuid ());
242 #ifndef MAIL_USE_MMDF
243 #ifdef MAIL_USE_SYSTEM_LOCK
244 indesc = open (inname, O_RDWR);
245 #else /* if not MAIL_USE_SYSTEM_LOCK */
246 indesc = open (inname, O_RDONLY);
247 #endif /* not MAIL_USE_SYSTEM_LOCK */
248 #else /* MAIL_USE_MMDF */
249 indesc = lk_open (inname, O_RDONLY, 0, 0, 10);
250 #endif /* MAIL_USE_MMDF */
252 if (indesc < 0)
253 pfatal_with_name (inname);
255 #if defined (BSD) || defined (XENIX)
256 /* In case movemail is setuid to root, make sure the user can
257 read the output file. */
258 /* This is desirable for all systems
259 but I don't want to assume all have the umask system call */
260 umask (umask (0) & 0333);
261 #endif /* BSD or Xenix */
262 outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
263 if (outdesc < 0)
264 pfatal_with_name (outname);
265 #ifdef MAIL_USE_SYSTEM_LOCK
266 #ifdef MAIL_USE_LOCKF
267 if (lockf (indesc, F_LOCK, 0) < 0) pfatal_with_name (inname);
268 #else /* not MAIL_USE_LOCKF */
269 #ifdef XENIX
270 if (locking (indesc, LK_RLCK, 0L) < 0) pfatal_with_name (inname);
271 #else
272 if (flock (indesc, LOCK_EX) < 0) pfatal_with_name (inname);
273 #endif
274 #endif /* not MAIL_USE_LOCKF */
275 #endif /* MAIL_USE_SYSTEM_LOCK */
278 char buf[1024];
280 while (1)
282 nread = read (indesc, buf, sizeof buf);
283 if (nread != write (outdesc, buf, nread))
285 int saved_errno = errno;
286 unlink (outname);
287 errno = saved_errno;
288 pfatal_with_name (outname);
290 if (nread < sizeof buf)
291 break;
295 #ifdef BSD
296 if (fsync (outdesc) < 0)
297 pfatal_and_delete (outname);
298 #endif
300 /* Check to make sure no errors before we zap the inbox. */
301 if (close (outdesc) != 0)
302 pfatal_and_delete (outname);
304 #ifdef MAIL_USE_SYSTEM_LOCK
305 #if defined (STRIDE) || defined (XENIX)
306 /* Stride, xenix have file locking, but no ftruncate. This mess will do. */
307 close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
308 #else
309 ftruncate (indesc, 0L);
310 #endif /* STRIDE or XENIX */
311 #endif /* MAIL_USE_SYSTEM_LOCK */
313 #ifdef MAIL_USE_MMDF
314 lk_close (indesc, 0, 0, 0);
315 #else
316 close (indesc);
317 #endif
319 #ifndef MAIL_USE_SYSTEM_LOCK
320 /* Delete the input file; if we can't, at least get rid of its
321 contents. */
322 #ifdef MAIL_UNLINK_SPOOL
323 /* This is generally bad to do, because it destroys the permissions
324 that were set on the file. Better to just empty the file. */
325 if (unlink (inname) < 0 && errno != ENOENT)
326 #endif /* MAIL_UNLINK_SPOOL */
327 creat (inname, 0600);
328 #endif /* not MAIL_USE_SYSTEM_LOCK */
330 exit (0);
333 wait (&status);
334 if (!WIFEXITED (status))
335 exit (1);
336 else if (WRETCODE (status) != 0)
337 exit (WRETCODE (status));
339 #if !defined (MAIL_USE_MMDF) && !defined (MAIL_USE_SYSTEM_LOCK)
340 unlink (lockname);
341 #endif /* not MAIL_USE_MMDF and not MAIL_USE_SYSTEM_LOCK */
342 exit (0);
345 /* Print error message and exit. */
347 fatal (s1, s2)
348 char *s1, *s2;
350 if (delete_lockname)
351 unlink (delete_lockname);
352 error (s1, s2);
353 exit (1);
356 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
358 error (s1, s2, s3)
359 char *s1, *s2, *s3;
361 printf ("movemail: ");
362 printf (s1, s2, s3);
363 printf ("\n");
366 pfatal_with_name (name)
367 char *name;
369 extern int errno;
370 extern char *strerror ();
371 char *s;
373 s = concat ("", strerror (errno), " for %s");
374 fatal (s, name);
377 pfatal_and_delete (name)
378 char *name;
380 extern int errno;
381 extern char *strerror ();
382 char *s;
384 s = concat ("", strerror (errno), " for %s");
385 unlink (name);
386 fatal (s, name);
389 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
391 char *
392 concat (s1, s2, s3)
393 char *s1, *s2, *s3;
395 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
396 char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
398 strcpy (result, s1);
399 strcpy (result + len1, s2);
400 strcpy (result + len1 + len2, s3);
401 *(result + len1 + len2 + len3) = 0;
403 return result;
406 /* Like malloc but get fatal error if memory is exhausted. */
408 char *
409 xmalloc (size)
410 unsigned size;
412 char *result = (char *) malloc (size);
413 if (!result)
414 fatal ("virtual memory exhausted", 0);
415 return result;
418 /* This is the guts of the interface to the Post Office Protocol. */
420 #ifdef MAIL_USE_POP
422 #include <sys/socket.h>
423 #include <netinet/in.h>
424 #include <netdb.h>
425 #include <stdio.h>
426 #include <pwd.h>
428 #ifdef USG
429 #include <fcntl.h>
430 /* Cancel substitutions made by config.h for Emacs. */
431 #undef open
432 #undef read
433 #undef write
434 #undef close
435 #endif /* USG */
437 #define NOTOK (-1)
438 #define OK 0
439 #define DONE 1
441 char *progname;
442 FILE *sfi;
443 FILE *sfo;
444 char Errmsg[80];
446 static int debug = 0;
448 char *get_errmsg ();
449 char *getenv ();
450 int mbx_write ();
452 popmail (user, outfile)
453 char *user;
454 char *outfile;
456 char *host;
457 int nmsgs, nbytes;
458 char response[128];
459 register int i;
460 int mbfi;
461 FILE *mbf;
462 struct passwd *pw = (struct passwd *) getpwuid (getuid ());
463 if (pw == NULL)
464 fatal ("cannot determine user name");
466 host = getenv ("MAILHOST");
467 if (host == NULL)
469 fatal ("no MAILHOST defined");
472 if (pop_init (host) == NOTOK)
474 fatal (Errmsg);
477 if (getline (response, sizeof response, sfi) != OK)
479 fatal (response);
482 if (pop_command ("USER %s", user) == NOTOK
483 || pop_command ("RPOP %s", pw->pw_name) == NOTOK)
485 pop_command ("QUIT");
486 fatal (Errmsg);
489 if (pop_stat (&nmsgs, &nbytes) == NOTOK)
491 pop_command ("QUIT");
492 fatal (Errmsg);
495 if (!nmsgs)
497 pop_command ("QUIT");
498 return 0;
501 mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
502 if (mbfi < 0)
504 pop_command ("QUIT");
505 pfatal_and_delete (outfile);
507 fchown (mbfi, getuid (), -1);
509 if ((mbf = fdopen (mbfi, "w")) == NULL)
511 pop_command ("QUIT");
512 pfatal_and_delete (outfile);
515 for (i = 1; i <= nmsgs; i++)
517 mbx_delimit_begin (mbf);
518 if (pop_retr (i, mbx_write, mbf) != OK)
520 pop_command ("QUIT");
521 close (mbfi);
522 unlink (outfile);
523 fatal (Errmsg);
525 mbx_delimit_end (mbf);
526 fflush (mbf);
529 if (fsync (mbfi) < 0)
531 pop_command ("QUIT");
532 pfatal_and_delete (outfile);
535 if (close (mbfi) == -1)
537 pop_command ("QUIT");
538 pfatal_and_delete (outfile);
541 for (i = 1; i <= nmsgs; i++)
543 if (pop_command ("DELE %d", i) == NOTOK)
545 /* Better to ignore this failure. */
549 pop_command ("QUIT");
550 return (0);
553 pop_init (host)
554 char *host;
556 register struct hostent *hp;
557 register struct servent *sp;
558 int lport = IPPORT_RESERVED - 1;
559 struct sockaddr_in sin;
560 register int s;
562 hp = gethostbyname (host);
563 if (hp == NULL)
565 sprintf (Errmsg, "MAILHOST unknown: %s", host);
566 return NOTOK;
569 sp = getservbyname ("pop", "tcp");
570 if (sp == 0)
572 strcpy (Errmsg, "tcp/pop: unknown service");
573 return NOTOK;
576 sin.sin_family = hp->h_addrtype;
577 bcopy (hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
578 sin.sin_port = sp->s_port;
579 s = rresvport (&lport);
580 if (s < 0)
582 sprintf (Errmsg, "error creating socket: %s", get_errmsg ());
583 return NOTOK;
586 if (connect (s, (char *)&sin, sizeof sin) < 0)
588 sprintf (Errmsg, "error during connect: %s", get_errmsg ());
589 close (s);
590 return NOTOK;
593 sfi = fdopen (s, "r");
594 sfo = fdopen (s, "w");
595 if (sfi == NULL || sfo == NULL)
597 sprintf (Errmsg, "error in fdopen: %s", get_errmsg ());
598 close (s);
599 return NOTOK;
602 return OK;
605 pop_command (fmt, a, b, c, d)
606 char *fmt;
608 char buf[128];
609 char errmsg[64];
611 sprintf (buf, fmt, a, b, c, d);
613 if (debug) fprintf (stderr, "---> %s\n", buf);
614 if (putline (buf, Errmsg, sfo) == NOTOK) return NOTOK;
616 if (getline (buf, sizeof buf, sfi) != OK)
618 strcpy (Errmsg, buf);
619 return NOTOK;
622 if (debug)
623 fprintf (stderr, "<--- %s\n", buf);
624 if (*buf != '+')
626 strcpy (Errmsg, buf);
627 return NOTOK;
629 else
631 return OK;
636 pop_stat (nmsgs, nbytes)
637 int *nmsgs, *nbytes;
639 char buf[128];
641 if (debug)
642 fprintf (stderr, "---> STAT\n");
643 if (putline ("STAT", Errmsg, sfo) == NOTOK)
644 return NOTOK;
646 if (getline (buf, sizeof buf, sfi) != OK)
648 strcpy (Errmsg, buf);
649 return NOTOK;
652 if (debug) fprintf (stderr, "<--- %s\n", buf);
653 if (*buf != '+')
655 strcpy (Errmsg, buf);
656 return NOTOK;
658 else
660 sscanf (buf, "+OK %d %d", nmsgs, nbytes);
661 return OK;
665 pop_retr (msgno, action, arg)
666 int (*action)();
668 char buf[128];
670 sprintf (buf, "RETR %d", msgno);
671 if (debug) fprintf (stderr, "%s\n", buf);
672 if (putline (buf, Errmsg, sfo) == NOTOK) return NOTOK;
674 if (getline (buf, sizeof buf, sfi) != OK)
676 strcpy (Errmsg, buf);
677 return NOTOK;
680 while (1)
682 switch (multiline (buf, sizeof buf, sfi))
684 case OK:
685 (*action)(buf, arg);
686 break;
687 case DONE:
688 return OK;
689 case NOTOK:
690 strcpy (Errmsg, buf);
691 return NOTOK;
696 getline (buf, n, f)
697 char *buf;
698 register int n;
699 FILE *f;
701 register char *p;
702 int c;
704 p = buf;
705 while (--n > 0 && (c = fgetc (f)) != EOF)
706 if ((*p++ = c) == '\n') break;
708 if (ferror (f))
710 strcpy (buf, "error on connection");
711 return NOTOK;
714 if (c == EOF && p == buf)
716 strcpy (buf, "connection closed by foreign host");
717 return DONE;
720 *p = NULL;
721 if (*--p == '\n') *p = NULL;
722 if (*--p == '\r') *p = NULL;
723 return OK;
726 multiline (buf, n, f)
727 char *buf;
728 register int n;
729 FILE *f;
731 if (getline (buf, n, f) != OK)
732 return NOTOK;
733 if (*buf == '.')
735 if (*(buf+1) == NULL)
736 return DONE;
737 else
738 strcpy (buf, buf+1);
740 return OK;
743 char *
744 get_errmsg ()
746 extern int errno;
747 extern char *strerror ();
748 return strerror (errno);
751 putline (buf, err, f)
752 char *buf;
753 char *err;
754 FILE *f;
756 fprintf (f, "%s\r\n", buf);
757 fflush (f);
758 if (ferror (f))
760 strcpy (err, "lost connection");
761 return NOTOK;
763 return OK;
766 mbx_write (line, mbf)
767 char *line;
768 FILE *mbf;
770 fputs (line, mbf);
771 fputc (0x0a, mbf);
774 mbx_delimit_begin (mbf)
775 FILE *mbf;
777 fputs ("\f\n0, unseen,,\n", mbf);
780 mbx_delimit_end (mbf)
781 FILE *mbf;
783 putc ('\037', mbf);
786 #endif /* MAIL_USE_POP */
788 #ifndef HAVE_STRERROR
789 char *
790 strerror (errnum)
791 int errnum;
793 extern char *sys_errlist[];
794 extern int sys_nerr;
796 if (errnum >= 0 && errnum < sys_nerr)
797 return sys_errlist[errnum];
798 return (char *) "Unknown error";
801 #endif /* ! HAVE_STRERROR */