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