*** empty log message ***
[emacs.git] / lib-src / movemail.c
blob9b216068357c3efec49e84cfe990012d3c7b182d
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.
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 *concat ();
84 extern int errno;
86 /* Nonzero means this is name of a lock file to delete on fatal error. */
87 char *delete_lockname;
89 main (argc, argv)
90 int argc;
91 char **argv;
93 char *inname, *outname;
94 int indesc, outdesc;
95 char buf[1024];
96 int nread;
98 #ifndef MAIL_USE_FLOCK
99 struct stat st;
100 long now;
101 int tem;
102 char *lockname, *p;
103 char tempname[40];
104 int desc;
105 #endif /* not MAIL_USE_FLOCK */
107 delete_lockname = 0;
109 if (argc < 3)
110 fatal ("two arguments required");
112 inname = argv[1];
113 outname = argv[2];
115 #ifdef MAIL_USE_MMDF
116 mmdf_init (argv[0]);
117 #endif
119 /* Check access to output file. */
120 if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
121 pfatal_with_name (outname);
123 /* Also check that outname's directory is writeable to the real uid. */
125 char *buf = (char *) malloc (strlen (outname) + 1);
126 char *p, q;
127 strcpy (buf, outname);
128 p = buf + strlen (buf);
129 while (p > buf && p[-1] != '/')
130 *--p = 0;
131 if (p == buf)
132 *p++ = '.';
133 if (access (buf, W_OK) != 0)
134 pfatal_with_name (buf);
135 free (buf);
138 #ifdef MAIL_USE_POP
139 if (!bcmp (inname, "po:", 3))
141 int status; char *user;
143 user = (char *) rindex (inname, ':') + 1;
144 status = popmail (user, outname);
145 exit (status);
148 setuid (getuid ());
149 #endif /* MAIL_USE_POP */
151 /* Check access to input file. */
152 if (access (inname, R_OK | W_OK) != 0)
153 pfatal_with_name (inname);
155 #ifndef MAIL_USE_MMDF
156 #ifndef MAIL_USE_FLOCK
157 /* Use a lock file named /usr/spool/mail/$USER.lock:
158 If it exists, the mail file is locked. */
159 /* Note: this locking mechanism is *required* by the mailer
160 (on systems which use it) to prevent loss of mail.
162 On systems that use a lock file, extracting the mail without locking
163 WILL occasionally cause loss of mail due to timing errors!
165 So, if creation of the lock file fails
166 due to access permission on /usr/spool/mail,
167 you simply MUST change the permission
168 and/or make movemail a setgid program
169 so it can create lock files properly.
171 You might also wish to verify that your system is one
172 which uses lock files for this purpose. Some systems use other methods.
174 If your system uses the `flock' system call for mail locking,
175 define MAIL_USE_FLOCK in config.h or the s-*.h file
176 and recompile movemail. If the s- file for your system
177 should define MAIL_USE_FLOCK but does not, send a bug report
178 to bug-gnu-emacs@prep.ai.mit.edu so we can fix it. */
180 lockname = concat (inname, ".lock", "");
181 strcpy (tempname, inname);
182 p = tempname + strlen (tempname);
183 while (p != tempname && p[-1] != '/')
184 p--;
185 *p = 0;
186 strcpy (p, "EXXXXXX");
187 mktemp (tempname);
188 unlink (tempname);
190 while (1)
192 /* Create the lock file, but not under the lock file name. */
193 /* Give up if cannot do that. */
194 desc = open (tempname, O_WRONLY | O_CREAT, 0666);
195 if (desc < 0)
196 pfatal_with_name ("lock file--see source file etc/movemail.c");
197 close (desc);
199 tem = link (tempname, lockname);
200 unlink (tempname);
201 if (tem >= 0)
202 break;
203 sleep (1);
205 /* If lock file is a minute old, unlock it. */
206 if (stat (lockname, &st) >= 0)
208 now = time (0);
209 if (st.st_ctime < now - 60)
210 unlink (lockname);
214 delete_lockname = lockname;
215 #endif /* not MAIL_USE_FLOCK */
217 #ifdef MAIL_USE_FLOCK
218 indesc = open (inname, O_RDWR);
219 #else /* if not MAIL_USE_FLOCK */
220 indesc = open (inname, O_RDONLY);
221 #endif /* not MAIL_USE_FLOCK */
222 #else /* MAIL_USE_MMDF */
223 indesc = lk_open (inname, O_RDONLY, 0, 0, 10);
224 #endif /* MAIL_USE_MMDF */
226 if (indesc < 0)
227 pfatal_with_name (inname);
229 #if defined (BSD) || defined (XENIX)
230 /* In case movemail is setuid to root, make sure the user can
231 read the output file. */
232 /* This is desirable for all systems
233 but I don't want to assume all have the umask system call */
234 umask (umask (0) & 0333);
235 #endif /* BSD or Xenix */
236 outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
237 if (outdesc < 0)
238 pfatal_with_name (outname);
239 #ifdef MAIL_USE_FLOCK
240 #ifdef XENIX
241 if (locking (indesc, LK_RLCK, 0L) < 0) pfatal_with_name (inname);
242 #else
243 if (flock (indesc, LOCK_EX) < 0) pfatal_with_name (inname);
244 #endif
245 #endif /* MAIL_USE_FLOCK */
247 while (1)
249 nread = read (indesc, buf, sizeof buf);
250 if (nread != write (outdesc, buf, nread))
252 int saved_errno = errno;
253 unlink (outname);
254 errno = saved_errno;
255 pfatal_with_name (outname);
257 if (nread < sizeof buf)
258 break;
261 #ifdef BSD
262 if (fsync (outdesc) < 0)
263 pfatal_and_delete (outname);
264 #endif
266 /* Check to make sure no errors before we zap the inbox. */
267 if (close (outdesc) != 0)
268 pfatal_and_delete (outname);
270 #ifdef MAIL_USE_FLOCK
271 #if defined (STRIDE) || defined (XENIX)
272 /* Stride, xenix have file locking, but no ftruncate. This mess will do. */
273 close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
274 #else
275 ftruncate (indesc, 0L);
276 #endif /* STRIDE or XENIX */
277 #endif /* MAIL_USE_FLOCK */
279 #ifdef MAIL_USE_MMDF
280 lk_close (indesc, 0, 0, 0);
281 #else
282 close (indesc);
283 #endif
285 #ifndef MAIL_USE_FLOCK
286 /* Delete the input file; if we can't, at least get rid of its contents. */
287 if (unlink (inname) < 0)
288 if (errno != ENOENT)
289 creat (inname, 0666);
290 #ifndef MAIL_USE_MMDF
291 unlink (lockname);
292 #endif /* not MAIL_USE_MMDF */
293 #endif /* not MAIL_USE_FLOCK */
294 exit (0);
297 /* Print error message and exit. */
299 fatal (s1, s2)
300 char *s1, *s2;
302 if (delete_lockname)
303 unlink (delete_lockname);
304 error (s1, s2);
305 exit (1);
308 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
310 error (s1, s2, s3)
311 char *s1, *s2, *s3;
313 printf ("movemail: ");
314 printf (s1, s2, s3);
315 printf ("\n");
318 pfatal_with_name (name)
319 char *name;
321 extern int errno, sys_nerr;
322 extern char *sys_errlist[];
323 char *s;
325 if (errno < sys_nerr)
326 s = concat ("", sys_errlist[errno], " for %s");
327 else
328 s = "cannot open %s";
329 fatal (s, name);
332 pfatal_and_delete (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";
344 unlink (name);
345 fatal (s, name);
348 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
350 char *
351 concat (s1, s2, s3)
352 char *s1, *s2, *s3;
354 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
355 char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
357 strcpy (result, s1);
358 strcpy (result + len1, s2);
359 strcpy (result + len1 + len2, s3);
360 *(result + len1 + len2 + len3) = 0;
362 return result;
365 /* Like malloc but get fatal error if memory is exhausted. */
368 xmalloc (size)
369 int size;
371 int result = malloc (size);
372 if (!result)
373 fatal ("virtual memory exhausted", 0);
374 return result;
377 /* This is the guts of the interface to the Post Office Protocol. */
379 #ifdef MAIL_USE_POP
381 #include <sys/socket.h>
382 #include <netinet/in.h>
383 #include <netdb.h>
384 #include <stdio.h>
386 #ifdef USG
387 #include <fcntl.h>
388 /* Cancel substitutions made by config.h for Emacs. */
389 #undef open
390 #undef read
391 #undef write
392 #undef close
393 #endif /* USG */
395 #define NOTOK (-1)
396 #define OK 0
397 #define DONE 1
399 char *progname;
400 FILE *sfi;
401 FILE *sfo;
402 char Errmsg[80];
404 static int debug = 0;
406 char *get_errmsg ();
407 char *getenv ();
408 int mbx_write ();
410 popmail (user, outfile)
411 char *user;
412 char *outfile;
414 char *host;
415 int nmsgs, nbytes;
416 char response[128];
417 register int i;
418 int mbfi;
419 FILE *mbf;
421 host = getenv ("MAILHOST");
422 if (host == NULL)
424 fatal ("no MAILHOST defined");
427 if (pop_init (host) == NOTOK)
429 fatal (Errmsg);
432 if (getline (response, sizeof response, sfi) != OK)
434 fatal (response);
437 if (pop_command ("USER %s", user) == NOTOK
438 || pop_command ("RPOP %s", user) == NOTOK)
440 pop_command ("QUIT");
441 fatal (Errmsg);
444 if (pop_stat (&nmsgs, &nbytes) == NOTOK)
446 pop_command ("QUIT");
447 fatal (Errmsg);
450 if (!nmsgs)
452 pop_command ("QUIT");
453 return 0;
456 mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
457 if (mbfi < 0)
459 pop_command ("QUIT");
460 pfatal_and_delete (outfile);
462 fchown (mbfi, getuid (), -1);
464 if ((mbf = fdopen (mbfi, "w")) == NULL)
466 pop_command ("QUIT");
467 pfatal_and_delete (outfile);
470 for (i = 1; i <= nmsgs; i++)
472 mbx_delimit_begin (mbf);
473 if (pop_retr (i, mbx_write, mbf) != OK)
475 pop_command ("QUIT");
476 close (mbfi);
477 unlink (outfile);
478 fatal (Errmsg);
480 mbx_delimit_end (mbf);
481 fflush (mbf);
484 if (fsync (mbfi) < 0)
486 pop_command ("QUIT");
487 pfatal_and_delete (outfile);
490 if (close (mbfi) == -1)
492 pop_command ("QUIT");
493 pfatal_and_delete (outfile);
496 for (i = 1; i <= nmsgs; i++)
498 if (pop_command ("DELE %d", i) == NOTOK)
500 /* Better to ignore this failure. */
504 pop_command ("QUIT");
505 return (0);
508 pop_init (host)
509 char *host;
511 register struct hostent *hp;
512 register struct servent *sp;
513 int lport = IPPORT_RESERVED - 1;
514 struct sockaddr_in sin;
515 register int s;
517 hp = gethostbyname (host);
518 if (hp == NULL)
520 sprintf (Errmsg, "MAILHOST unknown: %s", host);
521 return NOTOK;
524 sp = getservbyname ("pop", "tcp");
525 if (sp == 0)
527 strcpy (Errmsg, "tcp/pop: unknown service");
528 return NOTOK;
531 sin.sin_family = hp->h_addrtype;
532 bcopy (hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
533 sin.sin_port = sp->s_port;
534 s = rresvport (&lport);
535 if (s < 0)
537 sprintf (Errmsg, "error creating socket: %s", get_errmsg ());
538 return NOTOK;
541 if (connect (s, (char *)&sin, sizeof sin) < 0)
543 sprintf (Errmsg, "error during connect: %s", get_errmsg ());
544 close (s);
545 return NOTOK;
548 sfi = fdopen (s, "r");
549 sfo = fdopen (s, "w");
550 if (sfi == NULL || sfo == NULL)
552 sprintf (Errmsg, "error in fdopen: %s", get_errmsg ());
553 close (s);
554 return NOTOK;
557 return OK;
560 pop_command (fmt, a, b, c, d)
561 char *fmt;
563 char buf[128];
564 char errmsg[64];
566 sprintf (buf, fmt, a, b, c, d);
568 if (debug) fprintf (stderr, "---> %s\n", buf);
569 if (putline (buf, Errmsg, sfo) == NOTOK) return NOTOK;
571 if (getline (buf, sizeof buf, sfi) != OK)
573 strcpy (Errmsg, buf);
574 return NOTOK;
577 if (debug)
578 fprintf (stderr, "<--- %s\n", buf);
579 if (*buf != '+')
581 strcpy (Errmsg, buf);
582 return NOTOK;
584 else
586 return OK;
591 pop_stat (nmsgs, nbytes)
592 int *nmsgs, *nbytes;
594 char buf[128];
596 if (debug)
597 fprintf (stderr, "---> STAT\n");
598 if (putline ("STAT", Errmsg, sfo) == NOTOK)
599 return NOTOK;
601 if (getline (buf, sizeof buf, sfi) != OK)
603 strcpy (Errmsg, buf);
604 return NOTOK;
607 if (debug) fprintf (stderr, "<--- %s\n", buf);
608 if (*buf != '+')
610 strcpy (Errmsg, buf);
611 return NOTOK;
613 else
615 sscanf (buf, "+OK %d %d", nmsgs, nbytes);
616 return OK;
620 pop_retr (msgno, action, arg)
621 int (*action)();
623 char buf[128];
625 sprintf (buf, "RETR %d", msgno);
626 if (debug) fprintf (stderr, "%s\n", buf);
627 if (putline (buf, Errmsg, sfo) == NOTOK) return NOTOK;
629 if (getline (buf, sizeof buf, sfi) != OK)
631 strcpy (Errmsg, buf);
632 return NOTOK;
635 while (1)
637 switch (multiline (buf, sizeof buf, sfi))
639 case OK:
640 (*action)(buf, arg);
641 break;
642 case DONE:
643 return OK;
644 case NOTOK:
645 strcpy (Errmsg, buf);
646 return NOTOK;
651 getline (buf, n, f)
652 char *buf;
653 register int n;
654 FILE *f;
656 register char *p;
657 int c;
659 p = buf;
660 while (--n > 0 && (c = fgetc (f)) != EOF)
661 if ((*p++ = c) == '\n') break;
663 if (ferror (f))
665 strcpy (buf, "error on connection");
666 return NOTOK;
669 if (c == EOF && p == buf)
671 strcpy (buf, "connection closed by foreign host");
672 return DONE;
675 *p = NULL;
676 if (*--p == '\n') *p = NULL;
677 if (*--p == '\r') *p = NULL;
678 return OK;
681 multiline (buf, n, f)
682 char *buf;
683 register int n;
684 FILE *f;
686 if (getline (buf, n, f) != OK)
687 return NOTOK;
688 if (*buf == '.')
690 if (*(buf+1) == NULL)
691 return DONE;
692 else
693 strcpy (buf, buf+1);
695 return OK;
698 char *
699 get_errmsg ()
701 extern int errno, sys_nerr;
702 extern char *sys_errlist[];
703 char *s;
705 if (errno < sys_nerr)
706 s = sys_errlist[errno];
707 else
708 s = "unknown error";
709 return (s);
712 putline (buf, err, f)
713 char *buf;
714 char *err;
715 FILE *f;
717 fprintf (f, "%s\r\n", buf);
718 fflush (f);
719 if (ferror (f))
721 strcpy (err, "lost connection");
722 return NOTOK;
724 return OK;
727 mbx_write (line, mbf)
728 char *line;
729 FILE *mbf;
731 fputs (line, mbf);
732 fputc (0x0a, mbf);
735 mbx_delimit_begin (mbf)
736 FILE *mbf;
738 fputs ("\f\n0, unseen,,\n", mbf);
741 mbx_delimit_end (mbf)
742 FILE *mbf;
744 putc ('\037', mbf);
747 #endif /* MAIL_USE_POP */