1 #ifndef lint 2 static char sccsid[] = "@(#)ftpd.c 4.23 (Berkeley) 06/22/83"; 3 #endif 4 5 /* 6 * FTP server. 7 */ 8 #include <sys/param.h> 9 #include <sys/stat.h> 10 #include <sys/ioctl.h> 11 #include <sys/socket.h> 12 #include <sys/file.h> 13 14 #include <netinet/in.h> 15 16 #include <arpa/ftp.h> 17 #include <arpa/inet.h> 18 19 #include <stdio.h> 20 #include <signal.h> 21 #include <wait.h> 22 #include <pwd.h> 23 #include <setjmp.h> 24 #include <netdb.h> 25 #include <errno.h> 26 27 /* 28 * File containing login names 29 * NOT to be used on this machine. 30 * Commonly used to disallow uucp. 31 */ 32 #define FTPUSERS "/etc/ftpusers" 33 34 extern int errno; 35 extern char *sys_errlist[]; 36 extern char *crypt(); 37 extern char version[]; 38 extern char *home; /* pointer to home directory for glob */ 39 extern FILE *popen(), *fopen(); 40 extern int pclose(), fclose(); 41 42 struct sockaddr_in ctrl_addr; 43 struct sockaddr_in data_source; 44 struct sockaddr_in data_dest; 45 struct sockaddr_in his_addr; 46 47 struct hostent *hp; 48 49 int data; 50 jmp_buf errcatch; 51 int logged_in; 52 struct passwd *pw; 53 int debug; 54 int timeout; 55 int logging; 56 int guest; 57 int type; 58 int form; 59 int stru; /* avoid C keyword */ 60 int mode; 61 int usedefault = 1; /* for data transfers */ 62 char hostname[32]; 63 char remotehost[32]; 64 struct servent *sp; 65 66 /* 67 * Timeout intervals for retrying connections 68 * to hosts that don't accept PORT cmds. This 69 * is a kludge, but given the problems with TCP... 70 */ 71 #define SWAITMAX 90 /* wait at most 90 seconds */ 72 #define SWAITINT 5 /* interval between retries */ 73 74 int swaitmax = SWAITMAX; 75 int swaitint = SWAITINT; 76 77 int lostconn(); 78 int reapchild(); 79 FILE *getdatasock(), *dataconn(); 80 81 main(argc, argv) 82 int argc; 83 char *argv[]; 84 { 85 int ctrl, s, options = 0; 86 char *cp; 87 88 sp = getservbyname("ftp", "tcp"); 89 if (sp == 0) { 90 fprintf(stderr, "ftpd: ftp/tcp: unknown service\n"); 91 exit(1); 92 } 93 ctrl_addr.sin_port = sp->s_port; 94 data_source.sin_port = htons(ntohs(sp->s_port) - 1); 95 signal(SIGPIPE, lostconn); 96 debug = 0; 97 argc--, argv++; 98 while (argc > 0 && *argv[0] == '-') { 99 for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 100 101 case 'v': 102 debug = 1; 103 break; 104 105 case 'd': 106 debug = 1; 107 options |= SO_DEBUG; 108 break; 109 110 case 'l': 111 logging = 1; 112 break; 113 114 case 't': 115 timeout = atoi(++cp); 116 goto nextopt; 117 break; 118 119 default: 120 fprintf(stderr, "Unknown flag -%c ignored.\n", *cp); 121 break; 122 } 123 nextopt: 124 argc--, argv++; 125 } 126 #ifndef DEBUG 127 if (fork()) 128 exit(0); 129 for (s = 0; s < 10; s++) 130 if (!logging || (s != 2)) 131 (void) close(s); 132 (void) open("/", O_RDONLY); 133 (void) dup2(0, 1); 134 if (!logging) 135 (void) dup2(0, 2); 136 { int tt = open("/dev/tty", O_RDWR); 137 if (tt > 0) { 138 ioctl(tt, TIOCNOTTY, 0); 139 close(tt); 140 } 141 } 142 #endif 143 while ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 144 perror("ftpd: socket"); 145 sleep(5); 146 } 147 if (options & SO_DEBUG) 148 if (setsockopt(s, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) 149 perror("ftpd: setsockopt (SO_DEBUG)"); 150 if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, 0, 0) < 0) 151 perror("ftpd: setsockopt (SO_KEEPALIVE)"); 152 while (bind(s, &ctrl_addr, sizeof (ctrl_addr), 0) < 0) { 153 perror("ftpd: bind"); 154 sleep(5); 155 } 156 signal(SIGCHLD, reapchild); 157 listen(s, 10); 158 for (;;) { 159 int hisaddrlen = sizeof (his_addr); 160 161 ctrl = accept(s, &his_addr, &hisaddrlen, 0); 162 if (ctrl < 0) { 163 if (errno == EINTR) 164 continue; 165 perror("ftpd: accept"); 166 continue; 167 } 168 if (fork() == 0) { 169 signal (SIGCHLD, SIG_IGN); 170 dolog(&his_addr); 171 close(s); 172 dup2(ctrl, 0), close(ctrl), dup2(0, 1); 173 /* do telnet option negotiation here */ 174 /* 175 * Set up default state 176 */ 177 logged_in = 0; 178 data = -1; 179 type = TYPE_A; 180 form = FORM_N; 181 stru = STRU_F; 182 mode = MODE_S; 183 (void) getsockname(0, &ctrl_addr, sizeof (ctrl_addr)); 184 gethostname(hostname, sizeof (hostname)); 185 reply(220, "%s FTP server (%s) ready.", 186 hostname, version); 187 for (;;) { 188 setjmp(errcatch); 189 yyparse(); 190 } 191 } 192 close(ctrl); 193 } 194 } 195 196 reapchild() 197 { 198 union wait status; 199 200 while (wait3(&status, WNOHANG, 0) > 0) 201 ; 202 } 203 204 lostconn() 205 { 206 207 fatal("Connection closed."); 208 } 209 210 pass(passwd) 211 char *passwd; 212 { 213 char *xpasswd, *savestr(); 214 static struct passwd save; 215 216 if (logged_in || pw == NULL) { 217 reply(503, "Login with USER first."); 218 return; 219 } 220 if (!guest) { /* "ftp" is only account allowed no password */ 221 xpasswd = crypt(passwd, pw->pw_passwd); 222 if (strcmp(xpasswd, pw->pw_passwd) != 0) { 223 reply(530, "Login incorrect."); 224 pw = NULL; 225 return; 226 } 227 } 228 setegid(pw->pw_gid); 229 initgroups(pw->pw_name, pw->pw_gid); 230 if (chdir(pw->pw_dir)) { 231 reply(550, "User %s: can't change directory to $s.", 232 pw->pw_name, pw->pw_dir); 233 goto bad; 234 } 235 if (guest && chroot(pw->pw_dir) < 0) { 236 reply(550, "Can't set guest privileges."); 237 goto bad; 238 } 239 if (!guest) 240 reply(230, "User %s logged in.", pw->pw_name); 241 else 242 reply(230, "Guest login ok, access restrictions apply."); 243 logged_in = 1; 244 dologin(pw); 245 seteuid(pw->pw_uid); 246 /* 247 * Save everything so globbing doesn't 248 * clobber the fields. 249 */ 250 save = *pw; 251 save.pw_name = savestr(pw->pw_name); 252 save.pw_passwd = savestr(pw->pw_passwd); 253 save.pw_comment = savestr(pw->pw_comment); 254 save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos); 255 save.pw_dir = savestr(pw->pw_dir); 256 save.pw_shell = savestr(pw->pw_shell); 257 pw = &save; 258 home = pw->pw_dir; /* home dir for globbing */ 259 return; 260 bad: 261 seteuid(0); 262 pw = NULL; 263 } 264 265 char * 266 savestr(s) 267 char *s; 268 { 269 char *malloc(); 270 char *new = malloc(strlen(s) + 1); 271 272 if (new != NULL) 273 strcpy(new, s); 274 return (new); 275 } 276 277 retrieve(cmd, name) 278 char *cmd, *name; 279 { 280 FILE *fin, *dout; 281 struct stat st; 282 int (*closefunc)(); 283 284 if (cmd == 0) { 285 #ifdef notdef 286 /* no remote command execution -- it's a security hole */ 287 if (*name == '|') 288 fin = popen(name + 1, "r"), closefunc = pclose; 289 else 290 #endif 291 fin = fopen(name, "r"), closefunc = fclose; 292 } else { 293 char line[BUFSIZ]; 294 295 sprintf(line, cmd, name), name = line; 296 fin = popen(line, "r"), closefunc = pclose; 297 } 298 if (fin == NULL) { 299 if (errno != 0) 300 reply(550, "%s: %s.", name, sys_errlist[errno]); 301 return; 302 } 303 st.st_size = 0; 304 if (cmd == 0 && 305 (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) { 306 reply(550, "%s: not a plain file.", name); 307 goto done; 308 } 309 dout = dataconn(name, st.st_size, "w"); 310 if (dout == NULL) 311 goto done; 312 if (send_data(fin, dout) || ferror(dout)) 313 reply(550, "%s: %s.", name, sys_errlist[errno]); 314 else 315 reply(226, "Transfer complete."); 316 fclose(dout), data = -1; 317 done: 318 (*closefunc)(fin); 319 } 320 321 store(name, mode) 322 char *name, *mode; 323 { 324 FILE *fout, *din; 325 int (*closefunc)(), dochown = 0; 326 327 #ifdef notdef 328 /* no remote command execution -- it's a security hole */ 329 if (name[0] == '|') 330 fout = popen(&name[1], "w"), closefunc = pclose; 331 else 332 #endif 333 { 334 struct stat st; 335 336 if (stat(name, &st) < 0) 337 dochown++; 338 fout = fopen(name, mode), closefunc = fclose; 339 } 340 if (fout == NULL) { 341 reply(550, "%s: %s.", name, sys_errlist[errno]); 342 return; 343 } 344 din = dataconn(name, (off_t)-1, "r"); 345 if (din == NULL) 346 goto done; 347 if (receive_data(din, fout) || ferror(fout)) 348 reply(550, "%s: %s.", name, sys_errlist[errno]); 349 else 350 reply(226, "Transfer complete."); 351 fclose(din), data = -1; 352 done: 353 if (dochown) 354 (void) chown(name, pw->pw_uid, -1); 355 (*closefunc)(fout); 356 } 357 358 FILE * 359 getdatasock(mode) 360 char *mode; 361 { 362 int s; 363 364 if (data >= 0) 365 return (fdopen(data, mode)); 366 s = socket(AF_INET, SOCK_STREAM, 0); 367 if (s < 0) 368 return (NULL); 369 seteuid(0); 370 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0) 371 goto bad; 372 /* anchor socket to avoid multi-homing problems */ 373 data_source.sin_family = AF_INET; 374 data_source.sin_addr = ctrl_addr.sin_addr; 375 if (bind(s, &data_source, sizeof (data_source), 0) < 0) 376 goto bad; 377 seteuid(pw->pw_uid); 378 return (fdopen(s, mode)); 379 bad: 380 seteuid(pw->pw_uid); 381 close(s); 382 return (NULL); 383 } 384 385 FILE * 386 dataconn(name, size, mode) 387 char *name; 388 off_t size; 389 char *mode; 390 { 391 char sizebuf[32]; 392 FILE *file; 393 int retry = 0; 394 395 if (size >= 0) 396 sprintf (sizebuf, " (%ld bytes)", size); 397 else 398 (void) strcpy(sizebuf, ""); 399 if (data >= 0) { 400 reply(125, "Using existing data connection for %s%s.", 401 name, sizebuf); 402 usedefault = 1; 403 return (fdopen(data, mode)); 404 } 405 if (usedefault) 406 data_dest = his_addr; 407 usedefault = 1; 408 file = getdatasock(mode); 409 if (file == NULL) { 410 reply(425, "Can't create data socket (%s,%d): %s.", 411 inet_ntoa(data_source.sin_addr), 412 ntohs(data_source.sin_port), 413 sys_errlist[errno]); 414 return (NULL); 415 } 416 reply(150, "Opening data connection for %s (%s,%d)%s.", 417 name, inet_ntoa(data_dest.sin_addr.s_addr), 418 ntohs(data_dest.sin_port), sizebuf); 419 data = fileno(file); 420 while (connect(data, &data_dest, sizeof (data_dest), 0) < 0) { 421 if (errno == EADDRINUSE && retry < swaitmax) { 422 sleep(swaitint); 423 retry += swaitint; 424 continue; 425 } 426 reply(425, "Can't build data connection: %s.", 427 sys_errlist[errno]); 428 (void) fclose(file); 429 data = -1; 430 return (NULL); 431 } 432 return (file); 433 } 434 435 /* 436 * Tranfer the contents of "instr" to 437 * "outstr" peer using the appropriate 438 * encapulation of the date subject 439 * to Mode, Structure, and Type. 440 * 441 * NB: Form isn't handled. 442 */ 443 send_data(instr, outstr) 444 FILE *instr, *outstr; 445 { 446 register int c; 447 int netfd, filefd, cnt; 448 char buf[BUFSIZ]; 449 450 switch (type) { 451 452 case TYPE_A: 453 while ((c = getc(instr)) != EOF) { 454 if (c == '\n') { 455 if (ferror (outstr)) 456 return (1); 457 putc('\r', outstr); 458 } 459 putc(c, outstr); 460 if (c == '\r') 461 putc ('\0', outstr); 462 } 463 if (ferror (instr) || ferror (outstr)) 464 return (1); 465 return (0); 466 467 case TYPE_I: 468 case TYPE_L: 469 netfd = fileno(outstr); 470 filefd = fileno(instr); 471 472 while ((cnt = read(filefd, buf, sizeof (buf))) > 0) 473 if (write(netfd, buf, cnt) < 0) 474 return (1); 475 return (cnt < 0); 476 } 477 reply(504,"Unimplemented TYPE %d in send_data", type); 478 return (1); 479 } 480 481 /* 482 * Transfer data from peer to 483 * "outstr" using the appropriate 484 * encapulation of the data subject 485 * to Mode, Structure, and Type. 486 * 487 * N.B.: Form isn't handled. 488 */ 489 receive_data(instr, outstr) 490 FILE *instr, *outstr; 491 { 492 register int c; 493 int cnt; 494 char buf[BUFSIZ]; 495 496 497 switch (type) { 498 499 case TYPE_I: 500 case TYPE_L: 501 while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) 502 if (write(fileno(outstr), buf, cnt) < 0) 503 return (1); 504 return (cnt < 0); 505 506 case TYPE_E: 507 reply(504, "TYPE E not implemented."); 508 return (1); 509 510 case TYPE_A: 511 while ((c = getc(instr)) != EOF) { 512 if (c == '\r') { 513 if (ferror (outstr)) 514 return (1); 515 if ((c = getc(instr)) != '\n') 516 putc ('\r', outstr); 517 if (c == '\0') 518 continue; 519 } 520 putc (c, outstr); 521 } 522 if (ferror (instr) || ferror (outstr)) 523 return (1); 524 return (0); 525 } 526 fatal("Unknown type in receive_data."); 527 /*NOTREACHED*/ 528 } 529 530 fatal(s) 531 char *s; 532 { 533 reply(451, "Error in server: %s\n", s); 534 reply(221, "Closing connection due to server error."); 535 dologout(0); 536 } 537 538 reply(n, s, args) 539 int n; 540 char *s; 541 { 542 543 printf("%d ", n); 544 _doprnt(s, &args, stdout); 545 printf("\r\n"); 546 fflush(stdout); 547 if (debug) { 548 fprintf(stderr, "<--- %d ", n); 549 _doprnt(s, &args, stderr); 550 fprintf(stderr, "\n"); 551 fflush(stderr); 552 } 553 } 554 555 lreply(n, s, args) 556 int n; 557 char *s; 558 { 559 printf("%d-", n); 560 _doprnt(s, &args, stdout); 561 printf("\r\n"); 562 fflush(stdout); 563 if (debug) { 564 fprintf(stderr, "<--- %d-", n); 565 _doprnt(s, &args, stderr); 566 fprintf(stderr, "\n"); 567 } 568 } 569 570 replystr(s) 571 char *s; 572 { 573 printf("%s\r\n", s); 574 fflush(stdout); 575 if (debug) 576 fprintf(stderr, "<--- %s\n", s); 577 } 578 579 ack(s) 580 char *s; 581 { 582 reply(200, "%s command okay.", s); 583 } 584 585 nack(s) 586 char *s; 587 { 588 reply(502, "%s command not implemented.", s); 589 } 590 591 yyerror() 592 { 593 reply(500, "Command not understood."); 594 } 595 596 delete(name) 597 char *name; 598 { 599 struct stat st; 600 601 if (stat(name, &st) < 0) { 602 reply(550, "%s: %s.", name, sys_errlist[errno]); 603 return; 604 } 605 if ((st.st_mode&S_IFMT) == S_IFDIR) { 606 if (rmdir(name) < 0) { 607 reply(550, "%s: %s.", name, sys_errlist[errno]); 608 return; 609 } 610 goto done; 611 } 612 if (unlink(name) < 0) { 613 reply(550, "%s: %s.", name, sys_errlist[errno]); 614 return; 615 } 616 done: 617 ack("DELE"); 618 } 619 620 cwd(path) 621 char *path; 622 { 623 624 if (chdir(path) < 0) { 625 reply(550, "%s: %s.", path, sys_errlist[errno]); 626 return; 627 } 628 ack("CWD"); 629 } 630 631 makedir(name) 632 char *name; 633 { 634 struct stat st; 635 int dochown = stat(name, &st) < 0; 636 637 if (mkdir(name, 0777) < 0) { 638 reply(550, "%s: %s.", name, sys_errlist[errno]); 639 return; 640 } 641 if (dochown) 642 (void) chown(name, pw->pw_uid, -1); 643 ack("MKDIR"); 644 } 645 646 removedir(name) 647 char *name; 648 { 649 650 if (rmdir(name) < 0) { 651 reply(550, "%s: %s.", name, sys_errlist[errno]); 652 return; 653 } 654 ack("RMDIR"); 655 } 656 657 pwd() 658 { 659 char path[MAXPATHLEN + 1]; 660 661 if (getwd(path) == NULL) { 662 reply(451, "%s.", path); 663 return; 664 } 665 reply(251, "\"%s\" is current directory.", path); 666 } 667 668 char * 669 renamefrom(name) 670 char *name; 671 { 672 struct stat st; 673 674 if (stat(name, &st) < 0) { 675 reply(550, "%s: %s.", name, sys_errlist[errno]); 676 return ((char *)0); 677 } 678 reply(350, "File exists, ready for destination name"); 679 return (name); 680 } 681 682 renamecmd(from, to) 683 char *from, *to; 684 { 685 686 if (rename(from, to) < 0) { 687 reply(550, "rename: %s.", sys_errlist[errno]); 688 return; 689 } 690 ack("RNTO"); 691 } 692 693 dolog(sin) 694 struct sockaddr_in *sin; 695 { 696 struct hostent *hp = gethostbyaddr(&sin->sin_addr, 697 sizeof (struct in_addr), AF_INET); 698 time_t t; 699 700 if (hp) { 701 strncpy(remotehost, hp->h_name, sizeof (remotehost)); 702 endhostent(); 703 } else 704 strncpy(remotehost, inet_ntoa(sin->sin_addr), 705 sizeof (remotehost)); 706 if (!logging) 707 return; 708 t = time(0); 709 fprintf(stderr,"FTPD: connection from %s at %s", remotehost, ctime(&t)); 710 fflush(stderr); 711 } 712 713 #include <utmp.h> 714 715 #define SCPYN(a, b) strncpy(a, b, sizeof (a)) 716 struct utmp utmp; 717 718 /* 719 * Record login in wtmp file. 720 */ 721 dologin(pw) 722 struct passwd *pw; 723 { 724 int wtmp; 725 char line[32]; 726 727 wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); 728 if (wtmp >= 0) { 729 /* hack, but must be unique and no tty line */ 730 sprintf(line, "ftp%d", getpid()); 731 SCPYN(utmp.ut_line, line); 732 SCPYN(utmp.ut_name, pw->pw_name); 733 SCPYN(utmp.ut_host, remotehost); 734 utmp.ut_time = time(0); 735 (void) write(wtmp, (char *)&utmp, sizeof (utmp)); 736 (void) close(wtmp); 737 } 738 } 739 740 /* 741 * Record logout in wtmp file 742 * and exit with supplied status. 743 */ 744 dologout(status) 745 int status; 746 { 747 int wtmp; 748 749 if (!logged_in) 750 return; 751 seteuid(0); 752 wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND); 753 if (wtmp >= 0) { 754 SCPYN(utmp.ut_name, ""); 755 SCPYN(utmp.ut_host, ""); 756 utmp.ut_time = time(0); 757 (void) write(wtmp, (char *)&utmp, sizeof (utmp)); 758 (void) close(wtmp); 759 } 760 exit(status); 761 } 762 763 /* 764 * Special version of popen which avoids 765 * call to shell. This insures noone may 766 * create a pipe to a hidden program as a side 767 * effect of a list or dir command. 768 */ 769 #define tst(a,b) (*mode == 'r'? (b) : (a)) 770 #define RDR 0 771 #define WTR 1 772 static int popen_pid[5]; 773 774 static char * 775 nextarg(cpp) 776 char *cpp; 777 { 778 register char *cp = cpp; 779 780 if (cp == 0) 781 return (cp); 782 while (*cp && *cp != ' ' && *cp != '\t') 783 cp++; 784 if (*cp == ' ' || *cp == '\t') { 785 *cp++ = '\0'; 786 while (*cp == ' ' || *cp == '\t') 787 cp++; 788 } 789 if (cp == cpp) 790 return ((char *)0); 791 return (cp); 792 } 793 794 FILE * 795 popen(cmd, mode) 796 char *cmd, *mode; 797 { 798 int p[2], ac, gac; 799 register myside, hisside, pid; 800 char *av[20], *gav[512]; 801 register char *cp; 802 803 if (pipe(p) < 0) 804 return (NULL); 805 cp = cmd, ac = 0; 806 /* break up string into pieces */ 807 do { 808 av[ac++] = cp; 809 cp = nextarg(cp); 810 } while (cp && *cp && ac < 20); 811 av[ac] = (char *)0; 812 gav[0] = av[0]; 813 /* glob each piece */ 814 for (gac = ac = 1; av[ac] != NULL; ac++) { 815 char **pop; 816 extern char **glob(); 817 818 pop = glob(av[ac]); 819 if (pop) { 820 av[ac] = (char *)pop; /* save to free later */ 821 while (*pop && gac < 512) 822 gav[gac++] = *pop++; 823 } 824 } 825 gav[gac] = (char *)0; 826 myside = tst(p[WTR], p[RDR]); 827 hisside = tst(p[RDR], p[WTR]); 828 if ((pid = fork()) == 0) { 829 /* myside and hisside reverse roles in child */ 830 close(myside); 831 dup2(hisside, tst(0, 1)); 832 close(hisside); 833 execv(gav[0], gav); 834 _exit(1); 835 } 836 for (ac = 1; av[ac] != NULL; ac++) 837 blkfree((char **)av[ac]); 838 if (pid == -1) 839 return (NULL); 840 popen_pid[myside] = pid; 841 close(hisside); 842 return (fdopen(myside, mode)); 843 } 844 845 pclose(ptr) 846 FILE *ptr; 847 { 848 register f, r, (*hstat)(), (*istat)(), (*qstat)(); 849 int status; 850 851 f = fileno(ptr); 852 fclose(ptr); 853 istat = signal(SIGINT, SIG_IGN); 854 qstat = signal(SIGQUIT, SIG_IGN); 855 hstat = signal(SIGHUP, SIG_IGN); 856 while ((r = wait(&status)) != popen_pid[f] && r != -1) 857 ; 858 if (r == -1) 859 status = -1; 860 signal(SIGINT, istat); 861 signal(SIGQUIT, qstat); 862 signal(SIGHUP, hstat); 863 return (status); 864 } 865 866 /* 867 * Check user requesting login priviledges. 868 * Disallow anyone mentioned in the file FTPUSERS 869 * to allow people such as uucp to be avoided. 870 */ 871 checkuser(name) 872 register char *name; 873 { 874 char line[BUFSIZ], *index(); 875 FILE *fd; 876 int found = 0; 877 878 fd = fopen(FTPUSERS, "r"); 879 if (fd == NULL) 880 return (1); 881 while (fgets(line, sizeof (line), fd) != NULL) { 882 register char *cp = index(line, '\n'); 883 884 if (cp) 885 *cp = '\0'; 886 if (strcmp(line, name) == 0) { 887 found++; 888 break; 889 } 890 } 891 fclose(fd); 892 return (!found); 893 } 894