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