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