drm/linux: Restore wait_event*() functionality
[dragonfly.git] / contrib / opie / opieftpd.c
1 /* opieftpd.c: Main program for an FTP daemon.
2
3 %%% portions-copyright-cmetz-96
4 Portions of this software are Copyright 1996-1999 by Craig Metz, All Rights
5 Reserved. The Inner Net License Version 2 applies to these portions of
6 the software.
7 You should have received a copy of the license with this software. If
8 you didn't get a copy, you may request one from <license@inner.net>.
9
10 Portions of this software are Copyright 1995 by Randall Atkinson and Dan
11 McDonald, All Rights Reserved. All Rights under this copyright are assigned
12 to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
13 License Agreement applies to this software.
14
15         History:
16
17         Modified by cmetz for OPIE 2.4. Add id parameter to opielogwtmp. Use
18                 opiestrncpy(). Fix incorrect use of setproctitle().
19         Modified by cmetz for OPIE 2.32. Remove include of dirent.h here; it's
20                 done already (and conditionally) in opie_cfg.h.
21         Modified by cmetz for OPIE 2.31. Merged in some 4.4BSD-Lite changes.
22                 Merged in a security fix to BSD-derived ftpds.
23         Modified by cmetz for OPIE 2.3. Fixed the filename at the top.
24                 Moved LS_COMMAND here.
25         Modified by cmetz for OPIE 2.2. Use FUNCTION definition et al.
26                 Removed useless strings (I don't think that removing the
27                 ucb copyright one is a problem -- please let me know if
28                 I'm wrong). Changed default CMASK to 077. Removed random
29                 comments. Use ANSI stdargs for reply/lreply if we can,
30                 added stdargs version of reply/lreply. Don't declare the
31                 tos variable unless IP_TOS defined. Include stdargs headers
32                 early. More headers ifdefed. Made everything static.
33                 Got rid of gethostname() call and use of hostname. Pared
34                 down status response for places where header files frequently
35                 cause trouble. Made logging of user logins (ala -l)
36                 non-optional. Moved reply()/lrepy(). Fixed some prototypes.
37         Modified at NRL for OPIE 2.1. Added declaration of envp. Discard
38                 result of opiechallenge (allows access control to work).
39                 Added patches for AIX. Symbol changes for autoconf.
40         Modified at NRL for OPIE 2.01. Changed password lookup handling
41                 to avoid problems with drain-bamaged shadow password packages.
42                 Properly handle internal state for anonymous FTP. Unlock
43                 user accounts properly if login fails because of /etc/shells.
44                 Make sure to close syslog by function to avoid problems with
45                 drain bamaged syslog implementations.
46         Modified at NRL for OPIE 2.0.
47         Originally from BSD Net/2.
48
49                 There is some really, really ugly code in here.
50
51 $FreeBSD: head/contrib/opie/opieftpd.c 92914 2002-03-21 23:42:52Z markm $
52 */
53 /*
54  * Copyright (c) 1985, 1988, 1990 Regents of the University of California.
55  * All rights reserved.
56  *
57  * Redistribution and use in source and binary forms, with or without
58  * modification, are permitted provided that the following conditions
59  * are met:
60  * 1. Redistributions of source code must retain the above copyright
61  *    notice, this list of conditions and the following disclaimer.
62  * 2. Redistributions in binary form must reproduce the above copyright
63  *    notice, this list of conditions and the following disclaimer in the
64  *    documentation and/or other materials provided with the distribution.
65  * 3. All advertising materials mentioning features or use of this software
66  *    must display the following acknowledgement:
67  *      This product includes software developed by the University of
68  *      California, Berkeley and its contributors.
69  * 4. Neither the name of the University nor the names of its contributors
70  *    may be used to endorse or promote products derived from this software
71  *    without specific prior written permission.
72  *
73  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
74  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
75  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
76  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
77  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
78  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
79  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
80  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
81  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
82  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
83  * SUCH DAMAGE.
84  */
85
86 #include "opie_cfg.h"
87
88 #if HAVE_ANSISTDARG
89 #include <stdarg.h>
90 #endif /* HAVE_ANSISTDARG */
91
92 /*
93  * FTP server.
94  */
95
96 #if HAVE_SYS_PARAM_H
97 #include <sys/param.h>
98 #endif /* HAVE_SYS_PARAM_H */
99 #include <sys/stat.h>
100 /* #include <sys/ioctl.h> */
101 #include <sys/socket.h>
102 #include <sys/wait.h>
103 #ifdef SYS_FCNTL_H
104 #include <sys/fcntl.h>
105 #else
106 #include <fcntl.h>
107 #endif  /* SYS_FCNTL_H */
108 #include <sys/types.h>
109
110 #include <netinet/in.h>
111 #include <netinet/in_systm.h>
112 #include <netinet/ip.h>
113
114 #define FTP_NAMES
115 #include <arpa/ftp.h>
116 #include <arpa/inet.h>
117 #include <arpa/telnet.h>
118
119 #include <signal.h>
120 #include <fcntl.h>
121 #if HAVE_TIME_H
122 #include <time.h>
123 #endif /* HAVE_TIME_H */
124 #if HAVE_PWD_H
125 #include <pwd.h>
126 #endif /* HAVE_PWD_H */
127 #include <setjmp.h>
128 #include <netdb.h>
129 #include <errno.h>
130 #include <syslog.h>
131 #if HAVE_UNISTD_H
132 #include <unistd.h>
133 #endif /* HAVE_UNISTD_H */
134 #include <stdio.h>
135 #include <ctype.h>
136 #include <stdlib.h>
137 #include <string.h>
138 #include <grp.h>
139
140 #include "opie.h"
141
142 #if HAVE_SHADOW_H
143 #include <shadow.h>
144 #endif /* HAVE_SHADOW_H */
145
146 #if HAVE_CRYPT_H
147 #include <crypt.h>
148 #endif /* HAVE_CRYPT_H */
149
150 #if HAVE_SYS_UTSNAME_H
151 #include <sys/utsname.h>
152 #endif /* HAVE_SYS_UTSNAME_H */
153
154 #ifdef _AIX
155 #include <sys/id.h>
156 #include <sys/priv.h>
157 #endif /* _AIX */
158
159 #ifdef IP_TOS
160 #ifndef IPTOS_THROUGHPUT
161 #undef IP_TOS
162 #endif /* !IPTOS_THROUGHPUT */
163 #ifndef IPTOS_LOWDELAY
164 #undef IP_TOS
165 #endif /* !IPTOS_LOWDELAY */
166 #endif /* IP_TOS */
167
168 extern int errno;
169 extern char *home;      /* pointer to home directory for glob */
170 extern FILE *ftpd_popen __P((char *, char *));
171 extern int ftpd_pclose __P((FILE *));
172 extern char cbuf[];
173 extern off_t restart_point;
174
175 static struct sockaddr_in ctrl_addr;
176 static struct sockaddr_in data_source;
177 struct sockaddr_in data_dest;
178 struct sockaddr_in his_addr;
179 static struct sockaddr_in pasv_addr;
180
181 static int data;
182 jmp_buf errcatch;
183 static jmp_buf urgcatch;
184 int logged_in;
185 struct passwd *pw;
186 int debug;
187 int timeout = 900;      /* timeout after 15 minutes of inactivity */
188 int maxtimeout = 7200;  /* don't allow idle time to be set beyond 2 hours */
189
190 #if DOANONYMOUS
191 static int guest;
192 #endif  /* DOANONYMOUS */
193 int type;
194 int form;
195 static int stru;        /* avoid C keyword */
196 static int mode;
197 int usedefault = 1;     /* for data transfers */
198 int pdata = -1; /* for passive mode */
199 static int transflag;
200 static off_t file_size;
201 static off_t byte_count;
202
203 #if (!defined(CMASK) || CMASK == 0)
204 #undef CMASK
205 #define CMASK 077
206 #endif
207
208 static int defumask = CMASK;    /* default umask value */
209 char tmpline[7];
210 char remotehost[MAXHOSTNAMELEN];
211
212 /*
213  * Timeout intervals for retrying connections
214  * to hosts that don't accept PORT cmds.  This
215  * is a kludge, but given the problems with TCP...
216  */
217 #define SWAITMAX        90      /* wait at most 90 seconds */
218 #define SWAITINT        5       /* interval between retries */
219
220 static int swaitmax = SWAITMAX;
221 static int swaitint = SWAITINT;
222
223 #if DOTITLE
224 static char **Argv = NULL;      /* pointer to argument vector */
225 static char *LastArgv = NULL;   /* end of argv */
226 static char proctitle[BUFSIZ];  /* initial part of title */
227 #endif  /* DOTITLE */
228
229 static int af_pwok = 0, pwok = 0;
230 static struct opie opiestate;
231
232 VOIDRET perror_reply __P((int, char *));
233 VOIDRET dologout __P((int));
234 char *getline __P((char *, int, FILE *));
235 VOIDRET upper __P((char *));
236
237 static VOIDRET lostconn __P((int));
238 static VOIDRET myoob __P((int));
239 static FILE *getdatasock __P((char *));
240 static FILE *dataconn __P((char *, off_t, char *));
241 static int checkuser __P((char *));
242 static VOIDRET end_login __P((void));
243 static VOIDRET send_data __P((FILE *, FILE *, off_t));
244 static int receive_data __P((FILE *, FILE *));
245 static char *gunique __P((char *));
246 static char *sgetsave __P((char *));
247
248 int opielogwtmp __P((char *, char *, char *, char *));
249
250 int fclose __P((FILE *));
251
252 #ifdef HAVE_ANSISTDARG
253 VOIDRET reply FUNCTION((stdarg is ANSI only), int n AND char *fmt AND ...) 
254 {
255   va_list ap;
256   char buffer[1024];
257
258   va_start(ap, fmt);
259   vsprintf(buffer, fmt, ap);
260   va_end(ap);
261
262   printf("%d %s\r\n", n, buffer);
263   fflush(stdout);
264   if (debug)
265     syslog(LOG_DEBUG, "<--- %d %s", n, buffer);
266 }
267 #else /* HAVE_ANSISTDARG */
268 VOIDRET reply FUNCTION((n, fmt, p0, p1, p2, p3, p4, p5), int n AND char *fmt AND int p0 AND int p1 AND int p2 AND int p3 AND int p4 AND int p5)
269 {
270   printf("%d ", n);
271   printf(fmt, p0, p1, p2, p3, p4, p5);
272   printf("\r\n");
273   fflush(stdout);
274   if (debug) {
275     syslog(LOG_DEBUG, "<--- %d ", n);
276     syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
277   }
278 }
279 #endif /* HAVE_ANSISTDARG */
280
281 #ifdef HAVE_ANSISTDARG
282 VOIDRET lreply FUNCTION((stdarg is ANSI only), int n AND char *fmt AND ...) 
283 {
284   va_list ap;
285   char buffer[1024];
286
287   va_start(ap, fmt);
288   vsprintf(buffer, fmt, ap);
289   va_end(ap);
290
291   printf("%d- %s\r\n", n, buffer);
292   fflush(stdout);
293   if (debug)
294     syslog(LOG_DEBUG, "<--- %d- %s", n, buffer);
295 }
296 #else /* HAVE_ANSISTDARG */
297 VOIDRET lreply FUNCTION((n, fmt, p0, p1, p2, p3, p4, p5), int n AND char *fmt AND int p0 AND int p1 AND int p2 AND int p3 AND int p4 AND int p5)
298 {
299   printf("%d- ", n);
300   printf(fmt, p0, p1, p2, p3, p4, p5);
301   printf("\r\n");
302   fflush(stdout);
303   if (debug) {
304     syslog(LOG_DEBUG, "<--- %d- ", n);
305     syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
306   }
307 }
308 #endif /* HAVE_ANSISTDARG */
309
310 VOIDRET enable_signalling FUNCTION_NOARGS
311 {
312         signal(SIGPIPE, lostconn);
313         if ((int)signal(SIGURG, myoob) < 0)
314                 syslog(LOG_ERR, "signal: %m");
315 }
316
317 VOIDRET disable_signalling FUNCTION_NOARGS
318 {
319         signal(SIGPIPE, SIG_IGN);
320         if ((int)signal(SIGURG, SIG_IGN) < 0)
321                 syslog(LOG_ERR, "signal: %m");
322 }
323
324 static VOIDRET lostconn FUNCTION((input), int input)
325 {
326   if (debug)
327     syslog(LOG_DEBUG, "lost connection");
328   dologout(-1);
329 }
330
331 static char ttyline[20];
332
333 /*
334  * Helper function for sgetpwnam().
335  */
336 static char *sgetsave FUNCTION((s), char *s)
337 {
338   char *new = malloc((unsigned) strlen(s) + 1);
339
340   if (new == NULL) {
341     perror_reply(421, "Local resource failure: malloc");
342     dologout(1);
343     /* NOTREACHED */
344   }
345   strcpy(new, s);
346   return (new);
347 }
348
349 /*
350  * Save the result of a getpwnam.  Used for USER command, since
351  * the data returned must not be clobbered by any other command
352  * (e.g., globbing).
353  */
354 static struct passwd *sgetpwnam FUNCTION((name), char *name)
355 {
356   static struct passwd save;
357   register struct passwd *p;
358
359 #if HAVE_SHADOW
360   struct spwd *spwd;
361 #endif /* HAVE_SHADOW */
362
363   if ((p = getpwnam(name)) == NULL)
364     return (p);
365
366 #if HAVE_SHADOW
367   if ((spwd = getspnam(name)) == NULL)
368     return NULL;
369
370   endspent();
371
372   p->pw_passwd = spwd->sp_pwdp;
373 #endif /* HAVE_SHADOW */
374
375   endpwent();
376
377   if (save.pw_name) {
378     free(save.pw_name);
379     free(save.pw_passwd);
380     free(save.pw_gecos);
381     free(save.pw_dir);
382     free(save.pw_shell);
383   }
384   save = *p;
385   save.pw_name = sgetsave(p->pw_name);
386   save.pw_passwd = sgetsave(p->pw_passwd);
387   save.pw_gecos = sgetsave(p->pw_gecos);
388   save.pw_dir = sgetsave(p->pw_dir);
389   save.pw_shell = sgetsave(p->pw_shell);
390   return (&save);
391 }
392
393 int login_attempts;     /* number of failed login attempts */
394 int askpasswd;  /* had user command, ask for passwd */
395
396 /*
397  * USER command.
398  * Sets global passwd pointer pw if named account exists and is acceptable;
399  * sets askpasswd if a PASS command is expected.  If logged in previously,
400  * need to reset state.  If name is "ftp" or "anonymous", the name is not in
401  * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
402  * If account doesn't exist, ask for passwd anyway.  Otherwise, check user
403  * requesting login privileges.  Disallow anyone who does not have a standard
404  * shell as returned by getusershell().  Disallow anyone mentioned in the file
405  * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
406  */
407 int user FUNCTION((name), char *name)
408 {
409   register char *cp;
410   char *shell;
411
412   if (logged_in) {
413 #if DOANONYMOUS
414     if (guest) {
415       reply(530, "Can't change user from guest login.");
416       return -1;
417     }
418 #endif  /* DOANONMOUS */
419     end_login();
420   }
421   askpasswd = 1;
422 #if DOANONYMOUS
423   guest = 0;
424   if (!strcmp(name, "ftp") || !strcmp(name, "anonymous"))
425     if (!checkuser("ftp") && !checkuser("anonymous"))
426       if ((pw = sgetpwnam("ftp")) != NULL) {
427         guest = 1;
428         askpasswd = 1;
429         reply(331, "Guest login ok, send your e-mail address as your password.");
430         syslog(LOG_INFO, "Anonymous FTP connection made from host %s.", remotehost);
431         return 0;
432       }
433 #endif  /* DOANONYMOUS */
434   if (pw = sgetpwnam(name)) {
435     if ((shell = pw->pw_shell) == NULL || *shell == 0)
436       shell = _PATH_BSHELL;
437     while ((cp = getusershell()) != NULL)
438       if (!strcmp(cp, shell))
439         break;
440     endusershell();
441     if (cp == NULL || checkuser(name) || ((pw->pw_passwd[0] == '*') || (pw->pw_passwd[0] == '#'))) {
442 #if DEBUG
443       if (!cp)
444         syslog(LOG_DEBUG, "Couldn't find %s in the list of valid shells.", pw->pw_shell);
445       if (checkuser(name))
446         syslog(LOG_DEBUG, "checkuser failed - user in /etc/ftpusers?");
447       if (((pw->pw_passwd[0] == '*') || (pw->pw_passwd[0] == '#')))
448         syslog(LOG_DEBUG, "Login disabled: pw_passwd == %s", pw->pw_passwd);
449 #endif /* DEBUG */
450       pw = (struct passwd *) NULL;
451       askpasswd = -1;
452     }
453   } 
454   {
455     char prompt[OPIE_CHALLENGE_MAX + 1];
456
457     opiechallenge(&opiestate, name, prompt);
458
459     if (askpasswd == -1) {
460       syslog(LOG_WARNING, "Invalid FTP user name %s attempted from %s.", name, remotehost);
461       pwok = 0;
462     } else
463       pwok = af_pwok && opiealways(pw->pw_dir);
464
465 #if NEW_PROMPTS
466     reply(331, "Response to %s %s for %s.", prompt,
467 #else /* NEW_PROMPTS */
468     reply(331, "OTP response %s %s for %s.", prompt,
469 #endif /* NEW_PROMPTS */
470           pwok ? "requested" : "required", name);
471   }
472   /* Delay before reading passwd after first failed attempt to slow down
473      passwd-guessing programs. */
474   if (login_attempts)
475     sleep((unsigned) login_attempts);
476
477   return 0;
478 }
479
480 /*
481  * Check if a user is in the file _PATH_FTPUSERS
482  */
483 static int checkuser FUNCTION((name), char *name)
484 {
485   register FILE *fd;
486   register char *p;
487   char line[BUFSIZ];
488
489   if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
490     while (fgets(line, sizeof(line), fd) != NULL)
491       if ((p = strchr(line, '\n')) != NULL) {
492         *p = '\0';
493         if (line[0] == '#')
494           continue;
495         if (!strcmp(line, name)) {
496           fclose(fd);
497           return (1);
498         }
499       }
500     fclose(fd);
501   }
502   return (0);
503 }
504
505 /*
506  * Terminate login as previous user, if any, resetting state;
507  * used when USER command is given or login fails.
508  */
509 static VOIDRET end_login FUNCTION_NOARGS
510 {
511   disable_signalling();
512   if (seteuid((uid_t) 0))
513     syslog(LOG_ERR, "Can't set euid");
514   if (logged_in)
515     opielogwtmp(ttyline, "", "", "ftp");
516   pw = NULL;
517   logged_in = 0;
518 #if DOANONYMOUS
519   guest = 0;
520 #endif  /* DOANONYMOUS */
521   enable_signalling();
522 }
523
524 VOIDRET pass FUNCTION((passwd), char *passwd)
525 {
526   int legit = askpasswd + 1, i;
527   char *cryptpw;
528
529   if (logged_in || askpasswd == 0) {
530     reply(503, "Login with USER first.");
531     return;
532   }
533   askpasswd = 0;
534
535 #if DOANONYMOUS
536   if (!guest) { /* "ftp" is only account allowed no password */
537 #endif  /* DOANONYMOUS */
538     i = opieverify(&opiestate, passwd);
539     if (legit && i && pwok) {
540       cryptpw = crypt(passwd, pw->pw_passwd);
541       i = (cryptpw == NULL || strcmp(cryptpw, pw->pw_passwd) != 0);
542     }
543     if (!legit || i) {
544       reply(530, "Login incorrect.");
545       pw = NULL;
546       if (login_attempts++ >= 5) {
547         syslog(LOG_WARNING,
548                "Repeated login failures for user %s from %s",
549                pw->pw_name, remotehost);
550         exit(0);
551       }
552       return;
553     }
554 #if DOANONYMOUS
555   } else
556     if ((passwd[0] <= ' ') ||  checkuser(passwd)) {
557       reply(530, "No identity, no service.");
558       syslog(LOG_DEBUG, "Bogus address: %s", passwd);
559       exit(0);
560     }
561 #endif  /* DOANONYMOUS */
562   login_attempts = 0;   /* this time successful */
563   if (setegid((gid_t) pw->pw_gid) < 0) {
564     reply(550, "Can't set gid.");
565     syslog(LOG_DEBUG, "gid = %d, errno = %s(%d)", pw->pw_gid, strerror(errno), errno);
566     return;
567   }
568   initgroups(pw->pw_name, pw->pw_gid);
569
570   /* open wtmp before chroot */
571   sprintf(ttyline, "ftp%d", getpid());
572   opielogwtmp(ttyline, pw->pw_name, remotehost, "ftp");
573   logged_in = 1;
574
575 #if DOANONYMOUS
576   if (guest) {
577     /* We MUST do a chdir() after the chroot. Otherwise the old current
578        directory will be accessible as "." outside the new root! */
579     if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
580       reply(550, "Can't set guest privileges.");
581       goto bad;
582     }
583   } else
584 #endif  /* DOANONYMOUS */
585     if (chdir(pw->pw_dir) < 0) {
586       if (chdir("/") < 0) {
587         reply(530, "User %s: can't change directory to %s.",
588               pw->pw_name, pw->pw_dir);
589         goto bad;
590       } else
591         lreply(230, "No directory! Logging in with home=/");
592     }
593 /* This patch was contributed by an OPIE user. We don't know what it
594    does, exactly. It may or may not work. */
595 #ifdef _AIX
596    {
597        priv_t priv;
598        priv.pv_priv[0] = 0;
599        priv.pv_priv[1] = 0;
600        setgroups(NULL, NULL);
601        if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_EFFECTIVE|PRIV_BEQUEATH,
602                    &priv, sizeof(priv_t)) < 0 ||
603            setgidx(ID_REAL|ID_EFFECTIVE, (gid_t)pw->pw_gid) < 0 ||
604            setuidx(ID_REAL|ID_EFFECTIVE, (uid_t)pw->pw_uid) < 0 ||
605            seteuid((uid_t)pw->pw_uid) < 0) {
606                reply(550, "Can't set uid (_AIX3).");
607                goto bad;
608        }
609     }
610 #else /* _AIX */
611   if (seteuid((uid_t) pw->pw_uid) < 0) {
612     reply(550, "Can't set uid.");
613     goto bad;
614   }
615 #endif /* _AIX */
616  /*
617   * Display a login message, if it exists.
618   * N.B. reply(230,) must follow the message.
619   */
620   {
621   FILE *fd;
622
623   if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) {
624     char *cp, line[128];
625
626     while (fgets(line, sizeof(line), fd) != NULL) {
627       if ((cp = strchr(line, '\n')) != NULL)
628         *cp = '\0';
629       lreply(230, "%s", line);
630     }
631     (void) fflush(stdout);
632     (void) fclose(fd);
633   }
634   }
635 #if DOANONYMOUS
636   if (guest) {
637     reply(230, "Guest login ok, access restrictions apply.");
638 #if DOTITLE
639     setproctitle("%s: anonymous/%.*s", remotehost,
640             sizeof(proctitle) - sizeof(remotehost) - sizeof(": anonymous/"),
641             passwd);
642 #endif /* DOTITLE */
643     syslog(LOG_NOTICE, "ANONYMOUS FTP login from %s with ID %s",
644             remotehost, passwd);
645   } else
646 #endif  /* DOANONYMOUS */
647   {
648     reply(230, "User %s logged in.", pw->pw_name);
649
650 #if DOTITLE
651     setproctitle("%s: %s", remotehost, pw->pw_name);
652 #endif /* DOTITLE */
653     syslog(LOG_INFO, "FTP login from %s with user name %s", remotehost, pw->pw_name);
654   }
655   home = pw->pw_dir;    /* home dir for globbing */
656   umask(defumask);
657   return;
658
659 bad:
660   /* Forget all about it... */
661   end_login();
662 }
663
664 VOIDRET retrieve FUNCTION((cmd, name), char *cmd AND char *name)
665 {
666   FILE *fin, *dout;
667   struct stat st;
668   int (*closefunc) ();
669
670   if (cmd == 0) {
671     fin = fopen(name, "r"), closefunc = fclose;
672     st.st_size = 0;
673   } else {
674     char line[BUFSIZ];
675
676     snprintf(line, sizeof(line), cmd, name);
677     name = line;
678     fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
679     st.st_size = -1;
680 #if HAVE_ST_BLKSIZE
681     st.st_blksize = BUFSIZ;
682 #endif /* HAVE_ST_BLKSIZE */
683   }
684   if (fin == NULL) {
685     if (errno != 0)
686       perror_reply(550, name);
687     return;
688   }
689   if (cmd == 0 &&
690       (fstat(fileno(fin), &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)) {
691     reply(550, "%s: not a plain file.", name);
692     goto done;
693   }
694   if (restart_point) {
695     if (type == TYPE_A) {
696       register int i, n, c;
697
698       n = restart_point;
699       i = 0;
700       while (i++ < n) {
701         if ((c = getc(fin)) == EOF) {
702           perror_reply(550, name);
703           goto done;
704         }
705         if (c == '\n')
706           i++;
707       }
708     } else
709       if (lseek(fileno(fin), restart_point, SEEK_SET /* L_SET */ ) < 0) {
710         perror_reply(550, name);
711         goto done;
712       }
713   }
714   dout = dataconn(name, st.st_size, "w");
715   if (dout == NULL)
716     goto done;
717 #if HAVE_ST_BLKSIZE
718   send_data(fin, dout, st.st_blksize);
719 #else /* HAVE_ST_BLKSIZE */
720   send_data(fin, dout, BUFSIZ);
721 #endif /* HAVE_ST_BLKSIZE */
722   fclose(dout);
723   data = -1;
724   pdata = -1;
725 done:
726   (*closefunc) (fin);
727 }
728
729 VOIDRET store FUNCTION((name, mode, unique), char *name AND char *mode AND int unique)
730 {
731   FILE *fout, *din;
732   struct stat st;
733   int (*closefunc) ();
734
735   if (unique && stat(name, &st) == 0 &&
736       (name = gunique(name)) == NULL)
737     return;
738
739   if (restart_point)
740     mode = "r+w";
741   fout = fopen(name, mode);
742   closefunc = fclose;
743   if (fout == NULL) {
744     perror_reply(553, name);
745     return;
746   }
747   if (restart_point) {
748     if (type == TYPE_A) {
749       register int i, n, c;
750
751       n = restart_point;
752       i = 0;
753       while (i++ < n) {
754         if ((c = getc(fout)) == EOF) {
755           perror_reply(550, name);
756           goto done;
757         }
758         if (c == '\n')
759           i++;
760       }
761       /* We must do this seek to "current" position because we are changing
762          from reading to writing. */
763       if (fseek(fout, 0L, SEEK_CUR /* L_INCR */ ) < 0) {
764         perror_reply(550, name);
765         goto done;
766       }
767     } else
768       if (lseek(fileno(fout), restart_point, SEEK_SET /* L_SET */ ) < 0) {
769         perror_reply(550, name);
770         goto done;
771       }
772   }
773   din = dataconn(name, (off_t) - 1, "r");
774   if (din == NULL)
775     goto done;
776   if (receive_data(din, fout) == 0) {
777     if (unique)
778       reply(226, "Transfer complete (unique file name:%s).",
779             name);
780     else
781       reply(226, "Transfer complete.");
782   }
783   fclose(din);
784   data = -1;
785   pdata = -1;
786 done:
787   (*closefunc) (fout);
788 }
789
790 static FILE *getdatasock FUNCTION((mode), char *mode)
791 {
792   int s, on = 1, tries;
793
794   if (data >= 0)
795     return (fdopen(data, mode));
796   disable_signalling();
797   if (seteuid((uid_t) 0))
798     syslog(LOG_ERR, "Can't set euid");
799   s = socket(AF_INET, SOCK_STREAM, 0);
800   if (s < 0)
801     goto bad;
802   if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
803                  (char *) &on, sizeof(on)) < 0)
804     goto bad;
805   /* anchor socket to avoid multi-homing problems */
806   data_source.sin_family = AF_INET;
807   data_source.sin_addr = ctrl_addr.sin_addr;
808   for (tries = 1;; tries++) {
809     if (bind(s, (struct sockaddr *) & data_source,
810              sizeof(data_source)) >= 0)
811       break;
812     if (errno != EADDRINUSE || tries > 10)
813       goto bad;
814     sleep(tries);
815   }
816   if (seteuid((uid_t) pw->pw_uid))
817     syslog(LOG_ERR, "Can't set euid");
818   enable_signalling();
819 #ifdef IP_TOS
820   on = IPTOS_THROUGHPUT;
821   if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &on, sizeof(int)) < 0)
822     syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
823 #endif
824   return (fdopen(s, mode));
825 bad:
826   {
827   int t = errno;
828
829   if (seteuid((uid_t) pw->pw_uid))
830     syslog(LOG_ERR, "Can't set euid");
831   enable_signalling();
832   close(s);
833
834   errno = t;
835   }
836   return (NULL);
837 }
838
839 static FILE *dataconn FUNCTION((name, size, mode), char *name AND off_t size AND char *mode)
840 {
841   char sizebuf[32];
842   FILE *file;
843   int retry = 0;
844 #ifdef IP_TOS
845   int tos;
846 #endif /* IP_TOS */
847
848   file_size = size;
849   byte_count = 0;
850   if (size != (off_t) - 1)
851     snprintf(sizebuf, sizeof(sizebuf), " (%ld bytes)", size);
852   else
853     strcpy(sizebuf, "");
854   if (pdata >= 0) {
855     struct sockaddr_in from;
856     int s, fromlen = sizeof(from);
857
858     s = accept(pdata, (struct sockaddr *) & from, &fromlen);
859     if (s < 0) {
860       reply(425, "Can't open data connection.");
861       close(pdata);
862       pdata = -1;
863       return (NULL);
864     }
865     close(pdata);
866     pdata = s;
867 #ifdef IP_TOS
868     tos = IPTOS_LOWDELAY;
869     setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &tos,
870                       sizeof(int));
871
872 #endif
873     reply(150, "Opening %s mode data connection for %s%s.",
874           type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
875     return (fdopen(pdata, mode));
876   }
877   if (data >= 0) {
878     reply(125, "Using existing data connection for %s%s.",
879           name, sizebuf);
880     usedefault = 1;
881     return (fdopen(data, mode));
882   }
883   if (usedefault)
884     data_dest = his_addr;
885   usedefault = 1;
886   file = getdatasock(mode);
887   if (file == NULL) {
888     reply(425, "Can't create data socket (%s,%d): %s.",
889           inet_ntoa(data_source.sin_addr),
890           ntohs(data_source.sin_port), strerror(errno));
891     return (NULL);
892   }
893   data = fileno(file);
894   while (connect(data, (struct sockaddr *) & data_dest,
895                  sizeof(data_dest)) < 0) {
896     if (errno == EADDRINUSE && retry < swaitmax) {
897       sleep((unsigned) swaitint);
898       retry += swaitint;
899       continue;
900     }
901     perror_reply(425, "Can't build data connection");
902     fclose(file);
903     data = -1;
904     return (NULL);
905   }
906   reply(150, "Opening %s mode data connection for %s%s.",
907         type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
908   return (file);
909 }
910
911 /*
912  * Tranfer the contents of "instr" to
913  * "outstr" peer using the appropriate
914  * encapsulation of the data subject
915  * to Mode, Structure, and Type.
916  *
917  * NB: Form isn't handled.
918  */
919 static VOIDRET send_data FUNCTION((instr, outstr, blksize), FILE *instr AND FILE *outstr AND off_t blksize)
920 {
921   register int c, cnt;
922   register char *buf;
923   int netfd, filefd;
924
925   transflag++;
926   if (setjmp(urgcatch)) {
927     transflag = 0;
928     return;
929   }
930   switch (type) {
931
932   case TYPE_A:
933     while ((c = getc(instr)) != EOF) {
934       byte_count++;
935       if (c == '\n') {
936         if (ferror(outstr))
937           goto data_err;
938         putc('\r', outstr);
939       }
940       putc(c, outstr);
941     }
942     fflush(outstr);
943     transflag = 0;
944     if (ferror(instr))
945       goto file_err;
946     if (ferror(outstr))
947       goto data_err;
948     reply(226, "Transfer complete.");
949     return;
950
951   case TYPE_I:
952   case TYPE_L:
953     if ((buf = malloc((u_int) blksize)) == NULL) {
954       transflag = 0;
955       perror_reply(451, "Local resource failure: malloc");
956       return;
957     }
958     netfd = fileno(outstr);
959     filefd = fileno(instr);
960     while ((cnt = read(filefd, buf, (u_int) blksize)) > 0 &&
961            write(netfd, buf, cnt) == cnt)
962       byte_count += cnt;
963     transflag = 0;
964     free(buf);
965     if (cnt != 0) {
966       if (cnt < 0)
967         goto file_err;
968       goto data_err;
969     }
970     reply(226, "Transfer complete.");
971     return;
972   default:
973     transflag = 0;
974     reply(550, "Unimplemented TYPE %d in send_data", type);
975     return;
976   }
977
978 data_err:
979   transflag = 0;
980   perror_reply(426, "Data connection");
981   return;
982
983 file_err:
984   transflag = 0;
985   perror_reply(551, "Error on input file");
986 }
987
988 /*
989  * Transfer data from peer to
990  * "outstr" using the appropriate
991  * encapulation of the data subject
992  * to Mode, Structure, and Type.
993  *
994  * N.B.: Form isn't handled.
995  */
996 static int receive_data FUNCTION((instr, outstr), FILE *instr AND FILE *outstr)
997 {
998   register int c;
999   int cnt, bare_lfs = 0;
1000   char buf[BUFSIZ];
1001
1002   transflag++;
1003   if (setjmp(urgcatch)) {
1004     transflag = 0;
1005     return (-1);
1006   }
1007   switch (type) {
1008
1009   case TYPE_I:
1010   case TYPE_L:
1011     while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
1012       if (write(fileno(outstr), buf, cnt) != cnt)
1013         goto file_err;
1014       byte_count += cnt;
1015     }
1016     if (cnt < 0)
1017       goto data_err;
1018     transflag = 0;
1019     return (0);
1020
1021   case TYPE_E:
1022     reply(553, "TYPE E not implemented.");
1023     transflag = 0;
1024     return (-1);
1025
1026   case TYPE_A:
1027     while ((c = getc(instr)) != EOF) {
1028       byte_count++;
1029       if (c == '\n')
1030         bare_lfs++;
1031       while (c == '\r') {
1032         if (ferror(outstr))
1033           goto data_err;
1034         if ((c = getc(instr)) != '\n') {
1035           putc('\r', outstr);
1036           if (c == '\0' || c == EOF)
1037             goto contin2;
1038         }
1039       }
1040       putc(c, outstr);
1041   contin2:;
1042     }
1043     fflush(outstr);
1044     if (ferror(instr))
1045       goto data_err;
1046     if (ferror(outstr))
1047       goto file_err;
1048     transflag = 0;
1049     if (bare_lfs) {
1050       lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs);
1051       printf("   File may not have transferred correctly.\r\n");
1052     }
1053     return (0);
1054   default:
1055     reply(550, "Unimplemented TYPE %d in receive_data", type);
1056     transflag = 0;
1057     return (-1);
1058   }
1059
1060 data_err:
1061   transflag = 0;
1062   perror_reply(426, "Data Connection");
1063   return (-1);
1064
1065 file_err:
1066   transflag = 0;
1067   perror_reply(452, "Error writing file");
1068   return (-1);
1069 }
1070
1071 VOIDRET statfilecmd FUNCTION((filename), char *filename)
1072 {
1073   char line[BUFSIZ];
1074   FILE *fin;
1075   int c;
1076
1077 #if HAVE_LS_G_FLAG
1078   snprintf(line, sizeof(line), "%s %s", "/bin/ls -lgA", filename);
1079 #else /* HAVE_LS_G_FLAG */
1080   snprintf(line, sizeof(line), "%s %s", "/bin/ls -lA", filename);
1081 #endif /* HAVE_LS_G_FLAG */
1082   fin = ftpd_popen(line, "r");
1083   lreply(211, "status of %s:", filename);
1084   while ((c = getc(fin)) != EOF) {
1085     if (c == '\n') {
1086       if (ferror(stdout)) {
1087         perror_reply(421, "control connection");
1088         ftpd_pclose(fin);
1089         dologout(1);
1090         /* NOTREACHED */
1091       }
1092       if (ferror(fin)) {
1093         perror_reply(551, filename);
1094         ftpd_pclose(fin);
1095         return;
1096       }
1097       putc('\r', stdout);
1098     }
1099     putc(c, stdout);
1100   }
1101   ftpd_pclose(fin);
1102   reply(211, "End of Status");
1103 }
1104
1105 VOIDRET statcmd FUNCTION_NOARGS
1106 {
1107 /* COMMENTED OUT STUFF BECAUSE THINGS BROKE ON SUNOS. */
1108   struct sockaddr_in *sin;
1109   u_char *a, *p;
1110
1111   lreply(211, "FTP server status:");
1112   printf("     \r\n");
1113   printf("     Connected to %s", remotehost);
1114   if (!isdigit(remotehost[0]))
1115     printf(" (%s)", inet_ntoa(his_addr.sin_addr));
1116   printf("\r\n");
1117   if (logged_in) {
1118 #if DOANONYMOUS
1119     if (guest)
1120       printf("     Logged in anonymously\r\n");
1121     else
1122 #endif  /* DOANONYMOUS */
1123       printf("     Logged in as %s\r\n", pw->pw_name);
1124   } else
1125     if (askpasswd)
1126       printf("     Waiting for password\r\n");
1127     else
1128       printf("     Waiting for user name\r\n");
1129   if (data != -1)
1130     printf("     Data connection open\r\n");
1131   else
1132     if (pdata != -1) {
1133       printf("     in Passive mode");
1134       sin = &pasv_addr;
1135       goto printaddr;
1136     } else
1137       if (usedefault == 0) {
1138         printf("     PORT");
1139         sin = &data_dest;
1140     printaddr:
1141         a = (u_char *) & sin->sin_addr;
1142         p = (u_char *) & sin->sin_port;
1143 #define UC(b) (((int) b) & 0xff)
1144         printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
1145                UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1146 #undef UC
1147       } else
1148         printf("     No data connection\r\n");
1149   reply(211, "End of status");
1150 }
1151
1152 VOIDRET opiefatal FUNCTION((s), char *s)
1153 {
1154   reply(451, "Error in server: %s\n", s);
1155   reply(221, "Closing connection due to server error.");
1156   dologout(0);
1157   /* NOTREACHED */
1158 }
1159
1160 static VOIDRET ack FUNCTION((s), char *s)
1161 {
1162   reply(250, "%s command successful.", s);
1163 }
1164
1165 VOIDRET nack FUNCTION((s), char *s)
1166 {
1167   reply(502, "%s command not implemented.", s);
1168 }
1169
1170 VOIDRET yyerror FUNCTION((s), char *s)
1171 {
1172   char *cp;
1173
1174   if (cp = strchr(cbuf, '\n'))
1175     *cp = '\0';
1176   reply(500, "'%s': command not understood.", cbuf);
1177 }
1178
1179 VOIDRET delete FUNCTION((name), char *name)
1180 {
1181   struct stat st;
1182
1183   if (stat(name, &st) < 0) {
1184     perror_reply(550, name);
1185     return;
1186   }
1187   if ((st.st_mode & S_IFMT) == S_IFDIR) {
1188     if (rmdir(name) < 0) {
1189       perror_reply(550, name);
1190       return;
1191     }
1192     goto done;
1193   }
1194   if (unlink(name) < 0) {
1195     perror_reply(550, name);
1196     return;
1197   }
1198 done:
1199   ack("DELE");
1200 }
1201
1202 VOIDRET cwd FUNCTION((path), char *path)
1203 {
1204   if (chdir(path) < 0)
1205     perror_reply(550, path);
1206   else
1207     ack("CWD");
1208 }
1209
1210 VOIDRET makedir FUNCTION((name), char *name)
1211 {
1212   if (mkdir(name, 0777) < 0)
1213     perror_reply(550, name);
1214   else
1215     reply(257, "MKD command successful.");
1216 }
1217
1218 VOIDRET removedir FUNCTION((name), char *name)
1219 {
1220   if (rmdir(name) < 0)
1221     perror_reply(550, name);
1222   else
1223     ack("RMD");
1224 }
1225
1226 VOIDRET pwd FUNCTION_NOARGS
1227 {
1228   char path[MAXPATHLEN + 1];
1229
1230   if (getcwd(path, MAXPATHLEN) == (char *) NULL)
1231     reply(550, "%s.", path);
1232   else
1233     reply(257, "\"%s\" is current directory.", path);
1234 }
1235
1236 char *renamefrom FUNCTION((name), char *name)
1237 {
1238   struct stat st;
1239
1240   if (stat(name, &st) < 0) {
1241     perror_reply(550, name);
1242     return ((char *) 0);
1243   }
1244   reply(350, "File exists, ready for destination name");
1245   return (name);
1246 }
1247
1248 VOIDRET renamecmd FUNCTION((from, to), char *from AND char *to)
1249 {
1250   if (rename(from, to) < 0)
1251     perror_reply(550, "rename");
1252   else
1253     ack("RNTO");
1254 }
1255
1256 static VOIDRET dolog FUNCTION((sin), struct sockaddr_in *sin)
1257 {
1258   struct hostent *hp = gethostbyaddr((char *) &sin->sin_addr,
1259                                      sizeof(struct in_addr), AF_INET);
1260   time_t t, time();
1261
1262   if (hp)
1263     opiestrncpy(remotehost, hp->h_name, sizeof(remotehost));
1264   else
1265     opiestrncpy(remotehost, inet_ntoa(sin->sin_addr), sizeof(remotehost));
1266 #if DOTITLE
1267   setproctitle("%s: connected", remotehost);
1268 #endif  /* DOTITLE */
1269
1270   t = time((time_t *) 0);
1271   syslog(LOG_INFO, "connection from %s at %s",
1272     remotehost, ctime(&t));
1273 }
1274
1275 /*
1276  * Record logout in wtmp file
1277  * and exit with supplied status.
1278  */
1279 VOIDRET dologout FUNCTION((status), int status)
1280 {
1281   disable_signalling();
1282   if (logged_in) {
1283     if (seteuid((uid_t) 0))
1284       syslog(LOG_ERR, "Can't set euid");
1285     opielogwtmp(ttyline, "", "", "ftp");
1286   }
1287   /* beware of flushing buffers after a SIGPIPE */
1288   _exit(status);
1289 }
1290
1291 static VOIDRET myoob FUNCTION((input), int input)
1292 {
1293   char *cp;
1294
1295   /* only process if transfer occurring */
1296   if (!transflag)
1297     return;
1298   cp = tmpline;
1299   if (getline(cp, 7, stdin) == NULL) {
1300     reply(221, "You could at least say goodbye.");
1301     dologout(0);
1302   }
1303   upper(cp);
1304   if (strcmp(cp, "ABOR\r\n") == 0) {
1305     tmpline[0] = '\0';
1306     reply(426, "Transfer aborted. Data connection closed.");
1307     reply(226, "Abort successful");
1308     longjmp(urgcatch, 1);
1309   }
1310   if (strcmp(cp, "STAT\r\n") == 0) {
1311     if (file_size != (off_t) - 1)
1312       reply(213, "Status: %lu of %lu bytes transferred",
1313             byte_count, file_size);
1314     else
1315       reply(213, "Status: %lu bytes transferred", byte_count);
1316   }
1317 }
1318
1319 /*
1320  * Note: a response of 425 is not mentioned as a possible response to
1321  *      the PASV command in RFC959. However, it has been blessed as
1322  *      a legitimate response by Jon Postel in a telephone conversation
1323  *      with Rick Adams on 25 Jan 89.
1324  */
1325 VOIDRET passive FUNCTION_NOARGS
1326 {
1327   int len;
1328   register char *p, *a;
1329
1330   pdata = socket(AF_INET, SOCK_STREAM, 0);
1331   if (pdata < 0) {
1332     perror_reply(425, "Can't open passive connection");
1333     return;
1334   }
1335   pasv_addr = ctrl_addr;
1336   pasv_addr.sin_port = 0;
1337   if (seteuid((uid_t) 0))
1338     syslog(LOG_ERR, "Can't set euid");
1339   if (bind(pdata, (struct sockaddr *) & pasv_addr, sizeof(pasv_addr)) < 0) {
1340     seteuid((uid_t) pw->pw_uid);
1341     goto pasv_error;
1342   }
1343   if (seteuid((uid_t) pw->pw_uid))
1344     syslog(LOG_ERR, "Can't set euid");
1345   len = sizeof(pasv_addr);
1346   if (getsockname(pdata, (struct sockaddr *) & pasv_addr, &len) < 0)
1347     goto pasv_error;
1348   if (listen(pdata, 1) < 0)
1349     goto pasv_error;
1350   a = (char *) &pasv_addr.sin_addr;
1351   p = (char *) &pasv_addr.sin_port;
1352
1353 #define UC(b) (((int) b) & 0xff)
1354
1355   reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
1356         UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
1357   return;
1358
1359 pasv_error:
1360   close(pdata);
1361   pdata = -1;
1362   perror_reply(425, "Can't open passive connection");
1363   return;
1364 }
1365
1366 /*
1367  * Generate unique name for file with basename "local".
1368  * The file named "local" is already known to exist.
1369  * Generates failure reply on error.
1370  */
1371 static char *gunique FUNCTION((local), char *local)
1372 {
1373   static char new[MAXPATHLEN+1];
1374   struct stat st;
1375   char *cp = strrchr(local, '/');
1376   int count = 0;
1377
1378   if (cp)
1379     *cp = '\0';
1380   if (stat(cp ? local : ".", &st) < 0) {
1381     perror_reply(553, cp ? local : ".");
1382     return ((char *) 0);
1383   }
1384   if (cp)
1385     *cp = '/';
1386   strcpy(new, local);
1387   cp = new + strlen(new);
1388   *cp++ = '.';
1389   for (count = 1; count < 100; count++) {
1390     snprintf(cp, sizeof(new) - (cp - new), "%d", count);
1391     if (stat(new, &st) < 0)
1392       return (new);
1393   }
1394   reply(452, "Unique file name cannot be created.");
1395   return ((char *) 0);
1396 }
1397
1398 /*
1399  * Format and send reply containing system error number.
1400  */
1401 VOIDRET perror_reply FUNCTION((code, string), int code AND char *string)
1402 {
1403   reply(code, "%s: %s.", string, strerror(errno));
1404 }
1405
1406 static char *onefile[] =
1407 {
1408   "",
1409   0
1410 };
1411
1412 VOIDRET send_file_list FUNCTION((whichfiles), char *whichfiles)
1413 {
1414   struct stat st;
1415   DIR *dirp = NULL;
1416   struct dirent *dir;
1417   FILE *dout = NULL;
1418   register char **dirlist, *dirname;
1419   int simple = 0;
1420
1421   if (strpbrk(whichfiles, "~{[*?") != NULL) {
1422     extern char **ftpglob(), *globerr;
1423
1424     globerr = NULL;
1425     dirlist = ftpglob(whichfiles);
1426     if (globerr != NULL) {
1427       reply(550, globerr);
1428       return;
1429     } else
1430       if (dirlist == NULL) {
1431         errno = ENOENT;
1432         perror_reply(550, whichfiles);
1433         return;
1434       }
1435   } else {
1436     onefile[0] = whichfiles;
1437     dirlist = onefile;
1438     simple = 1;
1439   }
1440
1441   if (setjmp(urgcatch)) {
1442     transflag = 0;
1443     return;
1444   }
1445   while (dirname = *dirlist++) {
1446     if (stat(dirname, &st) < 0) {
1447       /* If user typed "ls -l", etc, and the client used NLST, do what the
1448          user meant. */
1449       if (dirname[0] == '-' && *dirlist == NULL &&
1450           transflag == 0) {
1451         retrieve("/bin/ls %s", dirname);
1452         return;
1453       }
1454       perror_reply(550, whichfiles);
1455       if (dout != NULL) {
1456         fclose(dout);
1457         transflag = 0;
1458         data = -1;
1459         pdata = -1;
1460       }
1461       return;
1462     }
1463     if ((st.st_mode & S_IFMT) == S_IFREG) {
1464       if (dout == NULL) {
1465         dout = dataconn("file list", (off_t) - 1, "w");
1466         if (dout == NULL)
1467           return;
1468         transflag++;
1469       }
1470       fprintf(dout, "%s%s\n", dirname,
1471               type == TYPE_A ? "\r" : "");
1472       byte_count += strlen(dirname) + 1;
1473       continue;
1474     } else
1475       if ((st.st_mode & S_IFMT) != S_IFDIR)
1476         continue;
1477
1478     if ((dirp = opendir(dirname)) == NULL)
1479       continue;
1480
1481     while ((dir = readdir(dirp)) != NULL) {
1482       char nbuf[MAXPATHLEN+1];
1483
1484       if (dir->d_name[0] == '.' && (strlen(dir->d_name) == 1))
1485         continue;
1486       if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
1487           (strlen(dir->d_name) == 2))
1488         continue;
1489
1490       snprintf(nbuf, sizeof(nbuf), "%s/%s", dirname, dir->d_name);
1491
1492       /* We have to do a stat to insure it's not a directory or special file. */
1493       if (simple || (stat(nbuf, &st) == 0 &&
1494                      (st.st_mode & S_IFMT) == S_IFREG)) {
1495         if (dout == NULL) {
1496           dout = dataconn("file list", (off_t) - 1, "w");
1497           if (dout == NULL)
1498             return;
1499           transflag++;
1500         }
1501         if (nbuf[0] == '.' && nbuf[1] == '/')
1502           fprintf(dout, "%s%s\n", &nbuf[2],
1503                   type == TYPE_A ? "\r" : "");
1504         else
1505           fprintf(dout, "%s%s\n", nbuf,
1506                   type == TYPE_A ? "\r" : "");
1507         byte_count += strlen(nbuf) + 1;
1508       }
1509     }
1510     closedir(dirp);
1511   }
1512
1513   if (dout == NULL)
1514     reply(550, "No files found.");
1515   else
1516     if (ferror(dout) != 0)
1517       perror_reply(550, "Data connection");
1518     else
1519       reply(226, "Transfer complete.");
1520
1521   transflag = 0;
1522   if (dout != NULL)
1523     fclose(dout);
1524   data = -1;
1525   pdata = -1;
1526 }
1527
1528 #if DOTITLE
1529 /*
1530  * clobber argv so ps will show what we're doing.
1531  * (stolen from sendmail)
1532  * warning, since this is usually started from inetd.conf, it
1533  * often doesn't have much of an environment or arglist to overwrite.
1534  */
1535 VOIDRET setproctitle FUNCTION((fmt, a, b, c), char *fmt AND int a AND int b AND int c)
1536 {
1537   register char *p, *bp, ch;
1538   register int i;
1539   char buf[BUFSIZ];
1540
1541   snprintf(buf, sizeof(buf), fmt, a, b, c);
1542
1543   /* make ps print our process name */
1544   p = Argv[0];
1545   *p++ = '-';
1546
1547   i = strlen(buf);
1548   if (i > LastArgv - p - 2) {
1549     i = LastArgv - p - 2;
1550     buf[i] = '\0';
1551   }
1552   bp = buf;
1553   while (ch = *bp++)
1554     if (ch != '\n' && ch != '\r')
1555       *p++ = ch;
1556   while (p < LastArgv)
1557     *p++ = ' ';
1558 }
1559 #endif  /* DOTITLE */
1560
1561 VOIDRET catchexit FUNCTION_NOARGS
1562 {
1563   closelog();
1564 }
1565
1566 int main FUNCTION((argc, argv, envp), int argc AND char *argv[] AND char *envp[])
1567 {
1568   int addrlen, on = 1;
1569   char *cp;
1570 #ifdef IP_TOS
1571   int tos;
1572 #endif /* IP_TOS */
1573
1574   {
1575   int i;
1576
1577   for (i = sysconf(_SC_OPEN_MAX); i > 2; i--)
1578     close(i);
1579   }
1580   
1581   /* LOG_NDELAY sets up the logging connection immediately, necessary for
1582      anonymous ftp's that chroot and can't do it later. */
1583   openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
1584   atexit(catchexit);
1585   addrlen = sizeof(his_addr);
1586   if (getpeername(0, (struct sockaddr *) & his_addr, &addrlen) < 0) {
1587     syslog(LOG_ERR, "getpeername (%s): %m", argv[0]);
1588     exit(1);
1589   }
1590   addrlen = sizeof(ctrl_addr);
1591   if (getsockname(0, (struct sockaddr *) & ctrl_addr, &addrlen) < 0) {
1592     syslog(LOG_ERR, "getsockname (%s): %m", argv[0]);
1593     exit(1);
1594   }
1595 #ifdef IP_TOS
1596   tos = IPTOS_LOWDELAY;
1597   if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(int)) < 0)
1598     syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
1599 #endif
1600   data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
1601   debug = 0;
1602 #if DOTITLE
1603   /* Save start and extent of argv for setproctitle. */
1604   Argv = argv;
1605   while (*envp)
1606     envp++;
1607   LastArgv = envp[-1] + strlen(envp[-1]);
1608 #endif  /* DOTITLE */
1609
1610   argc--, argv++;
1611   while (argc > 0 && *argv[0] == '-') {
1612     for (cp = &argv[0][1]; *cp; cp++)
1613       switch (*cp) {
1614
1615       case 'v':
1616         debug = 1;
1617         break;
1618
1619       case 'd':
1620         debug = 1;
1621         break;
1622
1623       case 'l':
1624         break;
1625
1626       case 't':
1627         timeout = atoi(++cp);
1628         if (maxtimeout < timeout)
1629           maxtimeout = timeout;
1630         goto nextopt;
1631
1632       case 'T':
1633         maxtimeout = atoi(++cp);
1634         if (timeout > maxtimeout)
1635           timeout = maxtimeout;
1636         goto nextopt;
1637
1638       case 'u':
1639         {
1640           int val = 0;
1641
1642           while (*++cp && *cp >= '0' && *cp <= '9')
1643             val = val * 8 + *cp - '0';
1644           if (*cp)
1645             fprintf(stderr, "ftpd: Bad value for -u\n");
1646           else
1647             defumask = val;
1648           goto nextopt;
1649         }
1650
1651       default:
1652         fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
1653                 *cp);
1654         break;
1655       }
1656 nextopt:
1657     argc--, argv++;
1658   }
1659   freopen(_PATH_DEVNULL, "w", stderr);
1660   signal(SIGCHLD, SIG_IGN);
1661   enable_signalling();
1662
1663   /* Try to handle urgent data inline */
1664 #ifdef SO_OOBINLINE
1665   if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof(on)) < 0)
1666     syslog(LOG_ERR, "setsockopt: %m");
1667 #endif
1668
1669 #ifdef  F_SETOWN
1670   if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
1671     syslog(LOG_ERR, "fcntl F_SETOWN: %m");
1672 #endif
1673   dolog(&his_addr);
1674   /* Set up default state */
1675   data = -1;
1676   type = TYPE_A;
1677   form = FORM_N;
1678   stru = STRU_F;
1679   mode = MODE_S;
1680   tmpline[0] = '\0';
1681   af_pwok = opieaccessfile(remotehost);
1682
1683   {
1684   FILE *fd;
1685   char line[128];
1686
1687   /* If logins are disabled, print out the message. */
1688   if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) {
1689     while (fgets(line, sizeof(line), fd) != NULL) {
1690       if ((cp = strchr(line, '\n')) != NULL)
1691         *cp = '\0';
1692       lreply(530, "%s", line);
1693     }
1694     (void) fflush(stdout);
1695     (void) fclose(fd);
1696     reply(530, "System not available.");
1697     exit(0);
1698   }
1699   if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
1700     while (fgets(line, sizeof(line), fd) != NULL) {
1701       if ((cp = strchr(line, '\n')) != NULL)
1702         *cp = '\0';
1703       lreply(220, "%s", line);
1704     }
1705     (void) fflush(stdout);
1706     (void) fclose(fd);
1707     /* reply(220,) must follow */
1708   }
1709   };
1710
1711   reply(220, "FTP server ready.");
1712
1713   setjmp(errcatch);
1714   for (;;)
1715     yyparse();
1716   /* NOTREACHED */
1717   return 0;
1718 }