(direct_output_forward_char): Use Fget_char_property to test for invisibility.
[emacs.git] / lib-src / movemail.c
blob67dca3ad6a9dda24881effa0d612b34d70b33f58
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 *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>
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_MMDF
79 extern int lk_open (), lk_close ();
80 #endif
82 /* Cancel substitutions made by config.h for Emacs. */
83 #undef open
84 #undef read
85 #undef write
86 #undef close
88 char *concat ();
89 char *xmalloc ();
90 #ifndef errno
91 extern int errno;
92 #endif
94 /* Nonzero means this is name of a lock file to delete on fatal error. */
95 char *delete_lockname;
97 main (argc, argv)
98 int argc;
99 char **argv;
101 char *inname, *outname;
102 int indesc, outdesc;
103 int nread;
104 WAITTYPE status;
106 #ifndef MAIL_USE_FLOCK
107 struct stat st;
108 long now;
109 int tem;
110 char *lockname, *p;
111 char *tempname;
112 int desc;
113 #endif /* not MAIL_USE_FLOCK */
115 delete_lockname = 0;
117 if (argc < 3)
118 fatal ("two arguments required");
120 inname = argv[1];
121 outname = argv[2];
123 #ifdef MAIL_USE_MMDF
124 mmdf_init (argv[0]);
125 #endif
127 /* Check access to output file. */
128 if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
129 pfatal_with_name (outname);
131 /* Also check that outname's directory is writeable to the real uid. */
133 char *buf = (char *) xmalloc (strlen (outname) + 1);
134 char *p, q;
135 strcpy (buf, outname);
136 p = buf + strlen (buf);
137 while (p > buf && p[-1] != '/')
138 *--p = 0;
139 if (p == buf)
140 *p++ = '.';
141 if (access (buf, W_OK) != 0)
142 pfatal_with_name (buf);
143 free (buf);
146 #ifdef MAIL_USE_POP
147 if (!strncmp (inname, "po:", 3))
149 int status; char *user;
151 for (user = &inname[strlen (inname) - 1]; user >= inname; user--)
152 if (*user == ':')
153 break;
155 status = popmail (user, outname);
156 exit (status);
159 setuid (getuid ());
160 #endif /* MAIL_USE_POP */
162 /* Check access to input file. */
163 if (access (inname, R_OK | W_OK) != 0)
164 pfatal_with_name (inname);
166 #ifndef MAIL_USE_MMDF
167 #ifndef MAIL_USE_FLOCK
168 /* Use a lock file named /usr/spool/mail/$USER.lock:
169 If it exists, the mail file is locked. */
170 /* Note: this locking mechanism is *required* by the mailer
171 (on systems which use it) to prevent loss of mail.
173 On systems that use a lock file, extracting the mail without locking
174 WILL occasionally cause loss of mail due to timing errors!
176 So, if creation of the lock file fails
177 due to access permission on /usr/spool/mail,
178 you simply MUST change the permission
179 and/or make movemail a setgid program
180 so it can create lock files properly.
182 You might also wish to verify that your system is one
183 which uses lock files for this purpose. Some systems use other methods.
185 If your system uses the `flock' system call for mail locking,
186 define MAIL_USE_FLOCK in config.h or the s-*.h file
187 and recompile movemail. If the s- file for your system
188 should define MAIL_USE_FLOCK but does not, send a bug report
189 to bug-gnu-emacs@prep.ai.mit.edu so we can fix it. */
191 lockname = concat (inname, ".lock", "");
192 tempname = (char *) xmalloc (strlen (inname) + strlen ("EXXXXXX") + 1);
193 strcpy (tempname, inname);
194 p = tempname + strlen (tempname);
195 while (p != tempname && p[-1] != '/')
196 p--;
197 *p = 0;
198 strcpy (p, "EXXXXXX");
199 mktemp (tempname);
200 unlink (tempname);
202 while (1)
204 /* Create the lock file, but not under the lock file name. */
205 /* Give up if cannot do that. */
206 desc = open (tempname, O_WRONLY | O_CREAT | O_EXCL, 0666);
207 if (desc < 0)
208 pfatal_with_name ("lock file--see source file lib-src/movemail.c");
209 close (desc);
211 tem = link (tempname, lockname);
212 unlink (tempname);
213 if (tem >= 0)
214 break;
215 sleep (1);
217 /* If lock file is a minute old, unlock it. */
218 if (stat (lockname, &st) >= 0)
220 now = time (0);
221 if (st.st_ctime < now - 60)
222 unlink (lockname);
226 delete_lockname = lockname;
227 #endif /* not MAIL_USE_FLOCK */
229 if (fork () == 0)
231 seteuid (getuid ());
233 #ifdef MAIL_USE_FLOCK
234 indesc = open (inname, O_RDWR);
235 #else /* if not MAIL_USE_FLOCK */
236 indesc = open (inname, O_RDONLY);
237 #endif /* not MAIL_USE_FLOCK */
238 #else /* MAIL_USE_MMDF */
239 indesc = lk_open (inname, O_RDONLY, 0, 0, 10);
240 #endif /* MAIL_USE_MMDF */
242 if (indesc < 0)
243 pfatal_with_name (inname);
245 #if defined (BSD) || defined (XENIX)
246 /* In case movemail is setuid to root, make sure the user can
247 read the output file. */
248 /* This is desirable for all systems
249 but I don't want to assume all have the umask system call */
250 umask (umask (0) & 0333);
251 #endif /* BSD or Xenix */
252 outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
253 if (outdesc < 0)
254 pfatal_with_name (outname);
255 #ifdef MAIL_USE_FLOCK
256 #ifdef XENIX
257 if (locking (indesc, LK_RLCK, 0L) < 0) pfatal_with_name (inname);
258 #else
259 if (flock (indesc, LOCK_EX) < 0) pfatal_with_name (inname);
260 #endif
261 #endif /* MAIL_USE_FLOCK */
264 char buf[1024];
266 while (1)
268 nread = read (indesc, buf, sizeof buf);
269 if (nread != write (outdesc, buf, nread))
271 int saved_errno = errno;
272 unlink (outname);
273 errno = saved_errno;
274 pfatal_with_name (outname);
276 if (nread < sizeof buf)
277 break;
281 #ifdef BSD
282 if (fsync (outdesc) < 0)
283 pfatal_and_delete (outname);
284 #endif
286 /* Check to make sure no errors before we zap the inbox. */
287 if (close (outdesc) != 0)
288 pfatal_and_delete (outname);
290 #ifdef MAIL_USE_FLOCK
291 #if defined (STRIDE) || defined (XENIX)
292 /* Stride, xenix have file locking, but no ftruncate. This mess will do. */
293 close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
294 #else
295 ftruncate (indesc, 0L);
296 #endif /* STRIDE or XENIX */
297 #endif /* MAIL_USE_FLOCK */
299 #ifdef MAIL_USE_MMDF
300 lk_close (indesc, 0, 0, 0);
301 #else
302 close (indesc);
303 #endif
305 #ifndef MAIL_USE_FLOCK
306 /* Delete the input file; if we can't, at least get rid of its
307 contents. */
308 #ifdef MAIL_UNLINK_SPOOL
309 /* This is generally bad to do, because it destroys the permissions
310 that were set on the file. Better to just empty the file. */
311 if (unlink (inname) < 0 && errno != ENOENT)
312 #endif /* MAIL_UNLINK_SPOOL */
313 creat (inname, 0600);
314 #endif /* not MAIL_USE_FLOCK */
316 exit (0);
319 wait (&status);
320 if (!WIFEXITED (status))
321 exit (1);
322 else if (WRETCODE (status) != 0)
323 exit (WRETCODE (status));
325 #if !defined (MAIL_USE_MMDF) && !defined (MAIL_USE_FLOCK)
326 unlink (lockname);
327 #endif /* not MAIL_USE_MMDF and not MAIL_USE_FLOCK */
328 exit (0);
331 /* Print error message and exit. */
333 fatal (s1, s2)
334 char *s1, *s2;
336 if (delete_lockname)
337 unlink (delete_lockname);
338 error (s1, s2);
339 exit (1);
342 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
344 error (s1, s2, s3)
345 char *s1, *s2, *s3;
347 printf ("movemail: ");
348 printf (s1, s2, s3);
349 printf ("\n");
352 pfatal_with_name (name)
353 char *name;
355 extern int errno;
356 extern char *strerror ();
357 char *s;
359 s = concat ("", strerror (errno), " for %s");
360 fatal (s, name);
363 pfatal_and_delete (name)
364 char *name;
366 extern int errno;
367 extern char *strerror ();
368 char *s;
370 s = concat ("", strerror (errno), " for %s");
371 unlink (name);
372 fatal (s, name);
375 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
377 char *
378 concat (s1, s2, s3)
379 char *s1, *s2, *s3;
381 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
382 char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
384 strcpy (result, s1);
385 strcpy (result + len1, s2);
386 strcpy (result + len1 + len2, s3);
387 *(result + len1 + len2 + len3) = 0;
389 return result;
392 /* Like malloc but get fatal error if memory is exhausted. */
394 char *
395 xmalloc (size)
396 unsigned size;
398 char *result = (char *) malloc (size);
399 if (!result)
400 fatal ("virtual memory exhausted", 0);
401 return result;
404 /* This is the guts of the interface to the Post Office Protocol. */
406 #ifdef MAIL_USE_POP
408 #include <sys/socket.h>
409 #include <netinet/in.h>
410 #include <netdb.h>
411 #include <stdio.h>
412 #include <pwd.h>
414 #ifdef USG
415 #include <fcntl.h>
416 /* Cancel substitutions made by config.h for Emacs. */
417 #undef open
418 #undef read
419 #undef write
420 #undef close
421 #endif /* USG */
423 #define NOTOK (-1)
424 #define OK 0
425 #define DONE 1
427 char *progname;
428 FILE *sfi;
429 FILE *sfo;
430 char Errmsg[80];
432 static int debug = 0;
434 char *get_errmsg ();
435 char *getenv ();
436 int mbx_write ();
438 popmail (user, outfile)
439 char *user;
440 char *outfile;
442 char *host;
443 int nmsgs, nbytes;
444 char response[128];
445 register int i;
446 int mbfi;
447 FILE *mbf;
448 struct passwd *pw = (struct passwd *) getpwuid (getuid ());
449 if (pw == NULL)
450 fatal ("cannot determine user name");
452 host = getenv ("MAILHOST");
453 if (host == NULL)
455 fatal ("no MAILHOST defined");
458 if (pop_init (host) == NOTOK)
460 fatal (Errmsg);
463 if (getline (response, sizeof response, sfi) != OK)
465 fatal (response);
468 if (pop_command ("USER %s", user) == NOTOK
469 || pop_command ("RPOP %s", pw->pw_name) == NOTOK)
471 pop_command ("QUIT");
472 fatal (Errmsg);
475 if (pop_stat (&nmsgs, &nbytes) == NOTOK)
477 pop_command ("QUIT");
478 fatal (Errmsg);
481 if (!nmsgs)
483 pop_command ("QUIT");
484 return 0;
487 mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
488 if (mbfi < 0)
490 pop_command ("QUIT");
491 pfatal_and_delete (outfile);
493 fchown (mbfi, getuid (), -1);
495 if ((mbf = fdopen (mbfi, "w")) == NULL)
497 pop_command ("QUIT");
498 pfatal_and_delete (outfile);
501 for (i = 1; i <= nmsgs; i++)
503 mbx_delimit_begin (mbf);
504 if (pop_retr (i, mbx_write, mbf) != OK)
506 pop_command ("QUIT");
507 close (mbfi);
508 unlink (outfile);
509 fatal (Errmsg);
511 mbx_delimit_end (mbf);
512 fflush (mbf);
515 if (fsync (mbfi) < 0)
517 pop_command ("QUIT");
518 pfatal_and_delete (outfile);
521 if (close (mbfi) == -1)
523 pop_command ("QUIT");
524 pfatal_and_delete (outfile);
527 for (i = 1; i <= nmsgs; i++)
529 if (pop_command ("DELE %d", i) == NOTOK)
531 /* Better to ignore this failure. */
535 pop_command ("QUIT");
536 return (0);
539 pop_init (host)
540 char *host;
542 register struct hostent *hp;
543 register struct servent *sp;
544 int lport = IPPORT_RESERVED - 1;
545 struct sockaddr_in sin;
546 register int s;
548 hp = gethostbyname (host);
549 if (hp == NULL)
551 sprintf (Errmsg, "MAILHOST unknown: %s", host);
552 return NOTOK;
555 sp = getservbyname ("pop", "tcp");
556 if (sp == 0)
558 strcpy (Errmsg, "tcp/pop: unknown service");
559 return NOTOK;
562 sin.sin_family = hp->h_addrtype;
563 bcopy (hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
564 sin.sin_port = sp->s_port;
565 s = rresvport (&lport);
566 if (s < 0)
568 sprintf (Errmsg, "error creating socket: %s", get_errmsg ());
569 return NOTOK;
572 if (connect (s, (char *)&sin, sizeof sin) < 0)
574 sprintf (Errmsg, "error during connect: %s", get_errmsg ());
575 close (s);
576 return NOTOK;
579 sfi = fdopen (s, "r");
580 sfo = fdopen (s, "w");
581 if (sfi == NULL || sfo == NULL)
583 sprintf (Errmsg, "error in fdopen: %s", get_errmsg ());
584 close (s);
585 return NOTOK;
588 return OK;
591 pop_command (fmt, a, b, c, d)
592 char *fmt;
594 char buf[128];
595 char errmsg[64];
597 sprintf (buf, fmt, a, b, c, d);
599 if (debug) fprintf (stderr, "---> %s\n", buf);
600 if (putline (buf, Errmsg, sfo) == NOTOK) return NOTOK;
602 if (getline (buf, sizeof buf, sfi) != OK)
604 strcpy (Errmsg, buf);
605 return NOTOK;
608 if (debug)
609 fprintf (stderr, "<--- %s\n", buf);
610 if (*buf != '+')
612 strcpy (Errmsg, buf);
613 return NOTOK;
615 else
617 return OK;
622 pop_stat (nmsgs, nbytes)
623 int *nmsgs, *nbytes;
625 char buf[128];
627 if (debug)
628 fprintf (stderr, "---> STAT\n");
629 if (putline ("STAT", Errmsg, sfo) == NOTOK)
630 return NOTOK;
632 if (getline (buf, sizeof buf, sfi) != OK)
634 strcpy (Errmsg, buf);
635 return NOTOK;
638 if (debug) fprintf (stderr, "<--- %s\n", buf);
639 if (*buf != '+')
641 strcpy (Errmsg, buf);
642 return NOTOK;
644 else
646 sscanf (buf, "+OK %d %d", nmsgs, nbytes);
647 return OK;
651 pop_retr (msgno, action, arg)
652 int (*action)();
654 char buf[128];
656 sprintf (buf, "RETR %d", msgno);
657 if (debug) fprintf (stderr, "%s\n", buf);
658 if (putline (buf, Errmsg, sfo) == NOTOK) return NOTOK;
660 if (getline (buf, sizeof buf, sfi) != OK)
662 strcpy (Errmsg, buf);
663 return NOTOK;
666 while (1)
668 switch (multiline (buf, sizeof buf, sfi))
670 case OK:
671 (*action)(buf, arg);
672 break;
673 case DONE:
674 return OK;
675 case NOTOK:
676 strcpy (Errmsg, buf);
677 return NOTOK;
682 getline (buf, n, f)
683 char *buf;
684 register int n;
685 FILE *f;
687 register char *p;
688 int c;
690 p = buf;
691 while (--n > 0 && (c = fgetc (f)) != EOF)
692 if ((*p++ = c) == '\n') break;
694 if (ferror (f))
696 strcpy (buf, "error on connection");
697 return NOTOK;
700 if (c == EOF && p == buf)
702 strcpy (buf, "connection closed by foreign host");
703 return DONE;
706 *p = NULL;
707 if (*--p == '\n') *p = NULL;
708 if (*--p == '\r') *p = NULL;
709 return OK;
712 multiline (buf, n, f)
713 char *buf;
714 register int n;
715 FILE *f;
717 if (getline (buf, n, f) != OK)
718 return NOTOK;
719 if (*buf == '.')
721 if (*(buf+1) == NULL)
722 return DONE;
723 else
724 strcpy (buf, buf+1);
726 return OK;
729 char *
730 get_errmsg ()
732 extern int errno;
733 extern char *strerror ();
734 return strerror (errno);
737 putline (buf, err, f)
738 char *buf;
739 char *err;
740 FILE *f;
742 fprintf (f, "%s\r\n", buf);
743 fflush (f);
744 if (ferror (f))
746 strcpy (err, "lost connection");
747 return NOTOK;
749 return OK;
752 mbx_write (line, mbf)
753 char *line;
754 FILE *mbf;
756 fputs (line, mbf);
757 fputc (0x0a, mbf);
760 mbx_delimit_begin (mbf)
761 FILE *mbf;
763 fputs ("\f\n0, unseen,,\n", mbf);
766 mbx_delimit_end (mbf)
767 FILE *mbf;
769 putc ('\037', mbf);
772 #endif /* MAIL_USE_POP */
774 #ifndef HAVE_STRERROR
775 char *
776 strerror (errnum)
777 int errnum;
779 extern char *sys_errlist[];
780 extern int sys_nerr;
782 if (errnum >= 0 && errnum < sys_nerr)
783 return sys_errlist[errnum];
784 return (char *) "Unknown error";
787 #endif /* ! HAVE_STRERROR */