|
|
1.1 root 1: #ifndef lint
2: static char sccsid[] = "@(#)ftpd.c 4.28 (Berkeley) 9/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: #include <sys/wait.h>
14:
15: #include <netinet/in.h>
16:
17: #include <arpa/ftp.h>
18: #include <arpa/inet.h>
19:
20: #include <stdio.h>
21: #include <signal.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: if (debug)
208: fprintf(stderr, "Lost connection.\n");
209: dologout(-1);
210: }
211:
212: pass(passwd)
213: char *passwd;
214: {
215: char *xpasswd, *savestr();
216: static struct passwd save;
217:
218: if (logged_in || pw == NULL) {
219: reply(503, "Login with USER first.");
220: return;
221: }
222: if (!guest) { /* "ftp" is only account allowed no password */
223: xpasswd = crypt(passwd, pw->pw_passwd);
224: if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) {
225: reply(530, "Login incorrect.");
226: pw = NULL;
227: return;
228: }
229: }
230: setegid(pw->pw_gid);
231: initgroups(pw->pw_name, pw->pw_gid);
232: if (chdir(pw->pw_dir)) {
233: reply(550, "User %s: can't change directory to $s.",
234: pw->pw_name, pw->pw_dir);
235: goto bad;
236: }
237: if (guest && chroot(pw->pw_dir) < 0) {
238: reply(550, "Can't set guest privileges.");
239: goto bad;
240: }
241: if (!guest)
242: reply(230, "User %s logged in.", pw->pw_name);
243: else
244: reply(230, "Guest login ok, access restrictions apply.");
245: logged_in = 1;
246: dologin(pw);
247: seteuid(pw->pw_uid);
248: /*
249: * Save everything so globbing doesn't
250: * clobber the fields.
251: */
252: save = *pw;
253: save.pw_name = savestr(pw->pw_name);
254: save.pw_passwd = savestr(pw->pw_passwd);
255: save.pw_comment = savestr(pw->pw_comment);
256: save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos);
257: save.pw_dir = savestr(pw->pw_dir);
258: save.pw_shell = savestr(pw->pw_shell);
259: pw = &save;
260: home = pw->pw_dir; /* home dir for globbing */
261: return;
262: bad:
263: seteuid(0);
264: pw = NULL;
265: }
266:
267: char *
268: savestr(s)
269: char *s;
270: {
271: char *malloc();
272: char *new = malloc(strlen(s) + 1);
273:
274: if (new != NULL)
275: strcpy(new, s);
276: return (new);
277: }
278:
279: retrieve(cmd, name)
280: char *cmd, *name;
281: {
282: FILE *fin, *dout;
283: struct stat st;
284: int (*closefunc)();
285:
286: if (cmd == 0) {
287: #ifdef notdef
288: /* no remote command execution -- it's a security hole */
289: if (*name == '|')
290: fin = popen(name + 1, "r"), closefunc = pclose;
291: else
292: #endif
293: fin = fopen(name, "r"), closefunc = fclose;
294: } else {
295: char line[BUFSIZ];
296:
297: sprintf(line, cmd, name), name = line;
298: fin = popen(line, "r"), closefunc = pclose;
299: }
300: if (fin == NULL) {
301: if (errno != 0)
302: reply(550, "%s: %s.", name, sys_errlist[errno]);
303: return;
304: }
305: st.st_size = 0;
306: if (cmd == 0 &&
307: (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
308: reply(550, "%s: not a plain file.", name);
309: goto done;
310: }
311: dout = dataconn(name, st.st_size, "w");
312: if (dout == NULL)
313: goto done;
314: if (send_data(fin, dout) || ferror(dout))
315: reply(550, "%s: %s.", name, sys_errlist[errno]);
316: else
317: reply(226, "Transfer complete.");
318: fclose(dout), data = -1;
319: done:
320: (*closefunc)(fin);
321: }
322:
323: store(name, mode)
324: char *name, *mode;
325: {
326: FILE *fout, *din;
327: int (*closefunc)(), dochown = 0;
328:
329: #ifdef notdef
330: /* no remote command execution -- it's a security hole */
331: if (name[0] == '|')
332: fout = popen(&name[1], "w"), closefunc = pclose;
333: else
334: #endif
335: {
336: struct stat st;
337:
338: if (stat(name, &st) < 0)
339: dochown++;
340: fout = fopen(name, mode), closefunc = fclose;
341: }
342: if (fout == NULL) {
343: reply(550, "%s: %s.", name, sys_errlist[errno]);
344: return;
345: }
346: din = dataconn(name, (off_t)-1, "r");
347: if (din == NULL)
348: goto done;
349: if (receive_data(din, fout) || ferror(fout))
350: reply(550, "%s: %s.", name, sys_errlist[errno]);
351: else
352: reply(226, "Transfer complete.");
353: fclose(din), data = -1;
354: done:
355: if (dochown)
356: (void) chown(name, pw->pw_uid, -1);
357: (*closefunc)(fout);
358: }
359:
360: FILE *
361: getdatasock(mode)
362: char *mode;
363: {
364: int s;
365:
366: if (data >= 0)
367: return (fdopen(data, mode));
368: s = socket(AF_INET, SOCK_STREAM, 0);
369: if (s < 0)
370: return (NULL);
371: seteuid(0);
372: if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0)
373: goto bad;
374: /* anchor socket to avoid multi-homing problems */
375: data_source.sin_family = AF_INET;
376: data_source.sin_addr = ctrl_addr.sin_addr;
377: if (bind(s, &data_source, sizeof (data_source), 0) < 0)
378: goto bad;
379: seteuid(pw->pw_uid);
380: return (fdopen(s, mode));
381: bad:
382: seteuid(pw->pw_uid);
383: close(s);
384: return (NULL);
385: }
386:
387: FILE *
388: dataconn(name, size, mode)
389: char *name;
390: off_t size;
391: char *mode;
392: {
393: char sizebuf[32];
394: FILE *file;
395: int retry = 0;
396:
397: if (size >= 0)
398: sprintf (sizebuf, " (%ld bytes)", size);
399: else
400: (void) strcpy(sizebuf, "");
401: if (data >= 0) {
402: reply(125, "Using existing data connection for %s%s.",
403: name, sizebuf);
404: usedefault = 1;
405: return (fdopen(data, mode));
406: }
407: if (usedefault)
408: data_dest = his_addr;
409: usedefault = 1;
410: file = getdatasock(mode);
411: if (file == NULL) {
412: reply(425, "Can't create data socket (%s,%d): %s.",
413: inet_ntoa(data_source.sin_addr),
414: ntohs(data_source.sin_port),
415: sys_errlist[errno]);
416: return (NULL);
417: }
418: reply(150, "Opening data connection for %s (%s,%d)%s.",
419: name, inet_ntoa(data_dest.sin_addr.s_addr),
420: ntohs(data_dest.sin_port), sizebuf);
421: data = fileno(file);
422: while (connect(data, &data_dest, sizeof (data_dest), 0) < 0) {
423: if (errno == EADDRINUSE && retry < swaitmax) {
424: sleep(swaitint);
425: retry += swaitint;
426: continue;
427: }
428: reply(425, "Can't build data connection: %s.",
429: sys_errlist[errno]);
430: (void) fclose(file);
431: data = -1;
432: return (NULL);
433: }
434: return (file);
435: }
436:
437: /*
438: * Tranfer the contents of "instr" to
439: * "outstr" peer using the appropriate
440: * encapulation of the date subject
441: * to Mode, Structure, and Type.
442: *
443: * NB: Form isn't handled.
444: */
445: send_data(instr, outstr)
446: FILE *instr, *outstr;
447: {
448: register int c;
449: int netfd, filefd, cnt;
450: char buf[BUFSIZ];
451:
452: switch (type) {
453:
454: case TYPE_A:
455: while ((c = getc(instr)) != EOF) {
456: if (c == '\n') {
457: if (ferror (outstr))
458: return (1);
459: putc('\r', outstr);
460: }
461: putc(c, outstr);
462: if (c == '\r')
463: putc ('\0', outstr);
464: }
465: if (ferror (instr) || ferror (outstr))
466: return (1);
467: return (0);
468:
469: case TYPE_I:
470: case TYPE_L:
471: netfd = fileno(outstr);
472: filefd = fileno(instr);
473:
474: while ((cnt = read(filefd, buf, sizeof (buf))) > 0)
475: if (write(netfd, buf, cnt) < 0)
476: return (1);
477: return (cnt < 0);
478: }
479: reply(504,"Unimplemented TYPE %d in send_data", type);
480: return (1);
481: }
482:
483: /*
484: * Transfer data from peer to
485: * "outstr" using the appropriate
486: * encapulation of the data subject
487: * to Mode, Structure, and Type.
488: *
489: * N.B.: Form isn't handled.
490: */
491: receive_data(instr, outstr)
492: FILE *instr, *outstr;
493: {
494: register int c;
495: int cnt;
496: char buf[BUFSIZ];
497:
498:
499: switch (type) {
500:
501: case TYPE_I:
502: case TYPE_L:
503: while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0)
504: if (write(fileno(outstr), buf, cnt) < 0)
505: return (1);
506: return (cnt < 0);
507:
508: case TYPE_E:
509: reply(504, "TYPE E not implemented.");
510: return (1);
511:
512: case TYPE_A:
513: while ((c = getc(instr)) != EOF) {
514: if (c == '\r') {
515: if (ferror (outstr))
516: return (1);
517: if ((c = getc(instr)) != '\n')
518: putc ('\r', outstr);
519: if (c == '\0')
520: continue;
521: }
522: putc (c, outstr);
523: }
524: if (ferror (instr) || ferror (outstr))
525: return (1);
526: return (0);
527: }
528: fatal("Unknown type in receive_data.");
529: /*NOTREACHED*/
530: }
531:
532: fatal(s)
533: char *s;
534: {
535: reply(451, "Error in server: %s\n", s);
536: reply(221, "Closing connection due to server error.");
537: dologout(0);
538: }
539:
540: reply(n, s, args)
541: int n;
542: char *s;
543: {
544:
545: printf("%d ", n);
546: _doprnt(s, &args, stdout);
547: printf("\r\n");
548: fflush(stdout);
549: if (debug) {
550: fprintf(stderr, "<--- %d ", n);
551: _doprnt(s, &args, stderr);
552: fprintf(stderr, "\n");
553: fflush(stderr);
554: }
555: }
556:
557: lreply(n, s, args)
558: int n;
559: char *s;
560: {
561: printf("%d-", n);
562: _doprnt(s, &args, stdout);
563: printf("\r\n");
564: fflush(stdout);
565: if (debug) {
566: fprintf(stderr, "<--- %d-", n);
567: _doprnt(s, &args, stderr);
568: fprintf(stderr, "\n");
569: }
570: }
571:
572: replystr(s)
573: char *s;
574: {
575: printf("%s\r\n", s);
576: fflush(stdout);
577: if (debug)
578: fprintf(stderr, "<--- %s\n", s);
579: }
580:
581: ack(s)
582: char *s;
583: {
584: reply(200, "%s command okay.", s);
585: }
586:
587: nack(s)
588: char *s;
589: {
590: reply(502, "%s command not implemented.", s);
591: }
592:
593: yyerror()
594: {
595: reply(500, "Command not understood.");
596: }
597:
598: delete(name)
599: char *name;
600: {
601: struct stat st;
602:
603: if (stat(name, &st) < 0) {
604: reply(550, "%s: %s.", name, sys_errlist[errno]);
605: return;
606: }
607: if ((st.st_mode&S_IFMT) == S_IFDIR) {
608: if (rmdir(name) < 0) {
609: reply(550, "%s: %s.", name, sys_errlist[errno]);
610: return;
611: }
612: goto done;
613: }
614: if (unlink(name) < 0) {
615: reply(550, "%s: %s.", name, sys_errlist[errno]);
616: return;
617: }
618: done:
619: ack("DELE");
620: }
621:
622: cwd(path)
623: char *path;
624: {
625:
626: if (chdir(path) < 0) {
627: reply(550, "%s: %s.", path, sys_errlist[errno]);
628: return;
629: }
630: ack("CWD");
631: }
632:
633: makedir(name)
634: char *name;
635: {
636: struct stat st;
637: int dochown = stat(name, &st) < 0;
638:
639: if (mkdir(name, 0777) < 0) {
640: reply(550, "%s: %s.", name, sys_errlist[errno]);
641: return;
642: }
643: if (dochown)
644: (void) chown(name, pw->pw_uid, -1);
645: ack("MKDIR");
646: }
647:
648: removedir(name)
649: char *name;
650: {
651:
652: if (rmdir(name) < 0) {
653: reply(550, "%s: %s.", name, sys_errlist[errno]);
654: return;
655: }
656: ack("RMDIR");
657: }
658:
659: pwd()
660: {
661: char path[MAXPATHLEN + 1];
662:
663: if (getwd(path) == NULL) {
664: reply(451, "%s.", path);
665: return;
666: }
667: reply(251, "\"%s\" is current directory.", path);
668: }
669:
670: char *
671: renamefrom(name)
672: char *name;
673: {
674: struct stat st;
675:
676: if (stat(name, &st) < 0) {
677: reply(550, "%s: %s.", name, sys_errlist[errno]);
678: return ((char *)0);
679: }
680: reply(350, "File exists, ready for destination name");
681: return (name);
682: }
683:
684: renamecmd(from, to)
685: char *from, *to;
686: {
687:
688: if (rename(from, to) < 0) {
689: reply(550, "rename: %s.", sys_errlist[errno]);
690: return;
691: }
692: ack("RNTO");
693: }
694:
695: dolog(sin)
696: struct sockaddr_in *sin;
697: {
698: struct hostent *hp = gethostbyaddr(&sin->sin_addr,
699: sizeof (struct in_addr), AF_INET);
700: time_t t;
701:
702: if (hp) {
703: strncpy(remotehost, hp->h_name, sizeof (remotehost));
704: endhostent();
705: } else
706: strncpy(remotehost, inet_ntoa(sin->sin_addr),
707: sizeof (remotehost));
708: if (!logging)
709: return;
710: t = time(0);
711: fprintf(stderr,"FTPD: connection from %s at %s", remotehost, ctime(&t));
712: fflush(stderr);
713: }
714:
715: #include <utmp.h>
716:
717: #define SCPYN(a, b) strncpy(a, b, sizeof (a))
718: struct utmp utmp;
719:
720: /*
721: * Record login in wtmp file.
722: */
723: dologin(pw)
724: struct passwd *pw;
725: {
726: int wtmp;
727: char line[32];
728:
729: wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
730: if (wtmp >= 0) {
731: /* hack, but must be unique and no tty line */
732: sprintf(line, "ftp%d", getpid());
733: SCPYN(utmp.ut_line, line);
734: SCPYN(utmp.ut_name, pw->pw_name);
735: SCPYN(utmp.ut_host, remotehost);
736: utmp.ut_time = time(0);
737: (void) write(wtmp, (char *)&utmp, sizeof (utmp));
738: (void) close(wtmp);
739: }
740: }
741:
742: /*
743: * Record logout in wtmp file
744: * and exit with supplied status.
745: */
746: dologout(status)
747: int status;
748: {
749: int wtmp;
750:
751: if (!logged_in)
752: _exit(status);
753: seteuid(0);
754: wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
755: if (wtmp >= 0) {
756: SCPYN(utmp.ut_name, "");
757: SCPYN(utmp.ut_host, "");
758: utmp.ut_time = time(0);
759: (void) write(wtmp, (char *)&utmp, sizeof (utmp));
760: (void) close(wtmp);
761: }
762: /* beware of flushing buffers after a SIGPIPE */
763: _exit(status);
764: }
765:
766: /*
767: * Special version of popen which avoids
768: * call to shell. This insures noone may
769: * create a pipe to a hidden program as a side
770: * effect of a list or dir command.
771: */
772: #define tst(a,b) (*mode == 'r'? (b) : (a))
773: #define RDR 0
774: #define WTR 1
775: static int popen_pid[5];
776:
777: static char *
778: nextarg(cpp)
779: char *cpp;
780: {
781: register char *cp = cpp;
782:
783: if (cp == 0)
784: return (cp);
785: while (*cp && *cp != ' ' && *cp != '\t')
786: cp++;
787: if (*cp == ' ' || *cp == '\t') {
788: *cp++ = '\0';
789: while (*cp == ' ' || *cp == '\t')
790: cp++;
791: }
792: if (cp == cpp)
793: return ((char *)0);
794: return (cp);
795: }
796:
797: FILE *
798: popen(cmd, mode)
799: char *cmd, *mode;
800: {
801: int p[2], ac, gac;
802: register myside, hisside, pid;
803: char *av[20], *gav[512];
804: register char *cp;
805:
806: if (pipe(p) < 0)
807: return (NULL);
808: cp = cmd, ac = 0;
809: /* break up string into pieces */
810: do {
811: av[ac++] = cp;
812: cp = nextarg(cp);
813: } while (cp && *cp && ac < 20);
814: av[ac] = (char *)0;
815: gav[0] = av[0];
816: /* glob each piece */
817: for (gac = ac = 1; av[ac] != NULL; ac++) {
818: char **pop;
819: extern char **glob();
820:
821: pop = glob(av[ac]);
822: if (pop) {
823: av[ac] = (char *)pop; /* save to free later */
824: while (*pop && gac < 512)
825: gav[gac++] = *pop++;
826: }
827: }
828: gav[gac] = (char *)0;
829: myside = tst(p[WTR], p[RDR]);
830: hisside = tst(p[RDR], p[WTR]);
831: if ((pid = fork()) == 0) {
832: /* myside and hisside reverse roles in child */
833: close(myside);
834: dup2(hisside, tst(0, 1));
835: close(hisside);
836: execv(gav[0], gav);
837: _exit(1);
838: }
839: for (ac = 1; av[ac] != NULL; ac++)
840: blkfree((char **)av[ac]);
841: if (pid == -1)
842: return (NULL);
843: popen_pid[myside] = pid;
844: close(hisside);
845: return (fdopen(myside, mode));
846: }
847:
848: pclose(ptr)
849: FILE *ptr;
850: {
851: register f, r, (*hstat)(), (*istat)(), (*qstat)();
852: int status;
853:
854: f = fileno(ptr);
855: fclose(ptr);
856: istat = signal(SIGINT, SIG_IGN);
857: qstat = signal(SIGQUIT, SIG_IGN);
858: hstat = signal(SIGHUP, SIG_IGN);
859: while ((r = wait(&status)) != popen_pid[f] && r != -1)
860: ;
861: if (r == -1)
862: status = -1;
863: signal(SIGINT, istat);
864: signal(SIGQUIT, qstat);
865: signal(SIGHUP, hstat);
866: return (status);
867: }
868:
869: /*
870: * Check user requesting login priviledges.
871: * Disallow anyone mentioned in the file FTPUSERS
872: * to allow people such as uucp to be avoided.
873: */
874: checkuser(name)
875: register char *name;
876: {
877: char line[BUFSIZ], *index();
878: FILE *fd;
879: int found = 0;
880:
881: fd = fopen(FTPUSERS, "r");
882: if (fd == NULL)
883: return (1);
884: while (fgets(line, sizeof (line), fd) != NULL) {
885: register char *cp = index(line, '\n');
886:
887: if (cp)
888: *cp = '\0';
889: if (strcmp(line, name) == 0) {
890: found++;
891: break;
892: }
893: }
894: fclose(fd);
895: return (!found);
896: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.