new copyright notice
[unix-history] / usr / src / libexec / ftpd / ftpd.c
CommitLineData
f644bb55 1/*
882508af 2 * Copyright (c) 1985, 1988 Regents of the University of California.
e2071415
KB
3 * All rights reserved.
4 *
1343342a 5 * %sccs.include.redist.c%
f644bb55
DF
6 */
7
8#ifndef lint
9char copyright[] =
882508af 10"@(#) Copyright (c) 1985, 1988 Regents of the University of California.\n\
f644bb55 11 All rights reserved.\n";
e2071415 12#endif /* not lint */
f644bb55 13
c6d3d85f 14#ifndef lint
1343342a 15static char sccsid[] = "@(#)ftpd.c 5.36 (Berkeley) %G%";
e2071415 16#endif /* not lint */
c6d3d85f
SL
17
18/*
19 * FTP server.
20 */
aa159ba8 21#include <sys/param.h>
c6d3d85f
SL
22#include <sys/stat.h>
23#include <sys/ioctl.h>
24#include <sys/socket.h>
bb16805b 25#include <sys/file.h>
c8b8c458 26#include <sys/wait.h>
843d1a1c 27#include <sys/dir.h>
c6d3d85f
SL
28
29#include <netinet/in.h>
30
fdb56acd 31#define FTP_NAMES
23eeaca6 32#include <arpa/ftp.h>
1668a723 33#include <arpa/inet.h>
07fffe50 34#include <arpa/telnet.h>
23eeaca6 35
fdb56acd 36#include <ctype.h>
c6d3d85f
SL
37#include <stdio.h>
38#include <signal.h>
c6d3d85f
SL
39#include <pwd.h>
40#include <setjmp.h>
41#include <netdb.h>
28e19fb7 42#include <errno.h>
6ebcb998 43#include <string.h>
df2f99b4 44#include <syslog.h>
23fe6e13 45#include <varargs.h>
e1ea1692 46#include "pathnames.h"
c6d3d85f 47
0597ed04
SL
48/*
49 * File containing login names
50 * NOT to be used on this machine.
51 * Commonly used to disallow uucp.
52 */
c6d3d85f 53extern int errno;
c6d3d85f
SL
54extern char *crypt();
55extern char version[];
56extern char *home; /* pointer to home directory for glob */
5256bbac 57extern FILE *ftpd_popen(), *fopen(), *freopen();
882508af 58extern int ftpd_pclose(), fclose();
07fffe50
GM
59extern char *getline();
60extern char cbuf[];
c6d3d85f
SL
61
62struct sockaddr_in ctrl_addr;
63struct sockaddr_in data_source;
64struct sockaddr_in data_dest;
65struct sockaddr_in his_addr;
fdb56acd 66struct sockaddr_in pasv_addr;
c6d3d85f 67
c6d3d85f 68int data;
07fffe50 69jmp_buf errcatch, urgcatch;
c6d3d85f
SL
70int logged_in;
71struct passwd *pw;
72int debug;
df2f99b4 73int timeout = 900; /* timeout after 15 minutes of inactivity */
fdb56acd 74int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
9072bd8a 75int logging;
c6d3d85f
SL
76int guest;
77int type;
78int form;
79int stru; /* avoid C keyword */
80int mode;
8643b66e 81int usedefault = 1; /* for data transfers */
882508af 82int pdata = -1; /* for passive mode */
07fffe50 83int transflag;
fdb56acd
MK
84off_t file_size;
85off_t byte_count;
86#if !defined(CMASK) || CMASK == 0
87#undef CMASK
88#define CMASK 027
89#endif
90int defumask = CMASK; /* default umask value */
07fffe50 91char tmpline[7];
5256bbac
KB
92char hostname[MAXHOSTNAMELEN];
93char remotehost[MAXHOSTNAMELEN];
c6d3d85f 94
5ac6fc46
SL
95/*
96 * Timeout intervals for retrying connections
97 * to hosts that don't accept PORT cmds. This
98 * is a kludge, but given the problems with TCP...
99 */
100#define SWAITMAX 90 /* wait at most 90 seconds */
101#define SWAITINT 5 /* interval between retries */
102
103int swaitmax = SWAITMAX;
104int swaitint = SWAITINT;
105
c6d3d85f 106int lostconn();
07fffe50 107int myoob();
c6d3d85f 108FILE *getdatasock(), *dataconn();
c6d3d85f 109
843d1a1c
RA
110#ifdef SETPROCTITLE
111char **Argv = NULL; /* pointer to argument vector */
112char *LastArgv = NULL; /* end of argv */
fdb56acd 113char proctitle[BUFSIZ]; /* initial part of title */
843d1a1c
RA
114#endif /* SETPROCTITLE */
115
116main(argc, argv, envp)
c6d3d85f
SL
117 int argc;
118 char *argv[];
843d1a1c 119 char **envp;
c6d3d85f 120{
d0604b39 121 int addrlen, on = 1;
c6d3d85f
SL
122 char *cp;
123
a3dc4d0e 124 addrlen = sizeof (his_addr);
882508af 125 if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
df2f99b4 126 syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
c6d3d85f
SL
127 exit(1);
128 }
a3dc4d0e 129 addrlen = sizeof (ctrl_addr);
882508af 130 if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
df2f99b4 131 syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
a3dc4d0e
MK
132 exit(1);
133 }
134 data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
c6d3d85f 135 debug = 0;
df2f99b4 136 openlog("ftpd", LOG_PID, LOG_DAEMON);
843d1a1c
RA
137#ifdef SETPROCTITLE
138 /*
139 * Save start and extent of argv for setproctitle.
140 */
141 Argv = argv;
142 while (*envp)
143 envp++;
144 LastArgv = envp[-1] + strlen(envp[-1]);
145#endif /* SETPROCTITLE */
146
c6d3d85f
SL
147 argc--, argv++;
148 while (argc > 0 && *argv[0] == '-') {
149 for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
150
5ac6fc46
SL
151 case 'v':
152 debug = 1;
153 break;
154
c6d3d85f
SL
155 case 'd':
156 debug = 1;
c6d3d85f
SL
157 break;
158
9072bd8a
SL
159 case 'l':
160 logging = 1;
161 break;
162
5ac6fc46
SL
163 case 't':
164 timeout = atoi(++cp);
fdb56acd
MK
165 if (maxtimeout < timeout)
166 maxtimeout = timeout;
167 goto nextopt;
168
169 case 'T':
170 maxtimeout = atoi(++cp);
171 if (timeout > maxtimeout)
172 timeout = maxtimeout;
173 goto nextopt;
174
175 case 'u':
176 {
177 int val = 0;
178
179 while (*++cp && *cp >= '0' && *cp <= '9')
180 val = val*8 + *cp - '0';
181 if (*cp)
182 fprintf(stderr, "ftpd: Bad value for -u\n");
183 else
184 defumask = val;
5ac6fc46 185 goto nextopt;
fdb56acd 186 }
5ac6fc46 187
c6d3d85f 188 default:
a3dc4d0e
MK
189 fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
190 *cp);
c6d3d85f
SL
191 break;
192 }
5ac6fc46 193nextopt:
c6d3d85f
SL
194 argc--, argv++;
195 }
e1ea1692 196 (void) freopen(_PATH_DEVNULL, "w", stderr);
df2f99b4
GM
197 (void) signal(SIGPIPE, lostconn);
198 (void) signal(SIGCHLD, SIG_IGN);
a0ff3aa3 199 if ((int)signal(SIGURG, myoob) < 0)
df2f99b4 200 syslog(LOG_ERR, "signal: %m");
a0ff3aa3 201
00aa66da 202 /* Try to handle urgent data inline */
d0604b39 203#ifdef SO_OOBINLINE
5256bbac 204 if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
d0604b39 205 syslog(LOG_ERR, "setsockopt: %m");
5256bbac 206#endif
00aa66da 207
fdb56acd 208#ifdef F_SETOWN
882508af
MK
209 if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
210 syslog(LOG_ERR, "fcntl F_SETOWN: %m");
fdb56acd 211#endif
2584c5be 212 dolog(&his_addr);
a3dc4d0e
MK
213 /*
214 * Set up default state
215 */
a3dc4d0e
MK
216 data = -1;
217 type = TYPE_A;
218 form = FORM_N;
219 stru = STRU_F;
220 mode = MODE_S;
07fffe50 221 tmpline[0] = '\0';
df2f99b4 222 (void) gethostname(hostname, sizeof (hostname));
5256bbac 223 reply(220, "%s FTP server (%s) ready.", hostname, version);
882508af
MK
224 (void) setjmp(errcatch);
225 for (;;)
df2f99b4 226 (void) yyparse();
843d1a1c 227 /* NOTREACHED */
c6d3d85f 228}
3cb22fa5 229
c6d3d85f
SL
230lostconn()
231{
232
407329f7 233 if (debug)
df2f99b4 234 syslog(LOG_DEBUG, "lost connection");
407329f7 235 dologout(-1);
c6d3d85f
SL
236}
237
b206b3b1
KB
238static char ttyline[20];
239
d443d52b
KB
240/*
241 * Helper function for sgetpwnam().
242 */
243char *
244sgetsave(s)
245 char *s;
246{
d443d52b
KB
247 char *malloc();
248 char *new = malloc((unsigned) strlen(s) + 1);
fdb56acd 249
d443d52b 250 if (new == NULL) {
843d1a1c 251 perror_reply(421, "Local resource failure: malloc");
d443d52b 252 dologout(1);
843d1a1c 253 /* NOTREACHED */
d443d52b 254 }
d443d52b 255 (void) strcpy(new, s);
d443d52b
KB
256 return (new);
257}
258
259/*
260 * Save the result of a getpwnam. Used for USER command, since
261 * the data returned must not be clobbered by any other command
262 * (e.g., globbing).
263 */
264struct passwd *
265sgetpwnam(name)
266 char *name;
267{
268 static struct passwd save;
269 register struct passwd *p;
270 char *sgetsave();
271
272 if ((p = getpwnam(name)) == NULL)
273 return (p);
274 if (save.pw_name) {
275 free(save.pw_name);
276 free(save.pw_passwd);
d443d52b
KB
277 free(save.pw_gecos);
278 free(save.pw_dir);
279 free(save.pw_shell);
280 }
281 save = *p;
282 save.pw_name = sgetsave(p->pw_name);
283 save.pw_passwd = sgetsave(p->pw_passwd);
d443d52b
KB
284 save.pw_gecos = sgetsave(p->pw_gecos);
285 save.pw_dir = sgetsave(p->pw_dir);
286 save.pw_shell = sgetsave(p->pw_shell);
287 return (&save);
288}
289
882508af
MK
290int login_attempts; /* number of failed login attempts */
291int askpasswd; /* had user command, ask for passwd */
292
293/*
294 * USER command.
c46ce161
KB
295 * Sets global passwd pointer pw if named account exists and is acceptable;
296 * sets askpasswd if a PASS command is expected. If logged in previously,
297 * need to reset state. If name is "ftp" or "anonymous", the name is not in
298 * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
299 * If account doesn't exist, ask for passwd anyway. Otherwise, check user
300 * requesting login privileges. Disallow anyone who does not have a standard
301 * shell as returned by getusershell(). Disallow anyone mentioned in the file
302 * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
882508af
MK
303 */
304user(name)
305 char *name;
306{
307 register char *cp;
882508af 308 char *shell;
4a5ba807 309 char *getusershell();
882508af
MK
310
311 if (logged_in) {
312 if (guest) {
313 reply(530, "Can't change user from guest login.");
314 return;
315 }
316 end_login();
317 }
318
319 guest = 0;
320 if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
4a5ba807 321 if (checkuser("ftp") || checkuser("anonymous"))
04f4dc63
KM
322 reply(530, "User %s access denied.", name);
323 else if ((pw = sgetpwnam("ftp")) != NULL) {
882508af
MK
324 guest = 1;
325 askpasswd = 1;
326 reply(331, "Guest login ok, send ident as password.");
fdb56acd 327 } else
882508af
MK
328 reply(530, "User %s unknown.", name);
329 return;
330 }
331 if (pw = sgetpwnam(name)) {
332 if ((shell = pw->pw_shell) == NULL || *shell == 0)
e1ea1692 333 shell = _PATH_BSHELL;
882508af
MK
334 while ((cp = getusershell()) != NULL)
335 if (strcmp(cp, shell) == 0)
336 break;
337 endusershell();
4a5ba807 338 if (cp == NULL || checkuser(name)) {
882508af 339 reply(530, "User %s access denied.", name);
fdb56acd
MK
340 if (logging)
341 syslog(LOG_NOTICE,
342 "FTP LOGIN REFUSED FROM %s, %s",
343 remotehost, name);
882508af
MK
344 pw = (struct passwd *) NULL;
345 return;
346 }
882508af
MK
347 }
348 reply(331, "Password required for %s.", name);
349 askpasswd = 1;
350 /*
351 * Delay before reading passwd after first failed
352 * attempt to slow down passwd-guessing programs.
353 */
354 if (login_attempts)
355 sleep((unsigned) login_attempts);
356}
357
4a5ba807
KM
358/*
359 * Check if a user is in the file _PATH_FTPUSERS
360 */
361checkuser(name)
362 char *name;
363{
c46ce161
KB
364 register FILE *fd;
365 register char *p;
366 char line[BUFSIZ];
4a5ba807
KM
367
368 if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
c46ce161
KB
369 while (fgets(line, sizeof(line), fd) != NULL)
370 if ((p = index(line, '\n')) != NULL) {
371 *p = '\0';
372 if (line[0] == '#')
373 continue;
374 if (strcmp(line, name) == 0)
375 return (1);
376 }
4a5ba807
KM
377 (void) fclose(fd);
378 }
379 return (0);
380}
381
882508af
MK
382/*
383 * Terminate login as previous user, if any, resetting state;
384 * used when USER command is given or login fails.
385 */
386end_login()
387{
388
389 (void) seteuid((uid_t)0);
390 if (logged_in)
391 logwtmp(ttyline, "", "");
392 pw = NULL;
393 logged_in = 0;
394 guest = 0;
395}
396
c6d3d85f
SL
397pass(passwd)
398 char *passwd;
399{
882508af 400 char *xpasswd, *salt;
c6d3d85f 401
882508af 402 if (logged_in || askpasswd == 0) {
c6d3d85f
SL
403 reply(503, "Login with USER first.");
404 return;
405 }
882508af 406 askpasswd = 0;
c6d3d85f 407 if (!guest) { /* "ftp" is only account allowed no password */
882508af
MK
408 if (pw == NULL)
409 salt = "xx";
410 else
411 salt = pw->pw_passwd;
412 xpasswd = crypt(passwd, salt);
2584c5be 413 /* The strcmp does not catch null passwords! */
882508af
MK
414 if (pw == NULL || *pw->pw_passwd == '\0' ||
415 strcmp(xpasswd, pw->pw_passwd)) {
c6d3d85f
SL
416 reply(530, "Login incorrect.");
417 pw = NULL;
882508af 418 if (login_attempts++ >= 5) {
fdb56acd 419 syslog(LOG_NOTICE,
882508af
MK
420 "repeated login failures from %s",
421 remotehost);
422 exit(0);
423 }
c6d3d85f
SL
424 return;
425 }
426 }
882508af
MK
427 login_attempts = 0; /* this time successful */
428 (void) setegid((gid_t)pw->pw_gid);
429 (void) initgroups(pw->pw_name, pw->pw_gid);
5c50e19b 430
82be70b7 431 /* open wtmp before chroot */
b206b3b1
KB
432 (void)sprintf(ttyline, "ftp%d", getpid());
433 logwtmp(ttyline, pw->pw_name, remotehost);
82be70b7
KB
434 logged_in = 1;
435
b5993e33 436 if (guest) {
843d1a1c 437 /*
fdb56acd
MK
438 * We MUST do a chdir() after the chroot. Otherwise
439 * the old current directory will be accessible as "."
440 * outside the new root!
843d1a1c
RA
441 */
442 if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
b5993e33
KB
443 reply(550, "Can't set guest privileges.");
444 goto bad;
445 }
fdb56acd 446 } else if (chdir(pw->pw_dir) < 0) {
843d1a1c 447 if (chdir("/") < 0) {
b5993e33
KB
448 reply(530, "User %s: can't change directory to %s.",
449 pw->pw_name, pw->pw_dir);
450 goto bad;
fdb56acd 451 } else
b5993e33 452 lreply(230, "No directory! Logging in with home=/");
843d1a1c 453 }
882508af
MK
454 if (seteuid((uid_t)pw->pw_uid) < 0) {
455 reply(550, "Can't set uid.");
456 goto bad;
457 }
049d3c92 458 if (guest) {
82be70b7 459 reply(230, "Guest login ok, access restrictions apply.");
fdb56acd
MK
460#ifdef SETPROCTITLE
461 sprintf(proctitle, "%s: anonymous/%.*s", remotehost,
462 sizeof(proctitle) - sizeof(remotehost) -
463 sizeof(": anonymous/"), passwd);
464 setproctitle(proctitle);
465#endif /* SETPROCTITLE */
466 if (logging)
467 syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
468 remotehost, passwd);
469 } else {
82be70b7 470 reply(230, "User %s logged in.", pw->pw_name);
fdb56acd
MK
471#ifdef SETPROCTITLE
472 sprintf(proctitle, "%s: %s", remotehost, pw->pw_name);
473 setproctitle(proctitle);
474#endif /* SETPROCTITLE */
475 if (logging)
476 syslog(LOG_INFO, "FTP LOGIN FROM %s, %s",
477 remotehost, pw->pw_name);
049d3c92 478 }
aa159ba8 479 home = pw->pw_dir; /* home dir for globbing */
fdb56acd 480 (void) umask(defumask);
aa159ba8
SL
481 return;
482bad:
882508af
MK
483 /* Forget all about it... */
484 end_login();
aa159ba8
SL
485}
486
c6d3d85f
SL
487retrieve(cmd, name)
488 char *cmd, *name;
489{
490 FILE *fin, *dout;
491 struct stat st;
843d1a1c 492 int (*closefunc)();
c6d3d85f 493
b8343eb6 494 if (cmd == 0) {
b5993e33 495 fin = fopen(name, "r"), closefunc = fclose;
b8343eb6
KB
496 st.st_size = 0;
497 } else {
c6d3d85f
SL
498 char line[BUFSIZ];
499
df2f99b4 500 (void) sprintf(line, cmd, name), name = line;
882508af 501 fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
b8343eb6 502 st.st_size = -1;
fdb56acd 503 st.st_blksize = BUFSIZ;
c6d3d85f
SL
504 }
505 if (fin == NULL) {
edcef58b 506 if (errno != 0)
882508af 507 perror_reply(550, name);
c6d3d85f
SL
508 return;
509 }
c6d3d85f 510 if (cmd == 0 &&
fdb56acd 511 (fstat(fileno(fin), &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
c6d3d85f
SL
512 reply(550, "%s: not a plain file.", name);
513 goto done;
514 }
515 dout = dataconn(name, st.st_size, "w");
516 if (dout == NULL)
517 goto done;
843d1a1c 518 send_data(fin, dout, st.st_blksize);
df2f99b4 519 (void) fclose(dout);
07fffe50
GM
520 data = -1;
521 pdata = -1;
c6d3d85f
SL
522done:
523 (*closefunc)(fin);
524}
525
882508af 526store(name, mode, unique)
c6d3d85f 527 char *name, *mode;
882508af 528 int unique;
c6d3d85f
SL
529{
530 FILE *fout, *din;
b5993e33 531 struct stat st;
843d1a1c 532 int (*closefunc)();
882508af 533 char *gunique();
c6d3d85f 534
b5993e33
KB
535 if (unique && stat(name, &st) == 0 &&
536 (name = gunique(name)) == NULL)
537 return;
aa159ba8 538
843d1a1c
RA
539 fout = fopen(name, mode);
540 closefunc = fclose;
c6d3d85f 541 if (fout == NULL) {
882508af 542 perror_reply(553, name);
c6d3d85f
SL
543 return;
544 }
882508af 545 din = dataconn(name, (off_t)-1, "r");
c6d3d85f
SL
546 if (din == NULL)
547 goto done;
843d1a1c
RA
548 if (receive_data(din, fout) == 0) {
549 if (unique)
882508af 550 reply(226, "Transfer complete (unique file name:%s).",
fdb56acd 551 name);
882508af
MK
552 else
553 reply(226, "Transfer complete.");
07fffe50 554 }
df2f99b4 555 (void) fclose(din);
07fffe50
GM
556 data = -1;
557 pdata = -1;
c6d3d85f
SL
558done:
559 (*closefunc)(fout);
560}
561
562FILE *
563getdatasock(mode)
564 char *mode;
565{
e1ea1692 566 int s, on = 1, tries;
c6d3d85f
SL
567
568 if (data >= 0)
569 return (fdopen(data, mode));
bb16805b 570 s = socket(AF_INET, SOCK_STREAM, 0);
58846728 571 if (s < 0)
c6d3d85f 572 return (NULL);
882508af 573 (void) seteuid((uid_t)0);
e1ea1692
MK
574 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
575 (char *) &on, sizeof (on)) < 0)
58846728 576 goto bad;
edcef58b
SL
577 /* anchor socket to avoid multi-homing problems */
578 data_source.sin_family = AF_INET;
579 data_source.sin_addr = ctrl_addr.sin_addr;
e1ea1692
MK
580 for (tries = 1; ; tries++) {
581 if (bind(s, (struct sockaddr *)&data_source,
582 sizeof (data_source)) >= 0)
583 break;
584 if (errno != EADDRINUSE || tries > 10)
585 goto bad;
586 sleep(tries);
587 }
882508af 588 (void) seteuid((uid_t)pw->pw_uid);
c6d3d85f 589 return (fdopen(s, mode));
58846728 590bad:
882508af 591 (void) seteuid((uid_t)pw->pw_uid);
df2f99b4 592 (void) close(s);
58846728 593 return (NULL);
c6d3d85f
SL
594}
595
596FILE *
597dataconn(name, size, mode)
598 char *name;
5ac6fc46 599 off_t size;
c6d3d85f
SL
600 char *mode;
601{
602 char sizebuf[32];
603 FILE *file;
5ac6fc46 604 int retry = 0;
c6d3d85f 605
fdb56acd
MK
606 file_size = size;
607 byte_count = 0;
882508af 608 if (size != (off_t) -1)
df2f99b4 609 (void) sprintf (sizebuf, " (%ld bytes)", size);
c6d3d85f
SL
610 else
611 (void) strcpy(sizebuf, "");
882508af 612 if (pdata >= 0) {
07fffe50
GM
613 struct sockaddr_in from;
614 int s, fromlen = sizeof(from);
615
882508af 616 s = accept(pdata, (struct sockaddr *)&from, &fromlen);
07fffe50
GM
617 if (s < 0) {
618 reply(425, "Can't open data connection.");
619 (void) close(pdata);
620 pdata = -1;
621 return(NULL);
622 }
623 (void) close(pdata);
624 pdata = s;
4af2732a
MK
625 reply(150, "Opening %s mode data connection for %s%s.",
626 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
07fffe50
GM
627 return(fdopen(pdata, mode));
628 }
c6d3d85f
SL
629 if (data >= 0) {
630 reply(125, "Using existing data connection for %s%s.",
631 name, sizebuf);
8643b66e 632 usedefault = 1;
c6d3d85f
SL
633 return (fdopen(data, mode));
634 }
afa6f79b 635 if (usedefault)
5c81b68e 636 data_dest = his_addr;
5c81b68e 637 usedefault = 1;
c6d3d85f
SL
638 file = getdatasock(mode);
639 if (file == NULL) {
640 reply(425, "Can't create data socket (%s,%d): %s.",
bb16805b 641 inet_ntoa(data_source.sin_addr),
396aa79f 642 ntohs(data_source.sin_port), strerror(errno));
c6d3d85f
SL
643 return (NULL);
644 }
645 data = fileno(file);
882508af
MK
646 while (connect(data, (struct sockaddr *)&data_dest,
647 sizeof (data_dest)) < 0) {
5ac6fc46 648 if (errno == EADDRINUSE && retry < swaitmax) {
df2f99b4 649 sleep((unsigned) swaitint);
5ac6fc46
SL
650 retry += swaitint;
651 continue;
652 }
882508af 653 perror_reply(425, "Can't build data connection");
c6d3d85f
SL
654 (void) fclose(file);
655 data = -1;
656 return (NULL);
657 }
4af2732a
MK
658 reply(150, "Opening %s mode data connection for %s%s.",
659 type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
c6d3d85f
SL
660 return (file);
661}
662
663/*
664 * Tranfer the contents of "instr" to
665 * "outstr" peer using the appropriate
fdb56acd 666 * encapsulation of the data subject
c6d3d85f
SL
667 * to Mode, Structure, and Type.
668 *
669 * NB: Form isn't handled.
670 */
b5993e33 671send_data(instr, outstr, blksize)
c6d3d85f 672 FILE *instr, *outstr;
b5993e33 673 off_t blksize;
c6d3d85f 674{
b5993e33
KB
675 register int c, cnt;
676 register char *buf;
677 int netfd, filefd;
c6d3d85f 678
07fffe50
GM
679 transflag++;
680 if (setjmp(urgcatch)) {
681 transflag = 0;
843d1a1c 682 return;
07fffe50 683 }
c6d3d85f
SL
684 switch (type) {
685
686 case TYPE_A:
687 while ((c = getc(instr)) != EOF) {
fdb56acd 688 byte_count++;
d33c618b 689 if (c == '\n') {
fdb56acd 690 if (ferror(outstr))
843d1a1c 691 goto data_err;
d0604b39 692 (void) putc('\r', outstr);
d33c618b 693 }
d0604b39 694 (void) putc(c, outstr);
c6d3d85f 695 }
843d1a1c 696 fflush(outstr);
fdb56acd
MK
697 transflag = 0;
698 if (ferror(instr))
699 goto file_err;
700 if (ferror(outstr))
843d1a1c
RA
701 goto data_err;
702 reply(226, "Transfer complete.");
703 return;
b5993e33 704
c6d3d85f
SL
705 case TYPE_I:
706 case TYPE_L:
b5993e33
KB
707 if ((buf = malloc((u_int)blksize)) == NULL) {
708 transflag = 0;
fdb56acd
MK
709 perror_reply(451, "Local resource failure: malloc");
710 return;
b5993e33 711 }
c6d3d85f
SL
712 netfd = fileno(outstr);
713 filefd = fileno(instr);
fdb56acd 714 while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
843d1a1c 715 write(netfd, buf, cnt) == cnt)
fdb56acd 716 byte_count += cnt;
07fffe50 717 transflag = 0;
b5993e33 718 (void)free(buf);
fdb56acd
MK
719 if (cnt != 0) {
720 if (cnt < 0)
721 goto file_err;
843d1a1c 722 goto data_err;
fdb56acd 723 }
843d1a1c
RA
724 reply(226, "Transfer complete.");
725 return;
726 default:
727 transflag = 0;
728 reply(550, "Unimplemented TYPE %d in send_data", type);
729 return;
c6d3d85f 730 }
843d1a1c
RA
731
732data_err:
07fffe50 733 transflag = 0;
fdb56acd
MK
734 perror_reply(426, "Data connection");
735 return;
736
737file_err:
738 transflag = 0;
739 perror_reply(551, "Error on input file");
c6d3d85f
SL
740}
741
742/*
743 * Transfer data from peer to
744 * "outstr" using the appropriate
745 * encapulation of the data subject
746 * to Mode, Structure, and Type.
747 *
748 * N.B.: Form isn't handled.
749 */
750receive_data(instr, outstr)
751 FILE *instr, *outstr;
752{
753 register int c;
00aa66da 754 int cnt, bare_lfs = 0;
c6d3d85f
SL
755 char buf[BUFSIZ];
756
07fffe50
GM
757 transflag++;
758 if (setjmp(urgcatch)) {
759 transflag = 0;
fdb56acd 760 return (-1);
07fffe50 761 }
c6d3d85f
SL
762 switch (type) {
763
764 case TYPE_I:
765 case TYPE_L:
07fffe50 766 while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
843d1a1c 767 if (write(fileno(outstr), buf, cnt) != cnt)
fdb56acd
MK
768 goto file_err;
769 byte_count += cnt;
07fffe50 770 }
fdb56acd 771 if (cnt < 0)
843d1a1c 772 goto data_err;
07fffe50 773 transflag = 0;
fdb56acd 774 return (0);
c6d3d85f
SL
775
776 case TYPE_E:
7ccaf1e3 777 reply(553, "TYPE E not implemented.");
07fffe50 778 transflag = 0;
7ccaf1e3 779 return (-1);
c6d3d85f
SL
780
781 case TYPE_A:
c6d3d85f 782 while ((c = getc(instr)) != EOF) {
fdb56acd 783 byte_count++;
00aa66da
RA
784 if (c == '\n')
785 bare_lfs++;
d0604b39 786 while (c == '\r') {
fdb56acd 787 if (ferror(outstr))
843d1a1c 788 goto data_err;
fdb56acd 789 if ((c = getc(instr)) != '\n') {
d0604b39 790 (void) putc ('\r', outstr);
fdb56acd
MK
791 if (c == '\0' || c == EOF)
792 goto contin2;
793 }
c6d3d85f 794 }
fdb56acd
MK
795 (void) putc(c, outstr);
796 contin2: ;
c6d3d85f 797 }
843d1a1c 798 fflush(outstr);
fdb56acd 799 if (ferror(instr))
843d1a1c 800 goto data_err;
fdb56acd
MK
801 if (ferror(outstr))
802 goto file_err;
843d1a1c 803 transflag = 0;
00aa66da
RA
804 if (bare_lfs) {
805 lreply(230, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs);
806 printf(" File may not have transferred correctly.\r\n");
807 }
c6d3d85f 808 return (0);
843d1a1c
RA
809 default:
810 reply(550, "Unimplemented TYPE %d in receive_data", type);
811 transflag = 0;
fdb56acd 812 return (-1);
c6d3d85f 813 }
843d1a1c
RA
814
815data_err:
07fffe50 816 transflag = 0;
fdb56acd
MK
817 perror_reply(426, "Data Connection");
818 return (-1);
819
820file_err:
821 transflag = 0;
822 perror_reply(452, "Error writing file");
823 return (-1);
824}
825
826statfilecmd(filename)
827 char *filename;
828{
829 char line[BUFSIZ];
830 FILE *fin;
831 int c;
832
833 (void) sprintf(line, "/bin/ls -lgA %s", filename);
834 fin = ftpd_popen(line, "r");
835 lreply(211, "status of %s:", filename);
836 while ((c = getc(fin)) != EOF) {
837 if (c == '\n') {
838 if (ferror(stdout)){
839 perror_reply(421, "control connection");
840 (void) ftpd_pclose(fin);
841 dologout(1);
842 /* NOTREACHED */
843 }
844 if (ferror(fin)) {
845 perror_reply(551, filename);
846 (void) ftpd_pclose(fin);
847 return;
848 }
849 (void) putc('\r', stdout);
850 }
851 (void) putc(c, stdout);
852 }
853 (void) ftpd_pclose(fin);
854 reply(211, "End of Status");
855}
856
857statcmd()
858{
859 struct sockaddr_in *sin;
860 u_char *a, *p;
861
862 lreply(211, "%s FTP server status:", hostname, version);
863 printf(" %s\r\n", version);
864 printf(" Connected to %s", remotehost);
00aa66da 865 if (!isdigit(remotehost[0]))
fdb56acd
MK
866 printf(" (%s)", inet_ntoa(his_addr.sin_addr));
867 printf("\r\n");
868 if (logged_in) {
869 if (guest)
870 printf(" Logged in anonymously\r\n");
871 else
872 printf(" Logged in as %s\r\n", pw->pw_name);
873 } else if (askpasswd)
874 printf(" Waiting for password\r\n");
875 else
876 printf(" Waiting for user name\r\n");
877 printf(" TYPE: %s", typenames[type]);
878 if (type == TYPE_A || type == TYPE_E)
879 printf(", FORM: %s", formnames[form]);
880 if (type == TYPE_L)
881#if NBBY == 8
882 printf(" %d", NBBY);
883#else
884 printf(" %d", bytesize); /* need definition! */
885#endif
886 printf("; STRUcture: %s; transfer MODE: %s\r\n",
887 strunames[stru], modenames[mode]);
888 if (data != -1)
889 printf(" Data connection open\r\n");
890 else if (pdata != -1) {
891 printf(" in Passive mode");
892 sin = &pasv_addr;
893 goto printaddr;
894 } else if (usedefault == 0) {
895 printf(" PORT");
896 sin = &data_dest;
897printaddr:
898 a = (u_char *) &sin->sin_addr;
899 p = (u_char *) &sin->sin_port;
900#define UC(b) (((int) b) & 0xff)
901 printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]),
902 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
903#undef UC
904 } else
905 printf(" No data connection\r\n");
906 reply(211, "End of status");
c6d3d85f
SL
907}
908
909fatal(s)
910 char *s;
911{
912 reply(451, "Error in server: %s\n", s);
913 reply(221, "Closing connection due to server error.");
bb16805b 914 dologout(0);
843d1a1c 915 /* NOTREACHED */
c6d3d85f
SL
916}
917
b5993e33
KB
918/* VARARGS2 */
919reply(n, fmt, p0, p1, p2, p3, p4, p5)
c6d3d85f 920 int n;
b5993e33 921 char *fmt;
c6d3d85f 922{
c6d3d85f 923 printf("%d ", n);
b5993e33 924 printf(fmt, p0, p1, p2, p3, p4, p5);
c6d3d85f 925 printf("\r\n");
23fe6e13 926 (void)fflush(stdout);
c6d3d85f 927 if (debug) {
df2f99b4 928 syslog(LOG_DEBUG, "<--- %d ", n);
b5993e33 929 syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
843d1a1c 930}
c6d3d85f
SL
931}
932
b5993e33
KB
933/* VARARGS2 */
934lreply(n, fmt, p0, p1, p2, p3, p4, p5)
c6d3d85f 935 int n;
b5993e33 936 char *fmt;
c6d3d85f 937{
b5993e33
KB
938 printf("%d- ", n);
939 printf(fmt, p0, p1, p2, p3, p4, p5);
940 printf("\r\n");
23fe6e13 941 (void)fflush(stdout);
b5993e33
KB
942 if (debug) {
943 syslog(LOG_DEBUG, "<--- %d- ", n);
944 syslog(LOG_DEBUG, fmt, p0, p1, p2, p3, p4, p5);
945 }
c6d3d85f
SL
946}
947
c6d3d85f
SL
948ack(s)
949 char *s;
950{
7ccaf1e3 951 reply(250, "%s command successful.", s);
c6d3d85f
SL
952}
953
954nack(s)
955 char *s;
956{
957 reply(502, "%s command not implemented.", s);
958}
959
882508af 960/* ARGSUSED */
df2f99b4
GM
961yyerror(s)
962 char *s;
c6d3d85f 963{
07fffe50
GM
964 char *cp;
965
139367fb
KB
966 if (cp = index(cbuf,'\n'))
967 *cp = '\0';
fdb56acd 968 reply(500, "'%s': command not understood.", cbuf);
c6d3d85f
SL
969}
970
971delete(name)
972 char *name;
973{
974 struct stat st;
975
976 if (stat(name, &st) < 0) {
882508af 977 perror_reply(550, name);
c6d3d85f
SL
978 return;
979 }
980 if ((st.st_mode&S_IFMT) == S_IFDIR) {
981 if (rmdir(name) < 0) {
882508af 982 perror_reply(550, name);
c6d3d85f
SL
983 return;
984 }
985 goto done;
986 }
987 if (unlink(name) < 0) {
882508af 988 perror_reply(550, name);
c6d3d85f
SL
989 return;
990 }
991done:
992 ack("DELE");
993}
994
995cwd(path)
996 char *path;
997{
843d1a1c 998 if (chdir(path) < 0)
882508af 999 perror_reply(550, path);
843d1a1c
RA
1000 else
1001 ack("CWD");
c6d3d85f
SL
1002}
1003
aa159ba8 1004makedir(name)
c6d3d85f
SL
1005 char *name;
1006{
5256bbac 1007 if (mkdir(name, 0777) < 0)
882508af 1008 perror_reply(550, name);
5256bbac
KB
1009 else
1010 reply(257, "MKD command successful.");
c6d3d85f
SL
1011}
1012
aa159ba8 1013removedir(name)
c6d3d85f
SL
1014 char *name;
1015{
843d1a1c 1016 if (rmdir(name) < 0)
882508af 1017 perror_reply(550, name);
843d1a1c
RA
1018 else
1019 ack("RMD");
c6d3d85f
SL
1020}
1021
aa159ba8 1022pwd()
c6d3d85f 1023{
aa159ba8 1024 char path[MAXPATHLEN + 1];
882508af 1025 extern char *getwd();
c6d3d85f 1026
843d1a1c 1027 if (getwd(path) == (char *)NULL)
7ccaf1e3 1028 reply(550, "%s.", path);
843d1a1c
RA
1029 else
1030 reply(257, "\"%s\" is current directory.", path);
c6d3d85f
SL
1031}
1032
1033char *
1034renamefrom(name)
1035 char *name;
1036{
1037 struct stat st;
1038
1039 if (stat(name, &st) < 0) {
882508af 1040 perror_reply(550, name);
c6d3d85f
SL
1041 return ((char *)0);
1042 }
aa159ba8 1043 reply(350, "File exists, ready for destination name");
c6d3d85f
SL
1044 return (name);
1045}
1046
1047renamecmd(from, to)
1048 char *from, *to;
1049{
843d1a1c 1050 if (rename(from, to) < 0)
882508af 1051 perror_reply(550, "rename");
843d1a1c
RA
1052 else
1053 ack("RNTO");
c6d3d85f
SL
1054}
1055
c6d3d85f
SL
1056dolog(sin)
1057 struct sockaddr_in *sin;
1058{
882508af 1059 struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
c6d3d85f 1060 sizeof (struct in_addr), AF_INET);
882508af 1061 time_t t, time();
df2f99b4 1062 extern char *ctime();
c6d3d85f 1063
882508af 1064 if (hp)
df2f99b4 1065 (void) strncpy(remotehost, hp->h_name, sizeof (remotehost));
882508af 1066 else
df2f99b4 1067 (void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
bb16805b 1068 sizeof (remotehost));
843d1a1c 1069#ifdef SETPROCTITLE
fdb56acd
MK
1070 sprintf(proctitle, "%s: connected", remotehost);
1071 setproctitle(proctitle);
843d1a1c 1072#endif /* SETPROCTITLE */
fdb56acd
MK
1073
1074 if (logging) {
1075 t = time((time_t *) 0);
1076 syslog(LOG_INFO, "connection from %s at %s",
1077 remotehost, ctime(&t));
1078 }
c6d3d85f 1079}
0597ed04 1080
bb16805b
SL
1081/*
1082 * Record logout in wtmp file
1083 * and exit with supplied status.
1084 */
1085dologout(status)
1086 int status;
1087{
632ff766 1088 if (logged_in) {
882508af 1089 (void) seteuid((uid_t)0);
b206b3b1 1090 logwtmp(ttyline, "", "");
bb16805b 1091 }
930b478b
SL
1092 /* beware of flushing buffers after a SIGPIPE */
1093 _exit(status);
bb16805b
SL
1094}
1095
07fffe50
GM
1096myoob()
1097{
d0604b39 1098 char *cp;
07fffe50 1099
d0604b39 1100 /* only process if transfer occurring */
882508af 1101 if (!transflag)
07fffe50 1102 return;
07fffe50 1103 cp = tmpline;
d0604b39 1104 if (getline(cp, 7, stdin) == NULL) {
882508af 1105 reply(221, "You could at least say goodbye.");
d0604b39
GM
1106 dologout(0);
1107 }
07fffe50 1108 upper(cp);
fdb56acd
MK
1109 if (strcmp(cp, "ABOR\r\n") == 0) {
1110 tmpline[0] = '\0';
1111 reply(426, "Transfer aborted. Data connection closed.");
1112 reply(226, "Abort successful");
1113 longjmp(urgcatch, 1);
1114 }
1115 if (strcmp(cp, "STAT\r\n") == 0) {
1116 if (file_size != (off_t) -1)
1117 reply(213, "Status: %lu of %lu bytes transferred",
1118 byte_count, file_size);
1119 else
1120 reply(213, "Status: %lu bytes transferred", byte_count);
1121 }
07fffe50
GM
1122}
1123
7ccaf1e3 1124/*
843d1a1c
RA
1125 * Note: a response of 425 is not mentioned as a possible response to
1126 * the PASV command in RFC959. However, it has been blessed as
1127 * a legitimate response by Jon Postel in a telephone conversation
1128 * with Rick Adams on 25 Jan 89.
7ccaf1e3 1129 */
07fffe50
GM
1130passive()
1131{
1132 int len;
07fffe50
GM
1133 register char *p, *a;
1134
1135 pdata = socket(AF_INET, SOCK_STREAM, 0);
1136 if (pdata < 0) {
843d1a1c 1137 perror_reply(425, "Can't open passive connection");
07fffe50
GM
1138 return;
1139 }
fdb56acd
MK
1140 pasv_addr = ctrl_addr;
1141 pasv_addr.sin_port = 0;
882508af 1142 (void) seteuid((uid_t)0);
fdb56acd 1143 if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) {
882508af 1144 (void) seteuid((uid_t)pw->pw_uid);
843d1a1c 1145 goto pasv_error;
07fffe50 1146 }
882508af 1147 (void) seteuid((uid_t)pw->pw_uid);
fdb56acd
MK
1148 len = sizeof(pasv_addr);
1149 if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
843d1a1c
RA
1150 goto pasv_error;
1151 if (listen(pdata, 1) < 0)
1152 goto pasv_error;
fdb56acd
MK
1153 a = (char *) &pasv_addr.sin_addr;
1154 p = (char *) &pasv_addr.sin_port;
07fffe50
GM
1155
1156#define UC(b) (((int) b) & 0xff)
1157
1158 reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
1159 UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
843d1a1c
RA
1160 return;
1161
1162pasv_error:
1163 (void) close(pdata);
1164 pdata = -1;
1165 perror_reply(425, "Can't open passive connection");
1166 return;
07fffe50
GM
1167}
1168
882508af
MK
1169/*
1170 * Generate unique name for file with basename "local".
1171 * The file named "local" is already known to exist.
1172 * Generates failure reply on error.
1173 */
07fffe50
GM
1174char *
1175gunique(local)
1176 char *local;
1177{
1178 static char new[MAXPATHLEN];
882508af 1179 struct stat st;
07fffe50 1180 char *cp = rindex(local, '/');
fdb56acd 1181 int count = 0;
07fffe50 1182
882508af 1183 if (cp)
07fffe50 1184 *cp = '\0';
843d1a1c 1185 if (stat(cp ? local : ".", &st) < 0) {
fdb56acd 1186 perror_reply(553, cp ? local : ".");
07fffe50
GM
1187 return((char *) 0);
1188 }
843d1a1c
RA
1189 if (cp)
1190 *cp = '/';
07fffe50
GM
1191 (void) strcpy(new, local);
1192 cp = new + strlen(new);
1193 *cp++ = '.';
882508af
MK
1194 for (count = 1; count < 100; count++) {
1195 (void) sprintf(cp, "%d", count);
1196 if (stat(new, &st) < 0)
1197 return(new);
07fffe50 1198 }
882508af
MK
1199 reply(452, "Unique file name cannot be created.");
1200 return((char *) 0);
1201}
1202
1203/*
1204 * Format and send reply containing system error number.
1205 */
1206perror_reply(code, string)
1207 int code;
1208 char *string;
1209{
396aa79f 1210 reply(code, "%s: %s.", string, strerror(errno));
07fffe50 1211}
843d1a1c
RA
1212
1213static char *onefile[] = {
1214 "",
1215 0
1216};
1217
1218send_file_list(whichfiles)
1219 char *whichfiles;
1220{
1221 struct stat st;
1222 DIR *dirp = NULL;
1223 struct direct *dir;
1224 FILE *dout = NULL;
1225 register char **dirlist, *dirname;
e1ea1692 1226 int simple = 0;
843d1a1c
RA
1227 char *strpbrk();
1228
1229 if (strpbrk(whichfiles, "~{[*?") != NULL) {
1230 extern char **glob(), *globerr;
fdb56acd 1231
843d1a1c
RA
1232 globerr = NULL;
1233 dirlist = glob(whichfiles);
1234 if (globerr != NULL) {
1235 reply(550, globerr);
1236 return;
1237 } else if (dirlist == NULL) {
1238 errno = ENOENT;
1239 perror_reply(550, whichfiles);
1240 return;
1241 }
1242 } else {
1243 onefile[0] = whichfiles;
1244 dirlist = onefile;
e1ea1692 1245 simple = 1;
843d1a1c 1246 }
fdb56acd
MK
1247
1248 if (setjmp(urgcatch)) {
1249 transflag = 0;
1250 return;
1251 }
843d1a1c
RA
1252 while (dirname = *dirlist++) {
1253 if (stat(dirname, &st) < 0) {
fdb56acd
MK
1254 /*
1255 * If user typed "ls -l", etc, and the client
1256 * used NLST, do what the user meant.
1257 */
1258 if (dirname[0] == '-' && *dirlist == NULL &&
1259 transflag == 0) {
1260 retrieve("/bin/ls %s", dirname);
1261 return;
1262 }
843d1a1c
RA
1263 perror_reply(550, whichfiles);
1264 if (dout != NULL) {
1265 (void) fclose(dout);
fdb56acd 1266 transflag = 0;
843d1a1c
RA
1267 data = -1;
1268 pdata = -1;
1269 }
1270 return;
1271 }
fdb56acd 1272
843d1a1c
RA
1273 if ((st.st_mode&S_IFMT) == S_IFREG) {
1274 if (dout == NULL) {
e1ea1692 1275 dout = dataconn("file list", (off_t)-1, "w");
843d1a1c
RA
1276 if (dout == NULL)
1277 return;
fdb56acd 1278 transflag++;
843d1a1c 1279 }
3b9b8bb1
RA
1280 fprintf(dout, "%s%s\n", dirname,
1281 type == TYPE_A ? "\r" : "");
fdb56acd 1282 byte_count += strlen(dirname) + 1;
843d1a1c 1283 continue;
fdb56acd 1284 } else if ((st.st_mode&S_IFMT) != S_IFDIR)
843d1a1c
RA
1285 continue;
1286
1287 if ((dirp = opendir(dirname)) == NULL)
1288 continue;
1289
1290 while ((dir = readdir(dirp)) != NULL) {
fdb56acd 1291 char nbuf[MAXPATHLEN];
843d1a1c
RA
1292
1293 if (dir->d_name[0] == '.' && dir->d_namlen == 1)
1294 continue;
fdb56acd
MK
1295 if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
1296 dir->d_namlen == 2)
843d1a1c
RA
1297 continue;
1298
fdb56acd
MK
1299 sprintf(nbuf, "%s/%s", dirname, dir->d_name);
1300
843d1a1c 1301 /*
fdb56acd
MK
1302 * We have to do a stat to insure it's
1303 * not a directory or special file.
843d1a1c 1304 */
e1ea1692
MK
1305 if (simple || (stat(nbuf, &st) == 0 &&
1306 (st.st_mode&S_IFMT) == S_IFREG)) {
843d1a1c 1307 if (dout == NULL) {
e1ea1692 1308 dout = dataconn("file list", (off_t)-1,
843d1a1c
RA
1309 "w");
1310 if (dout == NULL)
1311 return;
fdb56acd 1312 transflag++;
843d1a1c
RA
1313 }
1314 if (nbuf[0] == '.' && nbuf[1] == '/')
3b9b8bb1
RA
1315 fprintf(dout, "%s%s\n", &nbuf[2],
1316 type == TYPE_A ? "\r" : "");
843d1a1c 1317 else
3b9b8bb1
RA
1318 fprintf(dout, "%s%s\n", nbuf,
1319 type == TYPE_A ? "\r" : "");
fdb56acd 1320 byte_count += strlen(nbuf) + 1;
843d1a1c
RA
1321 }
1322 }
1323 (void) closedir(dirp);
1324 }
1325
fdb56acd
MK
1326 if (dout == NULL)
1327 reply(550, "No files found.");
1328 else if (ferror(dout) != 0)
1329 perror_reply(550, "Data connection");
1330 else
843d1a1c
RA
1331 reply(226, "Transfer complete.");
1332
fdb56acd
MK
1333 transflag = 0;
1334 if (dout != NULL)
843d1a1c
RA
1335 (void) fclose(dout);
1336 data = -1;
1337 pdata = -1;
1338}
1339
1340#ifdef SETPROCTITLE
1341/*
1342 * clobber argv so ps will show what we're doing.
1343 * (stolen from sendmail)
1344 * warning, since this is usually started from inetd.conf, it
1345 * often doesn't have much of an environment or arglist to overwrite.
1346 */
1347
1348/*VARARGS1*/
1349setproctitle(fmt, a, b, c)
1350char *fmt;
1351{
1352 register char *p, *bp, ch;
1353 register int i;
1354 char buf[BUFSIZ];
1355
1356 (void) sprintf(buf, fmt, a, b, c);
1357
1358 /* make ps print our process name */
1359 p = Argv[0];
1360 *p++ = '-';
1361
1362 i = strlen(buf);
1363 if (i > LastArgv - p - 2) {
1364 i = LastArgv - p - 2;
1365 buf[i] = '\0';
1366 }
1367 bp = buf;
1368 while (ch = *bp++)
1369 if (ch != '\n' && ch != '\r')
1370 *p++ = ch;
1371 while (p < LastArgv)
1372 *p++ = ' ';
1373}
1374#endif /* SETPROCTITLE */