1 /* movemail foo bar -- move file foo to file bar,
2 locking file foo the way /bin/mail respects.
3 Copyright (C) 1986 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 1, 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 /*
22 * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
23 *
24 * Added POP (Post Office Protocol) service. When compiled -DPOP
25 * movemail will accept input filename arguments of the form
26 * "po:username". This will cause movemail to open a connection to
27 * a pop server running on $MAILHOST (environment variable). Movemail
28 * must be setuid to root in order to work with POP.
29 *
30 * New module: popmail.c
31 * Modified routines:
32 * main - added code within #ifdef MAIL_USE_POP; added setuid(getuid())
33 * after POP code.
34 * New routines in movemail.c:
35 * get_errmsg - return pointer to system error message
36 *
37 */
38
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <sys/file.h>
42 #include <errno.h>
43 #include <unistd.h>
44 #define NO_SHORTNAMES /* Tell config not to load remap.h */
45 #include "../src/config.h"
46
47 #ifdef USG
48 #include <fcntl.h>
49 #include <unistd.h>
50 #ifndef F_OK
51 #define F_OK 0
52 #define X_OK 1
53 #define W_OK 2
54 #define R_OK 4
55 #endif
56 #endif /* USG */
57
58 #ifdef XENIX
59 #include <sys/locking.h>
60 #endif
61
62 /* Cancel substitutions made by config.h for Emacs. */
63 #undef open
64 #undef read
65 #undef write
66 #undef close
67
68 char *concat ();
69 extern int errno;
70
71 /* Nonzero means this is name of a lock file to delete on fatal error. */
72 char *delete_lockname;
73
main(argc,argv)74 main (argc, argv)
75 int argc;
76 char **argv;
77 {
78 char *inname, *outname;
79 int indesc, outdesc;
80 char buf[1024];
81 int nread;
82
83 #ifndef MAIL_USE_FLOCK
84 struct stat st;
85 long now;
86 int tem;
87 char *lockname, *p;
88 char tempname[40];
89 int desc;
90 #endif /* not MAIL_USE_FLOCK */
91
92 delete_lockname = 0;
93
94 if (argc < 3)
95 fatal ("two arguments required");
96
97 inname = argv[1];
98 outname = argv[2];
99
100 /* Check access to output file. */
101 if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
102 pfatal_with_name (outname);
103
104 /* Also check that outname's directory is writeable to the real uid. */
105 {
106 char *buf = (char *) malloc (strlen (outname) + 1);
107 char *p, q;
108 strcpy (buf, outname);
109 p = buf + strlen (buf);
110 while (p > buf && p[-1] != '/')
111 *--p = 0;
112 if (p == buf)
113 *p++ = '.';
114 if (access (buf, W_OK) != 0)
115 pfatal_with_name (buf);
116 free (buf);
117 }
118
119 #ifdef MAIL_USE_POP
120 if (!bcmp (inname, "po:", 3))
121 {
122 int status; char *user;
123
124 user = (char *) rindex (inname, ':') + 1;
125 status = popmail (user, outname);
126 exit (status);
127 }
128
129 setuid (getuid());
130 #endif /* MAIL_USE_POP */
131
132 /* Check access to input file. */
133 if (access (inname, R_OK | W_OK) != 0)
134 pfatal_with_name (inname);
135
136 #ifndef MAIL_USE_FLOCK
137 /* Use a lock file named /usr/spool/mail/$USER.lock:
138 If it exists, the mail file is locked. */
139 lockname = concat (inname, ".lock", "");
140 strcpy (tempname, inname);
141 p = tempname + strlen (tempname);
142 while (p != tempname && p[-1] != '/')
143 p--;
144 *p = 0;
145 strcpy (p, "EXXXXXX");
146 mktemp (tempname);
147 (void) unlink (tempname);
148
149 while (1)
150 {
151 /* Create the lock file, but not under the lock file name. */
152 /* Give up if cannot do that. */
153 desc = open (tempname, O_WRONLY | O_CREAT, 0666);
154 if (desc < 0)
155 pfatal_with_name (concat ("temporary file \"", tempname, "\""));
156 close (desc);
157
158 tem = link (tempname, lockname);
159 (void) unlink (tempname);
160 if (tem >= 0)
161 break;
162 sleep (1);
163
164 /* If lock file is a minute old, unlock it. */
165 if (stat (lockname, &st) >= 0)
166 {
167 now = time (0);
168 if (st.st_ctime < now - 60)
169 (void) unlink (lockname);
170 }
171 }
172
173 delete_lockname = lockname;
174 #endif /* not MAIL_USE_FLOCK */
175
176 #ifdef MAIL_USE_FLOCK
177 indesc = open (inname, O_RDWR);
178 #else /* if not MAIL_USE_FLOCK */
179 indesc = open (inname, O_RDONLY);
180 #endif /* not MAIL_USE_FLOCK */
181 if (indesc < 0)
182 pfatal_with_name (inname);
183
184 #if defined(BSD) || defined(XENIX)
185 /* In case movemail is setuid to root, make sure the user can
186 read the output file. */
187 /* This is desirable for all systems
188 but I don't want to assume all have the umask system call */
189 umask (umask (0) & 0333);
190 #endif /* BSD or Xenix */
191 outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
192 if (outdesc < 0)
193 pfatal_with_name (outname);
194 #ifdef MAIL_USE_FLOCK
195 #ifdef XENIX
196 if (locking (indesc, LK_RLCK, 0L) < 0) pfatal_with_name (inname);
197 #else
198 flock (indesc, LOCK_EX);
199 #endif
200 #endif /* MAIL_USE_FLOCK */
201
202 while (1)
203 {
204 nread = read (indesc, buf, sizeof buf);
205 if (nread != write (outdesc, buf, nread))
206 {
207 int saved_errno = errno;
208 (void) unlink (outname);
209 errno = saved_errno;
210 pfatal_with_name (outname);
211 }
212 if (nread < sizeof buf)
213 break;
214 }
215
216 #ifdef BSD
217 fsync (outdesc);
218 #endif
219
220 /* Check to make sure no errors before we zap the inbox. */
221 if (close (outdesc) != 0)
222 {
223 int saved_errno = errno;
224 (void) unlink (outname);
225 errno = saved_errno;
226 pfatal_with_name (outname);
227 }
228
229 #ifdef MAIL_USE_FLOCK
230 #if defined(STRIDE) || defined(XENIX)
231 /* Stride, xenix have file locking, but no ftruncate. This mess will do. */
232 (void) close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
233 #else
234 (void) ftruncate (indesc, (off_t)0);
235 #endif /* STRIDE or XENIX */
236 #endif /* MAIL_USE_FLOCK */
237 close (indesc);
238
239 #ifndef MAIL_USE_FLOCK
240 /* Delete the input file; if we can't, at least get rid of its contents. */
241 if (unlink (inname) < 0)
242 if (errno != ENOENT)
243 creat (inname, 0666);
244 (void) unlink (lockname);
245 #endif /* not MAIL_USE_FLOCK */
246 exit (0);
247 }
248
249 /* Print error message and exit. */
250
fatal(s1,s2)251 fatal (s1, s2)
252 char *s1, *s2;
253 {
254 if (delete_lockname)
255 unlink (delete_lockname);
256 error (s1, s2);
257 exit (1);
258 }
259
260 /* Print error message. `s1' is printf control string, `s2' is arg for it. */
261
error(s1,s2,s3)262 error (s1, s2, s3)
263 char *s1, *s2, *s3;
264 {
265 printf ("movemail: ");
266 printf (s1, s2, s3);
267 printf ("\n");
268 }
269
pfatal_with_name(name)270 pfatal_with_name (name)
271 char *name;
272 {
273 extern int errno, sys_nerr;
274 extern char *sys_errlist[];
275 char *s;
276
277 if (errno < sys_nerr)
278 s = concat ("", sys_errlist[errno], " for %s");
279 else
280 s = "cannot open %s";
281 fatal (s, name);
282 }
283
284 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
285
286 char *
concat(s1,s2,s3)287 concat (s1, s2, s3)
288 char *s1, *s2, *s3;
289 {
290 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
291 char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
292
293 strcpy (result, s1);
294 strcpy (result + len1, s2);
295 strcpy (result + len1 + len2, s3);
296 *(result + len1 + len2 + len3) = 0;
297
298 return result;
299 }
300
301 /* Like malloc but get fatal error if memory is exhausted. */
302
303 int
xmalloc(size)304 xmalloc (size)
305 int size;
306 {
307 int result = malloc (size);
308 if (!result)
309 fatal ("virtual memory exhausted", 0);
310 return result;
311 }
312
313 /* This is the guts of the interface to the Post Office Protocol. */
314
315 #ifdef MAIL_USE_POP
316
317 #include <sys/socket.h>
318 #include <netinet/in.h>
319 #include <netdb.h>
320 #include <stdio.h>
321
322 #ifdef USG
323 #include <fcntl.h>
324 /* Cancel substitutions made by config.h for Emacs. */
325 #undef open
326 #undef read
327 #undef write
328 #undef close
329 #endif /* USG */
330
331 #define NOTOK (-1)
332 #define OK 0
333 #define DONE 1
334
335 char *progname;
336 FILE *sfi;
337 FILE *sfo;
338 char Errmsg[80];
339
340 static int debug = 0;
341
popmail(user,outfile)342 popmail(user, outfile)
343 char *user;
344 char *outfile;
345 {
346 char *host;
347 int nmsgs, nbytes;
348 char response[128];
349 register int i;
350 int mbfi;
351 FILE *mbf;
352 char *getenv();
353 int mbx_write();
354 char *get_errmsg();
355
356 host = getenv("MAILHOST");
357 if (host == NULL) {
358 fatal("no MAILHOST defined");
359 }
360
361 if (pop_init(host) == NOTOK) {
362 error(Errmsg);
363 return(1);
364 }
365
366 if (getline(response, sizeof response, sfi) != OK) {
367 error(response);
368 return(1);
369 }
370
371 if (pop_command("USER %s", user) == NOTOK ||
372 pop_command("RPOP %s", user) == NOTOK) {
373 error(Errmsg);
374 pop_command("QUIT");
375 return(1);
376 }
377
378 if (pop_stat(&nmsgs, &nbytes) == NOTOK) {
379 error(Errmsg);
380 pop_command("QUIT");
381 return(1);
382 }
383
384 if (!nmsgs)
385 {
386 pop_command("QUIT");
387 return(0);
388 }
389
390 mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
391 if (mbfi < 0)
392 {
393 pop_command("QUIT");
394 error("Error in open: %s, %s", get_errmsg(), outfile);
395 return(1);
396 }
397 fchown(mbfi, getuid(), -1);
398
399 if ((mbf = fdopen(mbfi, "w")) == NULL)
400 {
401 pop_command("QUIT");
402 error("Error in fdopen: %s", get_errmsg());
403 close(mbfi);
404 unlink(outfile);
405 return(1);
406 }
407
408 for (i = 1; i <= nmsgs; i++) {
409 mbx_delimit_begin(mbf);
410 if (pop_retr(i, mbx_write, mbf) != OK) {
411 error(Errmsg);
412 pop_command("QUIT");
413 close(mbfi);
414 return(1);
415 }
416 mbx_delimit_end(mbf);
417 fflush(mbf);
418 }
419
420 for (i = 1; i <= nmsgs; i++) {
421 if (pop_command("DELE %d", i) == NOTOK) {
422 error(Errmsg);
423 pop_command("QUIT");
424 close(mbfi);
425 return(1);
426 }
427 }
428
429 pop_command("QUIT");
430 close(mbfi);
431 return(0);
432 }
433
pop_init(host)434 pop_init(host)
435 char *host;
436 {
437 register struct hostent *hp;
438 register struct servent *sp;
439 int lport = IPPORT_RESERVED - 1;
440 struct sockaddr_in sin;
441 register int s;
442 char *get_errmsg();
443
444 hp = gethostbyname(host);
445 if (hp == NULL) {
446 sprintf(Errmsg, "MAILHOST unknown: %s", host);
447 return(NOTOK);
448 }
449
450 sp = getservbyname("pop", "tcp");
451 if (sp == 0) {
452 strcpy(Errmsg, "tcp/pop: unknown service");
453 return(NOTOK);
454 }
455
456 sin.sin_family = hp->h_addrtype;
457 bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
458 sin.sin_port = sp->s_port;
459 s = rresvport(&lport);
460 if (s < 0) {
461 sprintf(Errmsg, "error creating socket: %s", get_errmsg());
462 return(NOTOK);
463 }
464
465 if (connect(s, (char *)&sin, sizeof sin) < 0) {
466 sprintf(Errmsg, "error during connect: %s", get_errmsg());
467 close(s);
468 return(NOTOK);
469 }
470
471 sfi = fdopen(s, "r");
472 sfo = fdopen(s, "w");
473 if (sfi == NULL || sfo == NULL) {
474 sprintf(Errmsg, "error in fdopen: %s", get_errmsg());
475 close(s);
476 return(NOTOK);
477 }
478
479 return(OK);
480 }
481
pop_command(fmt,a,b,c,d)482 pop_command(fmt, a, b, c, d)
483 char *fmt;
484 {
485 char buf[128];
486 char errmsg[64];
487
488 sprintf(buf, fmt, a, b, c, d);
489
490 if (debug) fprintf(stderr, "---> %s\n", buf);
491 if (putline(buf, Errmsg, sfo) == NOTOK) return(NOTOK);
492
493 if (getline(buf, sizeof buf, sfi) != OK) {
494 strcpy(Errmsg, buf);
495 return(NOTOK);
496 }
497
498 if (debug) fprintf(stderr, "<--- %s\n", buf);
499 if (*buf != '+') {
500 strcpy(Errmsg, buf);
501 return(NOTOK);
502 } else {
503 return(OK);
504 }
505 }
506
507
pop_stat(nmsgs,nbytes)508 pop_stat(nmsgs, nbytes)
509 int *nmsgs, *nbytes;
510 {
511 char buf[128];
512
513 if (debug) fprintf(stderr, "---> STAT\n");
514 if (putline("STAT", Errmsg, sfo) == NOTOK) return(NOTOK);
515
516 if (getline(buf, sizeof buf, sfi) != OK) {
517 strcpy(Errmsg, buf);
518 return(NOTOK);
519 }
520
521 if (debug) fprintf(stderr, "<--- %s\n", buf);
522 if (*buf != '+') {
523 strcpy(Errmsg, buf);
524 return(NOTOK);
525 } else {
526 sscanf(buf, "+OK %d %d", nmsgs, nbytes);
527 return(OK);
528 }
529 }
530
531 pop_retr(msgno, action, arg)
532 int (*action)();
533 {
534 char buf[128];
535
536 sprintf(buf, "RETR %d", msgno);
537 if (debug) fprintf(stderr, "%s\n", buf);
538 if (putline(buf, Errmsg, sfo) == NOTOK) return(NOTOK);
539
540 if (getline(buf, sizeof buf, sfi) != OK) {
541 strcpy(Errmsg, buf);
542 return(NOTOK);
543 }
544
545 while (1) {
546 switch (multiline(buf, sizeof buf, sfi)) {
547 case OK:
548 (*action)(buf, arg);
549 break;
550 case DONE:
551 return (OK);
552 case NOTOK:
553 strcpy(Errmsg, buf);
554 return (NOTOK);
555 }
556 }
557 }
558
getline(buf,n,f)559 getline(buf, n, f)
560 char *buf;
561 register int n;
562 FILE *f;
563 {
564 register char *p;
565 int c;
566
567 p = buf;
568 while (--n > 0 && (c = fgetc(f)) != EOF)
569 if ((*p++ = c) == '\n') break;
570
571 if (ferror(f)) {
572 strcpy(buf, "error on connection");
573 return (NOTOK);
574 }
575
576 if (c == EOF && p == buf) {
577 strcpy(buf, "connection closed by foreign host");
578 return (DONE);
579 }
580
581 *p = NULL;
582 if (*--p == '\n') *p = NULL;
583 if (*--p == '\r') *p = NULL;
584 return(OK);
585 }
586
multiline(buf,n,f)587 multiline(buf, n, f)
588 char *buf;
589 register int n;
590 FILE *f;
591 {
592 if (getline(buf, n, f) != OK) return (NOTOK);
593 if (*buf == '.') {
594 if (*(buf+1) == NULL) {
595 return (DONE);
596 } else {
597 strcpy(buf, buf+1);
598 }
599 }
600 return(OK);
601 }
602
603 char *
get_errmsg()604 get_errmsg()
605 {
606 extern int errno, sys_nerr;
607 extern char *sys_errlist[];
608 char *s;
609
610 if (errno < sys_nerr)
611 s = sys_errlist[errno];
612 else
613 s = "unknown error";
614 return(s);
615 }
616
putline(buf,err,f)617 putline(buf, err, f)
618 char *buf;
619 char *err;
620 FILE *f;
621 {
622 fprintf(f, "%s\r\n", buf);
623 fflush(f);
624 if (ferror(f)) {
625 strcpy(err, "lost connection");
626 return(NOTOK);
627 }
628 return(OK);
629 }
630
mbx_write(line,mbf)631 mbx_write(line, mbf)
632 char *line;
633 FILE *mbf;
634 {
635 fputs(line, mbf);
636 fputc(0x0a, mbf);
637 }
638
mbx_delimit_begin(mbf)639 mbx_delimit_begin(mbf)
640 FILE *mbf;
641 {
642 fputs("\f\n0, unseen,,\n", mbf);
643 }
644
mbx_delimit_end(mbf)645 mbx_delimit_end(mbf)
646 FILE *mbf;
647 {
648 putc('\037', mbf);
649 }
650
651 #endif /* MAIL_USE_POP */
652