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 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 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 262 error (s1, s2, s3) 263 char *s1, *s2, *s3; 264 { 265 printf ("movemail: "); 266 printf (s1, s2, s3); 267 printf ("\n"); 268 } 269 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 * 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 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 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 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 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 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 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 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 * 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 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 631 mbx_write(line, mbf) 632 char *line; 633 FILE *mbf; 634 { 635 fputs(line, mbf); 636 fputc(0x0a, mbf); 637 } 638 639 mbx_delimit_begin(mbf) 640 FILE *mbf; 641 { 642 fputs("\f\n0, unseen,,\n", mbf); 643 } 644 645 mbx_delimit_end(mbf) 646 FILE *mbf; 647 { 648 putc('\037', mbf); 649 } 650 651 #endif /* MAIL_USE_POP */ 652