Don't include param.h.
[bpt/emacs.git] / 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, 1992, 1993, 1994 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 2, 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 /* 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.
26
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. */
32
33 /*
34 * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
35 *
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.
41 *
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
48 *
49 * Modified August, 1993 by Jonathan Kamens (OpenVision Technologies)
50 *
51 * Move all of the POP code into a separate file, "pop.c".
52 * Use strerror instead of get_errmsg.
53 *
54 */
55
56 #define NO_SHORTNAMES /* Tell config not to load remap.h */
57 #include <../src/config.h>
58 #include <sys/types.h>
59 #include <sys/stat.h>
60 #include <sys/file.h>
61 #include <stdio.h>
62 #include <errno.h>
63 #include <../src/syswait.h>
64 #ifdef MAIL_USE_POP
65 #include "pop.h"
66 #endif
67
68 #ifdef MSDOS
69 #undef access
70 #endif /* MSDOS */
71
72 #ifdef USG
73 #include <fcntl.h>
74 #include <unistd.h>
75 #ifndef F_OK
76 #define F_OK 0
77 #define X_OK 1
78 #define W_OK 2
79 #define R_OK 4
80 #endif
81 #endif /* USG */
82
83 #ifdef HAVE_UNISTD_H
84 #include <unistd.h>
85 #endif
86
87 #ifdef XENIX
88 #include <sys/locking.h>
89 #endif
90
91 #ifdef MAIL_USE_LOCKF
92 #define MAIL_USE_SYSTEM_LOCK
93 #endif
94
95 #ifdef MAIL_USE_FLOCK
96 #define MAIL_USE_SYSTEM_LOCK
97 #endif
98
99 #ifdef MAIL_USE_MMDF
100 extern int lk_open (), lk_close ();
101 #endif
102
103 /* Cancel substitutions made by config.h for Emacs. */
104 #undef open
105 #undef read
106 #undef write
107 #undef close
108
109 #ifndef errno
110 extern int errno;
111 #endif
112 char *strerror ();
113
114 void fatal ();
115 void error ();
116 void pfatal_with_name ();
117 void pfatal_and_delete ();
118 char *concat ();
119 char *xmalloc ();
120 int popmail ();
121 int pop_retr ();
122 int mbx_write ();
123 int mbx_delimit_begin ();
124 int mbx_delimit_end ();
125
126 /* Nonzero means this is name of a lock file to delete on fatal error. */
127 char *delete_lockname;
128
129 int
130 main (argc, argv)
131 int argc;
132 char **argv;
133 {
134 char *inname, *outname;
135 int indesc, outdesc;
136 int nread;
137 WAITTYPE status;
138
139 #ifndef MAIL_USE_SYSTEM_LOCK
140 struct stat st;
141 long now;
142 int tem;
143 char *lockname, *p;
144 char *tempname;
145 int desc;
146 #endif /* not MAIL_USE_SYSTEM_LOCK */
147
148 delete_lockname = 0;
149
150 if (argc < 3)
151 {
152 fprintf (stderr, "Usage: movemail inbox destfile");
153 exit(1);
154 }
155
156 inname = argv[1];
157 outname = argv[2];
158
159 #ifdef MAIL_USE_MMDF
160 mmdf_init (argv[0]);
161 #endif
162
163 /* Check access to output file. */
164 if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
165 pfatal_with_name (outname);
166
167 /* Also check that outname's directory is writeable to the real uid. */
168 {
169 char *buf = (char *) xmalloc (strlen (outname) + 1);
170 char *p;
171 strcpy (buf, outname);
172 p = buf + strlen (buf);
173 while (p > buf && p[-1] != '/')
174 *--p = 0;
175 if (p == buf)
176 *p++ = '.';
177 if (access (buf, W_OK) != 0)
178 pfatal_with_name (buf);
179 free (buf);
180 }
181
182 #ifdef MAIL_USE_POP
183 if (!strncmp (inname, "po:", 3))
184 {
185 int status; char *user;
186
187 for (user = &inname[strlen (inname) - 1]; user >= inname; user--)
188 if (*user == ':')
189 break;
190
191 status = popmail (user, outname);
192 exit (status);
193 }
194
195 setuid (getuid ());
196 #endif /* MAIL_USE_POP */
197
198 /* Check access to input file. */
199 if (access (inname, R_OK | W_OK) != 0)
200 pfatal_with_name (inname);
201
202 #ifndef MAIL_USE_MMDF
203 #ifndef MAIL_USE_SYSTEM_LOCK
204 /* Use a lock file named /usr/spool/mail/$USER.lock:
205 If it exists, the mail file is locked. */
206 /* Note: this locking mechanism is *required* by the mailer
207 (on systems which use it) to prevent loss of mail.
208
209 On systems that use a lock file, extracting the mail without locking
210 WILL occasionally cause loss of mail due to timing errors!
211
212 So, if creation of the lock file fails
213 due to access permission on /usr/spool/mail,
214 you simply MUST change the permission
215 and/or make movemail a setgid program
216 so it can create lock files properly.
217
218 You might also wish to verify that your system is one
219 which uses lock files for this purpose. Some systems use other methods.
220
221 If your system uses the `flock' system call for mail locking,
222 define MAIL_USE_SYSTEM_LOCK in config.h or the s-*.h file
223 and recompile movemail. If the s- file for your system
224 should define MAIL_USE_SYSTEM_LOCK but does not, send a bug report
225 to bug-gnu-emacs@prep.ai.mit.edu so we can fix it. */
226
227 lockname = concat (inname, ".lock", "");
228 tempname = (char *) xmalloc (strlen (inname) + strlen ("EXXXXXX") + 1);
229 strcpy (tempname, inname);
230 p = tempname + strlen (tempname);
231 while (p != tempname && p[-1] != '/')
232 p--;
233 *p = 0;
234 strcpy (p, "EXXXXXX");
235 mktemp (tempname);
236 unlink (tempname);
237
238 while (1)
239 {
240 /* Create the lock file, but not under the lock file name. */
241 /* Give up if cannot do that. */
242 desc = open (tempname, O_WRONLY | O_CREAT | O_EXCL, 0666);
243 if (desc < 0)
244 pfatal_with_name ("lock file--see source file lib-src/movemail.c");
245 close (desc);
246
247 tem = link (tempname, lockname);
248 unlink (tempname);
249 if (tem >= 0)
250 break;
251 sleep (1);
252
253 /* If lock file is a minute old, unlock it. */
254 if (stat (lockname, &st) >= 0)
255 {
256 now = time (0);
257 if (st.st_ctime < now - 60)
258 unlink (lockname);
259 }
260 }
261
262 delete_lockname = lockname;
263 #endif /* not MAIL_USE_SYSTEM_LOCK */
264 #endif /* not MAIL_USE_MMDF */
265
266 if (fork () == 0)
267 {
268 setuid (getuid ());
269
270 #ifndef MAIL_USE_MMDF
271 #ifdef MAIL_USE_SYSTEM_LOCK
272 indesc = open (inname, O_RDWR);
273 #else /* if not MAIL_USE_SYSTEM_LOCK */
274 indesc = open (inname, O_RDONLY);
275 #endif /* not MAIL_USE_SYSTEM_LOCK */
276 #else /* MAIL_USE_MMDF */
277 indesc = lk_open (inname, O_RDONLY, 0, 0, 10);
278 #endif /* MAIL_USE_MMDF */
279
280 if (indesc < 0)
281 pfatal_with_name (inname);
282
283 #if defined (BSD) || defined (XENIX)
284 /* In case movemail is setuid to root, make sure the user can
285 read the output file. */
286 /* This is desirable for all systems
287 but I don't want to assume all have the umask system call */
288 umask (umask (0) & 0333);
289 #endif /* BSD or Xenix */
290 outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
291 if (outdesc < 0)
292 pfatal_with_name (outname);
293 #ifdef MAIL_USE_SYSTEM_LOCK
294 #ifdef MAIL_USE_LOCKF
295 if (lockf (indesc, F_LOCK, 0) < 0) pfatal_with_name (inname);
296 #else /* not MAIL_USE_LOCKF */
297 #ifdef XENIX
298 if (locking (indesc, LK_RLCK, 0L) < 0) pfatal_with_name (inname);
299 #else
300 if (flock (indesc, LOCK_EX) < 0) pfatal_with_name (inname);
301 #endif
302 #endif /* not MAIL_USE_LOCKF */
303 #endif /* MAIL_USE_SYSTEM_LOCK */
304
305 {
306 char buf[1024];
307
308 while (1)
309 {
310 nread = read (indesc, buf, sizeof buf);
311 if (nread != write (outdesc, buf, nread))
312 {
313 int saved_errno = errno;
314 unlink (outname);
315 errno = saved_errno;
316 pfatal_with_name (outname);
317 }
318 if (nread < sizeof buf)
319 break;
320 }
321 }
322
323 #ifdef BSD
324 if (fsync (outdesc) < 0)
325 pfatal_and_delete (outname);
326 #endif
327
328 /* Check to make sure no errors before we zap the inbox. */
329 if (close (outdesc) != 0)
330 pfatal_and_delete (outname);
331
332 #ifdef MAIL_USE_SYSTEM_LOCK
333 #if defined (STRIDE) || defined (XENIX)
334 /* Stride, xenix have file locking, but no ftruncate. This mess will do. */
335 close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
336 #else
337 ftruncate (indesc, 0L);
338 #endif /* STRIDE or XENIX */
339 #endif /* MAIL_USE_SYSTEM_LOCK */
340
341 #ifdef MAIL_USE_MMDF
342 lk_close (indesc, 0, 0, 0);
343 #else
344 close (indesc);
345 #endif
346
347 #ifndef MAIL_USE_SYSTEM_LOCK
348 /* Delete the input file; if we can't, at least get rid of its
349 contents. */
350 #ifdef MAIL_UNLINK_SPOOL
351 /* This is generally bad to do, because it destroys the permissions
352 that were set on the file. Better to just empty the file. */
353 if (unlink (inname) < 0 && errno != ENOENT)
354 #endif /* MAIL_UNLINK_SPOOL */
355 creat (inname, 0600);
356 #endif /* not MAIL_USE_SYSTEM_LOCK */
357
358 exit (0);
359 }
360
361 wait (&status);
362 if (!WIFEXITED (status))
363 exit (1);
364 else if (WRETCODE (status) != 0)
365 exit (WRETCODE (status));
366
367 #if !defined (MAIL_USE_MMDF) && !defined (MAIL_USE_SYSTEM_LOCK)
368 unlink (lockname);
369 #endif /* not MAIL_USE_MMDF and not MAIL_USE_SYSTEM_LOCK */
370 return 0;
371 }
372 \f
373 /* Print error message and exit. */
374
375 void
376 fatal (s1, s2)
377 char *s1, *s2;
378 {
379 if (delete_lockname)
380 unlink (delete_lockname);
381 error (s1, s2);
382 exit (1);
383 }
384
385 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
386
387 void
388 error (s1, s2, s3)
389 char *s1, *s2, *s3;
390 {
391 fprintf (stderr, "movemail: ");
392 fprintf (stderr, s1, s2, s3);
393 fprintf (stderr, "\n");
394 }
395
396 void
397 pfatal_with_name (name)
398 char *name;
399 {
400 char *s = concat ("", strerror (errno), " for %s");
401 fatal (s, name);
402 }
403
404 void
405 pfatal_and_delete (name)
406 char *name;
407 {
408 char *s = concat ("", strerror (errno), " for %s");
409 unlink (name);
410 fatal (s, name);
411 }
412
413 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
414
415 char *
416 concat (s1, s2, s3)
417 char *s1, *s2, *s3;
418 {
419 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
420 char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
421
422 strcpy (result, s1);
423 strcpy (result + len1, s2);
424 strcpy (result + len1 + len2, s3);
425 *(result + len1 + len2 + len3) = 0;
426
427 return result;
428 }
429
430 /* Like malloc but get fatal error if memory is exhausted. */
431
432 char *
433 xmalloc (size)
434 unsigned size;
435 {
436 char *result = (char *) malloc (size);
437 if (!result)
438 fatal ("virtual memory exhausted", 0);
439 return result;
440 }
441 \f
442 /* This is the guts of the interface to the Post Office Protocol. */
443
444 #ifdef MAIL_USE_POP
445
446 #include <sys/socket.h>
447 #include <netinet/in.h>
448 #include <netdb.h>
449 #include <stdio.h>
450 #include <pwd.h>
451
452 #ifdef USG
453 #include <fcntl.h>
454 /* Cancel substitutions made by config.h for Emacs. */
455 #undef open
456 #undef read
457 #undef write
458 #undef close
459 #endif /* USG */
460
461 #define NOTOK (-1)
462 #define OK 0
463 #define DONE 1
464
465 char *progname;
466 FILE *sfi;
467 FILE *sfo;
468 char ibuffer[BUFSIZ];
469 char obuffer[BUFSIZ];
470 char Errmsg[80];
471
472 popmail (user, outfile)
473 char *user;
474 char *outfile;
475 {
476 int nmsgs, nbytes;
477 register int i;
478 int mbfi;
479 FILE *mbf;
480 char *getenv ();
481 int mbx_write ();
482 popserver server;
483 extern char *strerror ();
484
485 server = pop_open (0, user, 0, POP_NO_GETPASS);
486 if (! server)
487 {
488 error (pop_error);
489 return (1);
490 }
491
492 if (pop_stat (server, &nmsgs, &nbytes))
493 {
494 error (pop_error);
495 return (1);
496 }
497
498 if (!nmsgs)
499 {
500 pop_close (server);
501 return (0);
502 }
503
504 mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
505 if (mbfi < 0)
506 {
507 pop_close (server);
508 error ("Error in open: %s, %s", strerror (errno), outfile);
509 return (1);
510 }
511 fchown (mbfi, getuid (), -1);
512
513 if ((mbf = fdopen (mbfi, "w")) == NULL)
514 {
515 pop_close (server);
516 error ("Error in fdopen: %s", strerror (errno));
517 close (mbfi);
518 unlink (outfile);
519 return (1);
520 }
521
522 for (i = 1; i <= nmsgs; i++)
523 {
524 mbx_delimit_begin (mbf);
525 if (pop_retr (server, i, mbx_write, mbf) != OK)
526 {
527 error (Errmsg);
528 close (mbfi);
529 return (1);
530 }
531 mbx_delimit_end (mbf);
532 fflush (mbf);
533 if (ferror (mbf))
534 {
535 error ("Error in fflush: %s", strerror (errno));
536 pop_close (server);
537 close (mbfi);
538 return (1);
539 }
540 }
541
542 /* On AFS, a call to write only modifies the file in the local
543 * workstation's AFS cache. The changes are not written to the server
544 * until a call to fsync or close is made. Users with AFS home
545 * directories have lost mail when over quota because these checks were
546 * not made in previous versions of movemail. */
547
548 #ifdef BSD
549 if (fsync (mbfi) < 0)
550 {
551 error ("Error in fsync: %s", strerror (errno));
552 return (1);
553 }
554 #endif
555
556 if (close (mbfi) == -1)
557 {
558 error ("Error in close: %s", strerror (errno));
559 return (1);
560 }
561
562 for (i = 1; i <= nmsgs; i++)
563 {
564 if (pop_delete (server, i))
565 {
566 error (pop_error);
567 pop_close (server);
568 return (1);
569 }
570 }
571
572 if (pop_quit (server))
573 {
574 error (pop_error);
575 return (1);
576 }
577
578 return (0);
579 }
580
581 pop_retr (server, msgno, action, arg)
582 popserver server;
583 int (*action)();
584 {
585 extern char *strerror ();
586 char *line;
587 int ret;
588
589 if (pop_retrieve_first (server, msgno, &line))
590 {
591 strncpy (Errmsg, pop_error, sizeof (Errmsg));
592 Errmsg[sizeof (Errmsg)-1] = '\0';
593 return (NOTOK);
594 }
595
596 while (! (ret = pop_retrieve_next (server, &line)))
597 {
598 if (! line)
599 break;
600
601 if ((*action)(line, arg) != OK)
602 {
603 strcpy (Errmsg, strerror (errno));
604 pop_close (server);
605 return (NOTOK);
606 }
607 }
608
609 if (ret)
610 {
611 strncpy (Errmsg, pop_error, sizeof (Errmsg));
612 Errmsg[sizeof (Errmsg)-1] = '\0';
613 return (NOTOK);
614 }
615
616 return (OK);
617 }
618
619 /* Do this as a macro instead of using strcmp to save on execution time. */
620 #define IS_FROM_LINE(a) ((a[0] == 'F') \
621 && (a[1] == 'r') \
622 && (a[2] == 'o') \
623 && (a[3] == 'm') \
624 && (a[4] == ' '))
625
626 int
627 mbx_write (line, mbf)
628 char *line;
629 FILE *mbf;
630 {
631 if (IS_FROM_LINE (line))
632 {
633 if (fputc ('>', mbf) == EOF)
634 return (NOTOK);
635 }
636 if (fputs (line, mbf) == EOF)
637 return (NOTOK);
638 if (fputc (0x0a, mbf) == EOF)
639 return (NOTOK);
640 return (OK);
641 }
642
643 int
644 mbx_delimit_begin (mbf)
645 FILE *mbf;
646 {
647 if (fputs ("\f\n0, unseen,,\n", mbf) == EOF)
648 return (NOTOK);
649 return (OK);
650 }
651
652 mbx_delimit_end (mbf)
653 FILE *mbf;
654 {
655 if (putc ('\037', mbf) == EOF)
656 return (NOTOK);
657 return (OK);
658 }
659
660 #endif /* MAIL_USE_POP */
661 \f
662 #ifndef HAVE_STRERROR
663 char *
664 strerror (errnum)
665 int errnum;
666 {
667 extern char *sys_errlist[];
668 extern int sys_nerr;
669
670 if (errnum >= 0 && errnum < sys_nerr)
671 return sys_errlist[errnum];
672 return (char *) "Unknown error";
673 }
674
675 #endif /* ! HAVE_STRERROR */