(x-invocation-args): Add defvar.
[emacs.git] / lib-src / movemail.c
blob91f926c3fbc5487ff70397dba1d52cbb9d11fb43
1 /* movemail foo bar -- move file foo to file bar,
2 locking file foo the way /bin/mail respects.
3 Copyright (C) 1986, 1992 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 1, 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 *will cause loss of mail*
22 if you do it on a system that does not normally use flock as its way of
23 interlocking access to inbox files. The setting of MAIL_USE_FLOCK
24 *must agree* with the system's own conventions.
25 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"
58 #ifdef USG
59 #include <fcntl.h>
60 #include <unistd.h>
61 #ifndef F_OK
62 #define F_OK 0
63 #define X_OK 1
64 #define W_OK 2
65 #define R_OK 4
66 #endif
67 #endif /* USG */
69 #ifdef XENIX
70 #include <sys/locking.h>
71 #endif
73 #ifdef MAIL_USE_MMDF
74 extern int lk_open (), lk_close ();
75 #endif
77 /* Cancel substitutions made by config.h for Emacs. */
78 #undef open
79 #undef read
80 #undef write
81 #undef close
83 char *malloc ();
84 char *strcpy ();
85 char *concat ();
86 char *xmalloc ();
87 #ifndef errno
88 extern int errno;
89 #endif
91 /* Nonzero means this is name of a lock file to delete on fatal error. */
92 char *delete_lockname;
94 main (argc, argv)
95 int argc;
96 char **argv;
98 char *inname, *outname;
99 int indesc, outdesc;
100 int nread;
102 #ifndef MAIL_USE_FLOCK
103 struct stat st;
104 long now;
105 int tem;
106 char *lockname, *p;
107 char *tempname;
108 int desc;
109 #endif /* not MAIL_USE_FLOCK */
111 delete_lockname = 0;
113 if (argc < 3)
114 fatal ("two arguments required");
116 inname = argv[1];
117 outname = argv[2];
119 #ifdef MAIL_USE_MMDF
120 mmdf_init (argv[0]);
121 #endif
123 /* Check access to output file. */
124 if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
125 pfatal_with_name (outname);
127 /* Also check that outname's directory is writeable to the real uid. */
129 char *buf = (char *) malloc (strlen (outname) + 1);
130 char *p, q;
131 strcpy (buf, outname);
132 p = buf + strlen (buf);
133 while (p > buf && p[-1] != '/')
134 *--p = 0;
135 if (p == buf)
136 *p++ = '.';
137 if (access (buf, W_OK) != 0)
138 pfatal_with_name (buf);
139 free (buf);
142 #ifdef MAIL_USE_POP
143 if (!strncmp (inname, "po:", 3))
145 int status; char *user;
147 for (user = &inname[strlen (inname) - 1]; user >= inname; user--)
148 if (*user == ':')
149 break;
151 status = popmail (user, outname);
152 exit (status);
155 setuid (getuid ());
156 #endif /* MAIL_USE_POP */
158 /* Check access to input file. */
159 if (access (inname, R_OK | W_OK) != 0)
160 pfatal_with_name (inname);
162 #ifndef MAIL_USE_MMDF
163 #ifndef MAIL_USE_FLOCK
164 /* Use a lock file named /usr/spool/mail/$USER.lock:
165 If it exists, the mail file is locked. */
166 /* Note: this locking mechanism is *required* by the mailer
167 (on systems which use it) to prevent loss of mail.
169 On systems that use a lock file, extracting the mail without locking
170 WILL occasionally cause loss of mail due to timing errors!
172 So, if creation of the lock file fails
173 due to access permission on /usr/spool/mail,
174 you simply MUST change the permission
175 and/or make movemail a setgid program
176 so it can create lock files properly.
178 You might also wish to verify that your system is one
179 which uses lock files for this purpose. Some systems use other methods.
181 If your system uses the `flock' system call for mail locking,
182 define MAIL_USE_FLOCK in config.h or the s-*.h file
183 and recompile movemail. If the s- file for your system
184 should define MAIL_USE_FLOCK but does not, send a bug report
185 to bug-gnu-emacs@prep.ai.mit.edu so we can fix it. */
187 lockname = concat (inname, ".lock", "");
188 tempname = strcpy (xmalloc (strlen (inname)+1), inname);
189 p = tempname + strlen (tempname);
190 while (p != tempname && p[-1] != '/')
191 p--;
192 *p = 0;
193 strcpy (p, "EXXXXXX");
194 mktemp (tempname);
195 unlink (tempname);
197 while (1)
199 /* Create the lock file, but not under the lock file name. */
200 /* Give up if cannot do that. */
201 desc = open (tempname, O_WRONLY | O_CREAT, 0666);
202 if (desc < 0)
203 pfatal_with_name ("lock file--see source file etc/movemail.c");
204 close (desc);
206 tem = link (tempname, lockname);
207 unlink (tempname);
208 if (tem >= 0)
209 break;
210 sleep (1);
212 /* If lock file is a minute old, unlock it. */
213 if (stat (lockname, &st) >= 0)
215 now = time (0);
216 if (st.st_ctime < now - 60)
217 unlink (lockname);
221 delete_lockname = lockname;
222 #endif /* not MAIL_USE_FLOCK */
224 #ifdef MAIL_USE_FLOCK
225 indesc = open (inname, O_RDWR);
226 #else /* if not MAIL_USE_FLOCK */
227 indesc = open (inname, O_RDONLY);
228 #endif /* not MAIL_USE_FLOCK */
229 #else /* MAIL_USE_MMDF */
230 indesc = lk_open (inname, O_RDONLY, 0, 0, 10);
231 #endif /* MAIL_USE_MMDF */
233 if (indesc < 0)
234 pfatal_with_name (inname);
236 #if defined (BSD) || defined (XENIX)
237 /* In case movemail is setuid to root, make sure the user can
238 read the output file. */
239 /* This is desirable for all systems
240 but I don't want to assume all have the umask system call */
241 umask (umask (0) & 0333);
242 #endif /* BSD or Xenix */
243 outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
244 if (outdesc < 0)
245 pfatal_with_name (outname);
246 #ifdef MAIL_USE_FLOCK
247 #ifdef XENIX
248 if (locking (indesc, LK_RLCK, 0L) < 0) pfatal_with_name (inname);
249 #else
250 if (flock (indesc, LOCK_EX) < 0) pfatal_with_name (inname);
251 #endif
252 #endif /* MAIL_USE_FLOCK */
255 char buf[1024];
257 while (1)
259 nread = read (indesc, buf, sizeof buf);
260 if (nread != write (outdesc, buf, nread))
262 int saved_errno = errno;
263 unlink (outname);
264 errno = saved_errno;
265 pfatal_with_name (outname);
267 if (nread < sizeof buf)
268 break;
272 #ifdef BSD
273 if (fsync (outdesc) < 0)
274 pfatal_and_delete (outname);
275 #endif
277 /* Check to make sure no errors before we zap the inbox. */
278 if (close (outdesc) != 0)
279 pfatal_and_delete (outname);
281 #ifdef MAIL_USE_FLOCK
282 #if defined (STRIDE) || defined (XENIX)
283 /* Stride, xenix have file locking, but no ftruncate. This mess will do. */
284 close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
285 #else
286 ftruncate (indesc, 0L);
287 #endif /* STRIDE or XENIX */
288 #endif /* MAIL_USE_FLOCK */
290 #ifdef MAIL_USE_MMDF
291 lk_close (indesc, 0, 0, 0);
292 #else
293 close (indesc);
294 #endif
296 #ifndef MAIL_USE_FLOCK
297 /* Delete the input file; if we can't, at least get rid of its contents. */
298 #ifdef MAIL_UNLINK_SPOOL
299 /* This is generally bad to do, because it destroys the permissions
300 that were set on the file. Better to just empty the file. */
301 if (unlink (inname) < 0 && errno != ENOENT)
302 #endif /* MAIL_UNLINK_SPOOL */
303 creat (inname, 0600);
304 #ifndef MAIL_USE_MMDF
305 unlink (lockname);
306 #endif /* not MAIL_USE_MMDF */
307 #endif /* not MAIL_USE_FLOCK */
308 exit (0);
311 /* Print error message and exit. */
313 fatal (s1, s2)
314 char *s1, *s2;
316 if (delete_lockname)
317 unlink (delete_lockname);
318 error (s1, s2);
319 exit (1);
322 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
324 error (s1, s2, s3)
325 char *s1, *s2, *s3;
327 printf ("movemail: ");
328 printf (s1, s2, s3);
329 printf ("\n");
332 pfatal_with_name (name)
333 char *name;
335 extern int errno, sys_nerr;
336 extern char *sys_errlist[];
337 char *s;
339 if (errno < sys_nerr)
340 s = concat ("", sys_errlist[errno], " for %s");
341 else
342 s = "cannot open %s";
343 fatal (s, name);
346 pfatal_and_delete (name)
347 char *name;
349 extern int errno, sys_nerr;
350 extern char *sys_errlist[];
351 char *s;
353 if (errno < sys_nerr)
354 s = concat ("", sys_errlist[errno], " for %s");
355 else
356 s = "cannot open %s";
358 unlink (name);
359 fatal (s, name);
362 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
364 char *
365 concat (s1, s2, s3)
366 char *s1, *s2, *s3;
368 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
369 char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
371 strcpy (result, s1);
372 strcpy (result + len1, s2);
373 strcpy (result + len1 + len2, s3);
374 *(result + len1 + len2 + len3) = 0;
376 return result;
379 /* Like malloc but get fatal error if memory is exhausted. */
381 char *
382 xmalloc (size)
383 unsigned size;
385 char *result = malloc (size);
386 if (!result)
387 fatal ("virtual memory exhausted", 0);
388 return result;
391 /* This is the guts of the interface to the Post Office Protocol. */
393 #ifdef MAIL_USE_POP
395 #include <sys/socket.h>
396 #include <netinet/in.h>
397 #include <netdb.h>
398 #include <stdio.h>
399 #include <pwd.h>
401 #ifdef USG
402 #include <fcntl.h>
403 /* Cancel substitutions made by config.h for Emacs. */
404 #undef open
405 #undef read
406 #undef write
407 #undef close
408 #endif /* USG */
410 #define NOTOK (-1)
411 #define OK 0
412 #define DONE 1
414 char *progname;
415 FILE *sfi;
416 FILE *sfo;
417 char Errmsg[80];
419 static int debug = 0;
421 char *get_errmsg ();
422 char *getenv ();
423 int mbx_write ();
425 popmail (user, outfile)
426 char *user;
427 char *outfile;
429 char *host;
430 int nmsgs, nbytes;
431 char response[128];
432 register int i;
433 int mbfi;
434 FILE *mbf;
435 struct passwd *pw = (struct passwd *) getpwuid (getuid ());
436 if (pw == NULL)
437 fatal ("cannot determine user name");
439 host = getenv ("MAILHOST");
440 if (host == NULL)
442 fatal ("no MAILHOST defined");
445 if (pop_init (host) == NOTOK)
447 fatal (Errmsg);
450 if (getline (response, sizeof response, sfi) != OK)
452 fatal (response);
455 if (pop_command ("USER %s", user) == NOTOK
456 || pop_command ("RPOP %s", pw->pw_name) == NOTOK)
458 pop_command ("QUIT");
459 fatal (Errmsg);
462 if (pop_stat (&nmsgs, &nbytes) == NOTOK)
464 pop_command ("QUIT");
465 fatal (Errmsg);
468 if (!nmsgs)
470 pop_command ("QUIT");
471 return 0;
474 mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
475 if (mbfi < 0)
477 pop_command ("QUIT");
478 pfatal_and_delete (outfile);
480 fchown (mbfi, getuid (), -1);
482 if ((mbf = fdopen (mbfi, "w")) == NULL)
484 pop_command ("QUIT");
485 pfatal_and_delete (outfile);
488 for (i = 1; i <= nmsgs; i++)
490 mbx_delimit_begin (mbf);
491 if (pop_retr (i, mbx_write, mbf) != OK)
493 pop_command ("QUIT");
494 close (mbfi);
495 unlink (outfile);
496 fatal (Errmsg);
498 mbx_delimit_end (mbf);
499 fflush (mbf);
502 if (fsync (mbfi) < 0)
504 pop_command ("QUIT");
505 pfatal_and_delete (outfile);
508 if (close (mbfi) == -1)
510 pop_command ("QUIT");
511 pfatal_and_delete (outfile);
514 for (i = 1; i <= nmsgs; i++)
516 if (pop_command ("DELE %d", i) == NOTOK)
518 /* Better to ignore this failure. */
522 pop_command ("QUIT");
523 return (0);
526 pop_init (host)
527 char *host;
529 register struct hostent *hp;
530 register struct servent *sp;
531 int lport = IPPORT_RESERVED - 1;
532 struct sockaddr_in sin;
533 register int s;
535 hp = gethostbyname (host);
536 if (hp == NULL)
538 sprintf (Errmsg, "MAILHOST unknown: %s", host);
539 return NOTOK;
542 sp = getservbyname ("pop", "tcp");
543 if (sp == 0)
545 strcpy (Errmsg, "tcp/pop: unknown service");
546 return NOTOK;
549 sin.sin_family = hp->h_addrtype;
550 bcopy (hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
551 sin.sin_port = sp->s_port;
552 s = rresvport (&lport);
553 if (s < 0)
555 sprintf (Errmsg, "error creating socket: %s", get_errmsg ());
556 return NOTOK;
559 if (connect (s, (char *)&sin, sizeof sin) < 0)
561 sprintf (Errmsg, "error during connect: %s", get_errmsg ());
562 close (s);
563 return NOTOK;
566 sfi = fdopen (s, "r");
567 sfo = fdopen (s, "w");
568 if (sfi == NULL || sfo == NULL)
570 sprintf (Errmsg, "error in fdopen: %s", get_errmsg ());
571 close (s);
572 return NOTOK;
575 return OK;
578 pop_command (fmt, a, b, c, d)
579 char *fmt;
581 char buf[128];
582 char errmsg[64];
584 sprintf (buf, fmt, a, b, c, d);
586 if (debug) fprintf (stderr, "---> %s\n", buf);
587 if (putline (buf, Errmsg, sfo) == NOTOK) return NOTOK;
589 if (getline (buf, sizeof buf, sfi) != OK)
591 strcpy (Errmsg, buf);
592 return NOTOK;
595 if (debug)
596 fprintf (stderr, "<--- %s\n", buf);
597 if (*buf != '+')
599 strcpy (Errmsg, buf);
600 return NOTOK;
602 else
604 return OK;
609 pop_stat (nmsgs, nbytes)
610 int *nmsgs, *nbytes;
612 char buf[128];
614 if (debug)
615 fprintf (stderr, "---> STAT\n");
616 if (putline ("STAT", Errmsg, sfo) == NOTOK)
617 return NOTOK;
619 if (getline (buf, sizeof buf, sfi) != OK)
621 strcpy (Errmsg, buf);
622 return NOTOK;
625 if (debug) fprintf (stderr, "<--- %s\n", buf);
626 if (*buf != '+')
628 strcpy (Errmsg, buf);
629 return NOTOK;
631 else
633 sscanf (buf, "+OK %d %d", nmsgs, nbytes);
634 return OK;
638 pop_retr (msgno, action, arg)
639 int (*action)();
641 char buf[128];
643 sprintf (buf, "RETR %d", msgno);
644 if (debug) fprintf (stderr, "%s\n", buf);
645 if (putline (buf, Errmsg, sfo) == NOTOK) return NOTOK;
647 if (getline (buf, sizeof buf, sfi) != OK)
649 strcpy (Errmsg, buf);
650 return NOTOK;
653 while (1)
655 switch (multiline (buf, sizeof buf, sfi))
657 case OK:
658 (*action)(buf, arg);
659 break;
660 case DONE:
661 return OK;
662 case NOTOK:
663 strcpy (Errmsg, buf);
664 return NOTOK;
669 getline (buf, n, f)
670 char *buf;
671 register int n;
672 FILE *f;
674 register char *p;
675 int c;
677 p = buf;
678 while (--n > 0 && (c = fgetc (f)) != EOF)
679 if ((*p++ = c) == '\n') break;
681 if (ferror (f))
683 strcpy (buf, "error on connection");
684 return NOTOK;
687 if (c == EOF && p == buf)
689 strcpy (buf, "connection closed by foreign host");
690 return DONE;
693 *p = NULL;
694 if (*--p == '\n') *p = NULL;
695 if (*--p == '\r') *p = NULL;
696 return OK;
699 multiline (buf, n, f)
700 char *buf;
701 register int n;
702 FILE *f;
704 if (getline (buf, n, f) != OK)
705 return NOTOK;
706 if (*buf == '.')
708 if (*(buf+1) == NULL)
709 return DONE;
710 else
711 strcpy (buf, buf+1);
713 return OK;
716 char *
717 get_errmsg ()
719 extern int errno, sys_nerr;
720 extern char *sys_errlist[];
721 char *s;
723 if (errno < sys_nerr)
724 s = sys_errlist[errno];
725 else
726 s = "unknown error";
727 return (s);
730 putline (buf, err, f)
731 char *buf;
732 char *err;
733 FILE *f;
735 fprintf (f, "%s\r\n", buf);
736 fflush (f);
737 if (ferror (f))
739 strcpy (err, "lost connection");
740 return NOTOK;
742 return OK;
745 mbx_write (line, mbf)
746 char *line;
747 FILE *mbf;
749 fputs (line, mbf);
750 fputc (0x0a, mbf);
753 mbx_delimit_begin (mbf)
754 FILE *mbf;
756 fputs ("\f\n0, unseen,,\n", mbf);
759 mbx_delimit_end (mbf)
760 FILE *mbf;
762 putc ('\037', mbf);
765 #endif /* MAIL_USE_POP */