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