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