]> git.sesse.net Git - betaftpd/blob - cmds.c
Implemented directory listing recursion (not very much testing yet). Both mmap()...
[betaftpd] / cmds.c
1 /*  cmds.c: BetaFTPD command handlers
2     Copyright (C) 1999-2000 Steinar H. Gunderson
3
4     This program is is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License, version 2 if the
6     License as published by the Free Software Foundation.
7
8     This program is distributed in the hope that it will be useful,
9     but WITHOUT ANY WARRANTY; without even the implied warranty of
10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11     GNU General Public License for more details.
12
13     You should have received a copy of the GNU General Public License
14     along with this program; if not, write to the Free Software
15     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 */
17
18 #define _GNU_SOURCE
19
20 #if HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #if HAVE_SYS_TYPES_H
25 #include <sys/types.h>
26 #endif
27
28 #if HAVE_STROPTS_H
29 #include <stropts.h>
30 #endif
31
32 #if HAVE_SYS_CONF_H
33 #include <sys/conf.h>
34 #endif
35
36 #if HAVE_DIRENT_H
37 #include <dirent.h>
38 #endif
39
40 #if HAVE_CRYPT_H
41 #include <crypt.h>
42 #endif
43
44 #if HAVE_ERRNO_H
45 #include <errno.h>
46 #endif
47
48 #if HAVE_GLOB_H
49 #include <glob.h>
50 #endif
51
52 #if HAVE_STDIO_H
53 #include <stdio.h>
54 #endif
55
56 #if HAVE_STDLIB_H
57 #include <stdlib.h>
58 #endif
59
60 #if HAVE_STRING_H
61 #include <string.h>
62 #endif
63
64 #if HAVE_STRINGS_H
65 #include <strings.h>
66 #endif
67
68 #if HAVE_UNISTD_H
69 #include <unistd.h>
70 #endif
71
72 #if HAVE_TIME_H
73 #include <time.h>
74 #endif
75
76 #if HAVE_FCNTL_H
77 #include <fcntl.h>
78 #endif
79
80 #if HAVE_PWD_H
81 #include <pwd.h>
82 #endif
83
84 #if HAVE_GRP_H
85 #include <grp.h>
86 #endif
87
88 #if HAVE_SYS_IOCTL_H
89 #include <sys/ioctl.h>
90 #endif
91
92 #if HAVE_SYS_SOCKET_H
93 #include <sys/socket.h>
94 #endif
95
96 #if HAVE_SYS_STAT_H
97 #include <sys/stat.h>
98 #endif
99
100 #if HAVE_SYS_PARAM_H
101 #include <sys/param.h>
102 #endif
103
104 #if HAVE_STROPTS_H
105 #include <stropts.h>
106 #endif
107
108 #if HAVE_SYS_CONF_H
109 #include <sys/conf.h>
110 #endif
111
112 #if HAVE_NETINET_IN_H
113 #include <netinet/in.h>
114 #endif
115
116 #if HAVE_SHADOW_H
117 #include <shadow.h>
118 #endif
119
120 #if HAVE_SYS_FILIO_H
121 #include <sys/filio.h>
122 #endif
123
124 #if HAVE_SYS_POLL_H
125 #include <sys/poll.h>
126 #endif
127
128 #if WANT_NONROOT
129 #define NO_SETUID
130 #define DO_SETUID
131 #else
132 #define NO_SETUID ,0
133 #define DO_SETUID ,1
134 #endif
135
136 #ifndef NBBY
137 #define NBBY 8
138 #endif
139
140 #include <ftpd.h>
141 #include <cmds.h>
142 #include <nonroot.h>
143
144 #define lstat stat
145
146 extern struct conn *first_conn;
147 #if WANT_DCACHE
148 extern struct dcache *first_dcache;
149 #endif
150
151 #if HAVE_POLL
152 extern struct pollfd fds[];
153 #else
154 extern fd_set master_fds, master_send_fds;
155 #endif
156
157 struct handler {
158         char cmd_name[6];
159         char add_cmlen;         /* =1 if the command takes an argument */
160         int (*callback)(struct conn * const);
161         char min_auth;
162 #if !WANT_NONROOT
163         char do_setuid;         /* =1 if root is not *really* needed */
164 #endif
165 };
166
167 static const struct handler handler_table[] = {
168         { "user ", 1, cmd_user, 0       NO_SETUID },
169         { "pass ", 1, cmd_pass, 1       NO_SETUID },
170         { "retr ", 1, cmd_retr, 3       DO_SETUID },
171         { "acct ", 1, cmd_acct, 0       NO_SETUID },
172         { "port ", 1, cmd_port, 3       DO_SETUID },
173         { "pasv" , 0, cmd_pasv, 3       DO_SETUID },
174         { "pwd"  , 0, cmd_pwd,  3       DO_SETUID },
175         { "cwd " , 1, cmd_cwd,  3       DO_SETUID },
176         { "cdup" , 0, cmd_cdup, 3       DO_SETUID },
177         { "rest ", 1, cmd_rest, 3       DO_SETUID },
178         { "list" , 0, cmd_list, 3       DO_SETUID },
179         { "nlst" , 0, cmd_nlst, 3       DO_SETUID },
180         { "type ", 1, cmd_type, 3       DO_SETUID },
181         { "mode ", 1, cmd_mode, 3       DO_SETUID },
182         { "stru ", 1, cmd_stru, 3       DO_SETUID },
183         { "size ", 1, cmd_size, 3       DO_SETUID },
184         { "mdtm ", 1, cmd_mdtm, 3       DO_SETUID },
185         { "abor" , 0, cmd_abor, 3       DO_SETUID },
186         { "dele ", 1, cmd_dele, 3       DO_SETUID },
187         { "rnfr ", 1, cmd_rnfr, 3       DO_SETUID },
188         { "rnto ", 1, cmd_rnto, 3       DO_SETUID },
189         { "mkd " , 1, cmd_mkd,  3       DO_SETUID },
190         { "rmd " , 1, cmd_rmd,  3       DO_SETUID },
191         { "allo ", 1, cmd_allo, 3       DO_SETUID },
192         { "stat" , 0, cmd_stat, 0       NO_SETUID },
193         { "noop" , 0, cmd_noop, 0       DO_SETUID },
194         { "syst" , 0, cmd_syst, 0       DO_SETUID },
195         { "help" , 0, cmd_help, 0       NO_SETUID },
196         { "quit" , 0, cmd_quit, 0       DO_SETUID },
197         { "rein" , 0, cmd_rein, 0       DO_SETUID },
198
199         /* deprecated forms */
200         { "xcup" , 0, cmd_cdup, 3       DO_SETUID },
201         { "xcwd ", 1, cmd_cwd,  3       DO_SETUID },
202         { "xpwd" , 0, cmd_pwd,  3       DO_SETUID },
203         { "xmkd ", 1, cmd_mkd,  3       DO_SETUID },
204         { "xrmd ", 1, cmd_rmd,  3       DO_SETUID },
205 #if WANT_UPLOAD
206         { "stor ", 1, cmd_stor, 3       DO_SETUID },
207         { "appe ", 1, cmd_appe, 3       DO_SETUID },
208 #endif
209 #if DOING_PROFILING
210 #warning Use DOING_PROFILING with caution, and NEVER on a production server! :-)
211         { "exit",  0, cmd_exit, 0       NO_SETUID },
212 #endif
213         { ""    ,  0, NULL,     0       NO_SETUID }
214 };
215
216 /*
217  * do_chdir():  Does a chdir() to newd on c, staying inside the
218  *              limits of root_dir. Use this instead of a chdir() whenever
219  *              you can, and possibly even when you can't :-)
220  *
221  *              This command quirks around some problems in the rest of
222  *              the code (namely translate_path()), so a blank newdir is
223  *              interpreted as the root directory.
224  */
225 int do_chdir(struct conn * const c, const char * const newd)
226 {
227         char chd[512], temp[512];
228
229         TRAP_ERROR(chdir(c->curr_dir) == -1, 550, return -1);
230
231         /* handle `absolute' paths */
232         if (newd[0] == '/' || newd[0] == '\0') {
233                 strcpy(temp, c->root_dir);
234
235                 /*
236                  * is this the root directory? if not, remove the trailing `/'
237                  * and concatenate the new directory on
238                  */
239                 if (newd[1] != '\0' && newd[0] != '\0') {
240                         temp[strlen(temp) - 1] = 0;
241                         strcat(temp, newd);
242                 }
243         } else {
244                 strcpy(temp, newd);
245         }
246
247 #if WANT_NONROOT
248         if (nr_check_permission(c->uid, temp, 1, 1, NULL) == -1) {
249                 numeric(c, 550, "Permission denied");
250                 return -1;
251         }
252 #endif
253
254         TRAP_ERROR(chdir(temp) == -1, 550, return -1);
255
256         getcwd(chd, 254);
257         if (chd[strlen(chd) - 1] != '/') {
258                 strcat(chd, "/");
259         }
260
261         if (strncmp(chd, c->root_dir, strlen(c->root_dir)) != 0) {
262                 numeric(c, 550, "No such file or directory.");
263                 return -1;
264         }
265
266         return 0;
267 }
268
269 /*
270  * cmd_user():  Handles the USER command, and does most of the initial
271  *              authentication work. User names are limited to 16
272  *              characters, by force...
273  */
274 int cmd_user(struct conn * const c)
275 {
276         strncpy(c->username, c->recv_buf, 16);
277         c->username[16] = 0;
278
279         if (strcasecmp(c->username, "anonymous") == 0) {
280                 strcpy(c->username, "ftp");
281         }
282         if (strcasecmp(c->username, "ftp") == 0) {
283                 numeric(c, 331, "Login OK, send password (your e-mail).");
284                 c->auth = 1;
285         } else {
286                 numeric(c, 331, "Password required for %s.", c->username);
287                 c->auth = 2;
288         }
289         return 1;
290 }
291
292 /*
293  * cmd_pass():  Handles the PASS command, and checks the password.
294  *              This function is rather long and complicated, mostly
295  *              because there are so many ways of doing users
296  *              (including my nonroot system) out there... And we
297  *              don't even support PAM or real shadow passwords (with
298  *              expiry etc) yet...
299  */
300 int cmd_pass(struct conn * const c)
301 {
302 #if WANT_NONROOT
303         c->auth = nr_userinfo(c->username, &c->uid, c->curr_dir, c->root_dir,
304                               c->recv_buf);
305 #else /* !WANT_NONROOT */
306 #if WANT_SHADOW && HAVE_SHADOW_H
307         struct spwd *s;
308 #endif
309         struct passwd *p;
310
311         p = getpwnam(c->username);
312 #if WANT_SHADOW && HAVE_SHADOW_H
313         s = getspnam(c->username);
314 #endif
315         
316         if (p == NULL) {
317                 c->auth = 0;
318         } else {
319                 c->uid = p->pw_uid;
320                 strncpy(c->curr_dir, p->pw_dir, 254);
321                 c->curr_dir[254] = 0;
322         }
323
324         if (c->auth == 1) {
325                 if (c->curr_dir[strlen(c->curr_dir) - 1] != '/') {
326                         strcat(c->curr_dir, "/");
327                 }
328                 strcpy(c->root_dir, c->curr_dir);       
329                 c->auth = 3;
330         } else if (c->auth != 0) {
331                 strcpy(c->root_dir, "/");
332                 if (strcmp(crypt(c->recv_buf, p->pw_passwd), p->pw_passwd) != 0
333 #if WANT_SHADOW && HAVE_SHADOW_H
334                     && (s == NULL || strcmp(crypt(c->recv_buf, s->sp_pwdp), s->sp_pwdp) != 0)
335 #endif
336                 ) {
337                         c->auth = 0;
338                 } else {
339                         c->auth = 3;
340                 }
341         }
342 #endif /* !WANT_NONROOT */
343
344         /* root should not be allowed to FTP */
345         if (c->uid == 0) {
346                 c->auth = 0;
347         }
348         if (c->auth == 0) {
349                 numeric(c, 530, "Login incorrect.");
350         } else {
351 #if WANT_MESSAGE
352                 chdir(c->curr_dir);
353                 dump_file(c, 230, "welcome.msg");
354 #endif
355                 numeric(c, 230, "User logged in.");
356         }
357         return 1;
358 }
359
360 /*
361  * cmd_acct():  Handle (ignore) the ACCT command. I don't see how we
362  *              could make use of this command... wu-ftpd doesn't, either.
363  *              However, wu-ftpd (at least the version I have here) uses
364  *              502, which isn't a legal error code according to RFC959.
365  *              202, on the other hand, is, and seems to be applicable.
366  *
367  *              I'm unsure if this one should require setuid or not, but
368  *              I feel that the RFC959 intention is having it _before_
369  *              USER/PASS. Therefore, this one runs with root privilegies :-)
370  */
371 int cmd_acct(struct conn * const c)
372 {
373         numeric(c, 202, "ACCT ignored OK -- not applicable on this system.");
374         return 1;
375 }
376
377 /*
378  * cmd_port():  Handles the PORT command, and sets up the data socket.
379  *              Making a brief uid=0 (root) appearance (to bind the socket) --
380  *              I feel it's safer that way (instead of running as root
381  *              the whole way), in case there are some weird overflows
382  *              somewhere.
383  */
384 int cmd_port(struct conn * const c)
385 {
386         short int a0, a1, a2, a3, p0, p1;
387         int i, sock, err;
388         struct ftran *f;
389         struct sockaddr_in sin;
390     
391         if ((c->transfer != NULL) && (c->transfer->state >= 4)) {
392                 numeric(c, 500, "Sorry, only one transfer at a time.");
393                 return 1;
394         }
395
396         sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
397         TRAP_ERROR(sock == -1, 500, return 1);
398
399         destroy_ftran(c->transfer);
400         c->transfer = f = alloc_new_ftran(sock, c);
401
402         i = sscanf(c->recv_buf, "%3hu,%3hu,%3hu,%3hu,%3hu,%3hu", &a0, &a1, &a2, &a3, &p0, &p1);
403         if (i < 6) {
404                 numeric(c, 501, "Parse error.");
405         } else {
406                 const int one = 1;
407                 int tmp;
408
409                 /* bind to own address, port 20 (FTP data) */
410                 tmp = sizeof(sin);
411                 err = getsockname(c->sock, (struct sockaddr *)&sin, &tmp);
412                 TRAP_ERROR(err == -1, 500, return 1);
413                 sin.sin_port = FTP_PORT - 1;
414
415                 numeric(c, 200, "PORT command OK.");
416
417                 /* note that bind() might well fail, so we don't error check */
418 #if !WANT_NONROOT
419                 /* need root privilegies for a short while */
420                 seteuid(getuid());
421 #endif
422                 bind(sock, (struct sockaddr *)&sin, sizeof(sin));
423 #if !WANT_NONROOT
424                 seteuid(c->uid);
425 #endif
426
427                 f->sin.sin_family = AF_INET;
428                 f->sin.sin_addr.s_addr = htonl(
429                         ((unsigned char)(a0) << 24) +
430                         ((unsigned char)(a1) << 16) +
431                         ((unsigned char)(a2) <<  8) +
432                         ((unsigned char)(a3)      ));
433                 f->sin.sin_port = htons(
434                         ((unsigned char)(p0) << 8) +
435                         ((unsigned char)(p1)     ));
436                 f->sock = sock;
437                 f->state = 3;
438
439                 i = 1;          
440                 ioctl(f->sock, FIONBIO, &one);
441         }
442         return 1;
443 }
444
445 /*
446  * cmd_pasv():  Handles the PASV command, and sets up the data socket.
447  *              Uses port numbers > 1024, since it doesn't run as root.
448  */
449 int cmd_pasv(struct conn * const c)
450 {
451         struct ftran *f;
452         int tmp, sock, err;
453         unsigned int one = 1;
454         struct sockaddr_in addr;
455
456         if ((c->transfer != NULL) && (c->transfer->state >= 4)) {
457                 numeric(c, 503, "Sorry, only one transfer at once.");
458                 return 1;
459         }
460         destroy_ftran(c->transfer);
461
462         sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
463         TRAP_ERROR(sock == -1, 500, return 1);
464         err = add_fd(sock, POLLIN);
465         TRAP_ERROR(err != 0, 501, return 1);
466
467         c->transfer = f = alloc_new_ftran(sock, c);
468
469         ioctl(sock, FIONBIO, &one);
470
471         /* setup socket */
472         tmp = sizeof(addr);
473         err = getsockname(c->sock, (struct sockaddr *)&addr, &tmp);
474         TRAP_ERROR(err == -1, 500, return 1);
475
476         addr.sin_port = 0;      /* let the system choose */
477         err = bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr));
478         TRAP_ERROR(err == -1, 500, return 1);
479
480         tmp = sizeof(addr);
481         err = getsockname(sock, (struct sockaddr *)&addr, &tmp);
482         TRAP_ERROR(err == -1, 500, return 1);
483
484         err = listen(f->sock, 1);
485         TRAP_ERROR(err == -1, 500, return 1);
486         f->state = 1;
487
488         numeric(c, 227, "Entering passive mode (%u,%u,%u,%u,%u,%u)",
489                 (htonl(addr.sin_addr.s_addr) & 0xff000000) >> 24,
490                 (htonl(addr.sin_addr.s_addr) & 0x00ff0000) >> 16,
491                 (htonl(addr.sin_addr.s_addr) & 0x0000ff00) >>  8,
492                 (htonl(addr.sin_addr.s_addr) & 0x000000ff),
493                 (htons(addr.sin_port) & 0xff00) >> 8,
494                 (htons(addr.sin_port) & 0x00ff));
495         return 1;
496 }
497
498 /*
499  * cmd_pwd():   Handles PWD command (print working directory).
500  *
501  *              Note that if somebody contacts you with the message `the server
502  *              says curr_dir() is outside root_dir()', you should fix your
503  *              /betaftpd.users file, if you use nonroot. If not, it's a bug.
504  *              Try to get it _reproducible_, and mail it to me.
505  */
506 int cmd_pwd(struct conn * const c)
507 {
508         char temp[512], *cdir = NULL;
509
510         cdir = do_pwd(c, temp, c->curr_dir);
511         if (cdir != NULL) {
512                 numeric(c, 257, "\"%s\" is current working directory.", cdir);
513         }
514         return 1;
515 }
516
517 /*
518  * do_pwd():    Translates an absolute path to a path suitable for viewing
519  *              to the user (ie. removes the root_dir, and removes a trailing
520  *              slash if it exists). Note that the retbuf is only used as a
521  *              storage place -- the pointer to the right place within retbuf
522  *              is _returned_.
523  */
524 char *do_pwd(struct conn * const c, char * const retbuf, const char * const dir) 
525 {
526         char *cdir = NULL;
527
528         strcpy(retbuf, dir);
529         if (strncmp(retbuf, c->root_dir, strlen(c->root_dir)) != 0) {
530                 numeric(c, 550, "curr_dir is outside root_dir, please contact site administrator.");
531                 return NULL;
532         }
533
534         cdir = retbuf + strlen(c->root_dir) - 1;
535         if (cdir[strlen(cdir) - 1] == '/' && strlen(cdir) > 1) {
536                 cdir[strlen(cdir) - 1] = 0;
537         } else if (strlen(cdir) == 0) {
538                 strcpy(cdir, "/");
539         }       
540
541         return cdir;
542 }
543
544 /*
545  * cmd_cwd():   Handles CWD command (change working directory). Uses
546  *              cmd_cwd_internal() (see below).
547  */
548 int cmd_cwd(struct conn * const c)
549 {
550         cmd_cwd_internal(c, c->recv_buf);
551         return 1;
552 }
553
554 /*
555  * cmd_cdup():  Handles a CDUP command (identical to `CWD ..'). Note that
556  *              RFC959 gives two different response codes (250 and 200) --
557  *              250 is the same as CWD gives, which sounds logical to me.
558  *              wu-ftpd uses it as well.
559  *
560  *              Note that using a CDUP to try to get outside root_dir returns
561  *              an error, instead of just staying in the root directory (as
562  *              the OS and thus wu-ftpd does).
563  */
564 int cmd_cdup(struct conn * const c)
565 {
566         cmd_cwd_internal(c, "..");
567         return 1;
568 }
569
570 /*
571  * cmd_cwd_internal():
572  *              Does the work for CWD and CDUP (modularized to save some
573  *              space and have clearer code). Mostly, it just uses do_chdir(),
574  *              and sees where that takes us. It adds a trailing slash if needed.
575  */
576 void cmd_cwd_internal(struct conn * const c, const char * const newd)
577 {
578         if (do_chdir(c, newd) != -1) {
579                 int i;
580
581                 getcwd(c->curr_dir, 254);
582                 i = strlen(c->curr_dir);
583                 if (c->curr_dir[i - 1] != '/') {
584                         c->curr_dir[i++] = '/';
585                         c->curr_dir[i] = '\0';
586                 }
587
588 #if WANT_MESSAGE
589                 dump_file(c, 250, ".message");
590                 list_readmes(c);
591 #endif
592
593                 numeric(c, 250, "CWD successful.");
594         }
595 }
596
597 /*
598  * cmd_rest():  Handles the REST command. All it does is tell the file
599  *              sending functions to start at the correct number. We should
600  *              perhaps add some better error checking to this?
601  */
602 int cmd_rest(struct conn * const c)
603 {
604         c->rest_pos = abs(atoi(c->recv_buf));
605         numeric(c, 350, "Setting resume at %u bytes.", c->rest_pos);
606         return 1;
607 }
608
609 /*
610  * cmd_retr():  Handles the RETR command. This command doesn't send the
611  *              file, but it opens it and tells the socket handling code
612  *              to check for activity on the data socket. When the
613  *              connection occurs (or succeeds, if we're using PORT mode),
614  *              the actual file transfer begins.
615  */
616 int cmd_retr(struct conn * const c)
617 {
618         struct ftran *f = c->transfer;
619
620         if ((f == NULL) || ((f->state != 1) && (f->state != 3))) {
621                 numeric(c, 425, "No data connection set up; please use PASV or PORT.");
622                 return 1;
623         }
624
625 #if WANT_ASCII
626         if ((c->rest_pos > 0) && (c->ascii_mode == 1)) {
627                 numeric(c, 500, "Cannot resume while in ASCII mode.");
628                 return 1;
629         }
630 #endif
631
632         f->local_file = do_openfile(c, c->recv_buf, f->filename, O_RDONLY
633 #if WANT_NONROOT
634                 , 4
635 #endif
636         );
637         f->dir_listing = 0;
638
639         if (f->local_file == -1) {
640                 numeric(f->owner, 550, strerror(errno));
641                 destroy_ftran(f);
642         } else if (f->local_file == -2) {
643                 f->local_file = -1;
644                 destroy_ftran(f);
645         } else {
646 #if WANT_UPLOAD
647                 f->upload = 0;
648 #endif
649                 prepare_for_transfer(f);
650         }
651         return 1;
652 }
653
654 #if WANT_UPLOAD
655 /*
656  * cmd_stor():  Handles the STOR command (upload file). Pushes the
657  *              work down to do_store(), below.
658  */
659 int cmd_stor(struct conn * const c)
660 {
661         do_store(c, 0);
662         return 1;
663 }
664
665 /*
666  * cmd_appe():  Handles the APPE command (append to file). Pushes
667  *              the work down to do_store(), below.
668  */
669 int cmd_appe(struct conn * const c)
670 {
671         do_store(c, 1);
672         return 1;
673 }
674
675 /*
676  * do_store():  Initiate an upload. Most of the comments to do_retr()
677  *              (above) apply to this one as well.
678  */
679 void do_store(struct conn * const c, const int append)
680 {
681         struct ftran *f = c->transfer;
682
683         if ((f == NULL) || ((f->state != 1) && (f->state != 3))) {
684                 numeric(c, 425, "No data connection set up; please use PASV or PORT.");
685                 return;
686         }
687
688 #if WANT_ASCII
689         if ((c->rest_pos > 0) && (c->ascii_mode == 1)) {
690                 numeric(c, 500, "Cannot resume while in ASCII mode.");
691                 return;
692         }
693 #endif
694
695         f->local_file = do_openfile(c, c->recv_buf, f->filename, O_WRONLY |
696                 O_CREAT | ((append || c->rest_pos > 0) ? 0 : O_TRUNC)
697 #if WANT_NONROOT
698                 , 2
699 #endif
700         );
701         f->dir_listing = 0;
702
703         if (f->local_file == -1) {
704                 numeric(f->owner, 550, strerror(errno));
705         } else if (f->local_file == -2) {
706                 f->local_file = -1;
707         } else {
708                 f->upload = 1;
709                 f->append = append;
710 #if WANT_ASCII
711                 f->ascii_mode = c->ascii_mode;
712 #endif
713                 prepare_for_transfer(f);
714         }
715 }
716 #endif /* WANT_UPLOAD */
717
718 /*
719  * cmd_size():  Handle the SIZE command -- returns the size of a
720  *              file. Note that this command is not part of RFC959,
721  *              and thus there is no clear specification (except
722  *              for some ftpext documents, which we try to follow
723  *              as closely as we can). BetaFTPD deviates from wu-ftpd
724  *              in that it lets you check the `size' of directories
725  *              as well (instead of giving 550). This is _not_ the
726  *              size of all the files in the directory, rather how
727  *              much space the directory inode uses.
728  */
729 int cmd_size(struct conn * const c)
730 {
731 #if WANT_ASCII
732         if (c->ascii_mode) {
733                 numeric(c, 550, "SIZE not available in ASCII mode.");
734                 return 1;
735         }
736 #endif
737         {
738                 const char * const fname = translate_path(c, c->recv_buf);
739                 struct stat buf;
740         
741                 TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return 1);
742         
743                 numeric(c, 213, "%lu", (unsigned long)(buf.st_size));
744                 return 1;
745         }
746 }
747
748 /*
749  * cmd_mdtm():  Handle the MDTM command -- returns the modification
750  *              date/time of a file. See the comments on cmd_size(),
751  *              above.
752  */
753 int cmd_mdtm(struct conn * const c)
754 {
755         const char * const fname = translate_path(c, c->recv_buf);
756         struct stat buf;
757         struct tm *m;
758
759         TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return 1);
760
761         m = gmtime(&(buf.st_mtime));    /* at least wu-ftpd does it in GMT */
762         numeric(c, 213, "%u%02u%02u%02u%02u%02u", m->tm_year + 1900,
763                 m->tm_mon + 1, m->tm_mday, m->tm_hour, m->tm_min, m->tm_sec);
764         return 1;
765 }
766
767 /*
768  * cmd_abor():  Handle the ABOR command (abort a file transfer). This should
769  *              be clean enough, but isn't tested extensively.
770  */
771 int cmd_abor(struct conn * const c)
772 {
773         if (c->transfer != NULL) {
774                 numeric(c, 426, "File transfer aborted.");
775                 destroy_ftran(c->transfer);
776         }
777         numeric(c, 226, "ABOR command processed OK.");
778         return 1;
779 }
780
781 /*
782  * cmd_dele():  Handle the DELE command (delete a file).
783  */
784 int cmd_dele(struct conn * const c)
785 {
786         const char * const fname = translate_path(c, c->recv_buf);
787         
788         TRAP_ERROR(fname == NULL || unlink(fname) == -1, 550, return 1);
789         numeric(c, 250, "File deleted OK.");
790         return 1;
791 }
792
793 /*
794  * cmd_rnfr():  Handle the RNFR command (take a filename to rename from).
795  */
796 int cmd_rnfr(struct conn * const c)
797 {
798         const char * const fname = translate_path(c, c->recv_buf);
799         char cwd[256];
800         struct stat buf;
801
802         c->rename_from[0] = '\0';
803         if (fname == NULL) return 1;
804         
805         getcwd(cwd, 256);
806         snprintf(c->rename_from, 256, "%s/%s", cwd, fname);
807
808         /* Just check that the file exists. */
809         TRAP_ERROR(lstat(c->rename_from, &buf) == -1, 550, c->rename_from[0] = '\0'; return 1);
810
811         numeric(c, 350, "File exists, send RNTO.");
812         return 1;
813 }
814
815 /*
816  * cmd_rnto():  Handle the RNTO command (do the actual renaming).
817  */
818 int cmd_rnto(struct conn * const c)
819 {
820         const char * const fname = translate_path(c, c->recv_buf);
821
822         if (fname == NULL) return 1;
823         if (c->rename_from[0] == '\0') {
824                 numeric(c, 503, "Please send RNFR first.");
825                 return 1;
826         }
827
828         TRAP_ERROR(rename(c->rename_from, fname) == -1, 550, c->rename_from[0] = '\0'; return 1);
829         c->rename_from[0] = '\0';
830
831         numeric(c, 250, "File renamed successfully.");
832         return 1;
833 }
834
835 /*
836  * cmd_mkd():   Handle the MKD/XMKD command (create a new directory).
837  *              RFC959 is not clear on the error codes for this command --
838  *              one place, 521 is cited as the correct error, but is
839  *              mentioned nowhere else. Different FTP servers differ here
840  *              as well. Thus, I've followed what appears to be the intention
841  *              (having `analogous' errors with STOR), and use 550 instead.
842  *
843  *              Making directories is probably the topic covered most
844  *              extensively by RFC959 (and in the most confusing way as
845  *              well). I try to follow the conventions, but it isn't always
846  *              easy :-) (This code isn't quite easy to understand, because
847  *              temp2 is used twice, in two different roles.)
848  */
849 int cmd_mkd(struct conn * const c)
850 {
851         const char * const fname = translate_path(c, c->recv_buf);
852         char temp[512], temp2[1024], *cdir;
853         int i, j;
854
855         TRAP_ERROR(fname == NULL || mkdir(fname, 0755) == -1, 550, return 1);
856
857         chdir(fname);
858         getcwd(temp2, 512);
859         cdir = do_pwd(c, temp, temp2);
860
861         /* double the quotes in the output */ 
862         for (i = 0, j = 0; i <= strlen(cdir); i++, j++) {
863                 temp2[j] = cdir[i];
864                 if (cdir[i] == '"') {
865                         temp2[++j] = '"';
866                 }
867         }
868         numeric(c, 257, "\"%s\" created.", temp2);
869         return 1;
870 }
871
872 /*
873  * cmd_rmd():   Handle the RMD/XRMD command. Works just like DELE, only for
874  *              directories.
875  */
876 int cmd_rmd(struct conn * const c)
877 {
878         const char * const fname = translate_path(c, c->recv_buf);
879
880         TRAP_ERROR(fname == NULL || rmdir(fname) == -1, 550, return 1);
881         numeric(c, 250, "Directory deleted.");
882         return 1;
883 }
884
885 /*
886  * cmd_allo():  Handle the ALLO command. The command does not do anything, except
887  *              sit around and play compliant. Some Windows FTP servers (Serv-U,
888  *              for instance), verifies that there is enough space on the disk,
889  *              but since we have no idea on what the filesystem will be stored on,
890  *              we just ignore the command.
891  *
892  *              We could theoretically use this information to give more information
893  *              to the full-screen mode, but close to no FTP clients send this
894  *              command, and it would touch too much code.
895  */
896 int cmd_allo(struct conn * const c)
897 {
898         numeric(c, 202, "No storage allocation necessary.");
899         return 1;
900 }
901
902 /*
903  * cmd_stat():  Handle the STAT command. Please see README for more details.
904  *              Note that this command is run with euid=root, since it has
905  *              to be able to run before USER.
906  *
907  *              Note that we need to bypass numeric(), to get a multi-line
908  *              reply.
909  */
910 #if WANT_STAT
911 char conn_state[5][27] = {
912         "Not logged in",
913         "Waiting for e-mail address",
914         "Waiting for password",
915         "Logged in",
916         "Waiting for password",         /* actually non-existant user */
917 };
918
919 char ftran_state[6][42] = {
920         "Not initialized",
921         "Decided PASV address/port",
922         "Waiting on PASV socket",
923         "Got PORT address/port",
924         "Connecting on PORT address/port",
925         "Transferring file (or connecting on PORT)"
926 };
927 #endif
928
929 int cmd_stat(struct conn * const c)
930
931 #if WANT_STAT
932         char buf[1024];
933         int i, err;
934         struct ftran *f = c->transfer;
935
936         snprintf(buf, 1024, "211- FTP server status:\r\n"
937                             "     BetaFTPD version " VERSION " (http://members.xoom.com/sneeze/betaftpd.html)\r\n"
938                             "     Connected to %s\r\n"
939                             "     Control connection state: %s\r\n"
940 #if WANT_ASCII
941                             "     TYPE: %s; STRUcture: File; transfer MODE: Stream\r\n"
942 #else
943                             "     TYPE: Image; STRUcture: File; transfer MODE: Stream\r\n"
944 #endif
945                             "     Data connection state: %s\r\n"
946                             "211 End of status\r\n",
947                                 inet_ntoa(((struct sockaddr_in *)(&(c->addr)))->sin_addr),
948                                 conn_state[c->auth],
949 #if WANT_ASCII
950                                 (c->ascii_mode == 1) ? "ASCII, FORM: Nonprint" : "Image",
951 #endif
952                                 (f) ? ftran_state[f->state] : ftran_state[0]);
953
954         i = strlen(buf);
955
956         err = send(c->sock, buf, i, 0);
957         if (err == -1 && errno == EPIPE) {
958                 destroy_conn(c);
959                 return 0;
960         }
961 #else
962         numeric(c, 502, "STAT command disabled for security reasons.");
963 #endif
964         return 1;
965 }
966
967 #if HAVE_MMAP
968 /*
969  * _mwrite():   This define is for mmap-listing. It works as a write()
970  *              (not in parameter, but in function), and is used in
971  *              cmd_list() and cmd_nlst() only.
972  *
973  *              Note that this function returns the new position in the
974  *              `file'. The caller is expected to send this information
975  *              back in `pos' at the next call to _mwrite().
976  */
977 int _mwrite(const char * const buf, const struct ftran * const f,
978             const int pos, const int count, const int size)
979 {
980         if (pos + count >= size) return size;   /* out of space */
981         memcpy(f->file_data + pos, buf, count);
982         return pos + count;
983 }
984 #endif
985
986 /*
987  * mwrite:      This is a short_hand define, making calls to _mwrite() very
988  *              similiar to calls to write(). It works both with and without
989  *              mmap().
990  */
991 #if HAVE_MMAP
992 #define mwrite(buf, count) pos = _mwrite((buf), (f), (pos), (count), (size));
993 #else
994 #define mwrite(buf, count) write(f->local_file, buf, count);
995 #endif
996
997 /*
998  * long_listing():
999  *              Formats output in `ls -l' style. It returns one line for the
1000  *              file PATHNAME, and returns it in retbuf. Setting do_classify
1001  *              to nonzero has the same effect as `ls -F'.
1002  *
1003  *              This command is so long, because simply there is so much to
1004  *              be done. GNU ls has some extra functions, but it's close to
1005  *              3000 lines too...
1006  */
1007 int long_listing(char * const retbuf, const char * const pathname, const int do_classify)
1008 {
1009         int i, year;
1010         char newd[512], temp[1026];
1011         struct stat buf;
1012         struct tm *t;
1013         time_t now;
1014         char username[17], groupname[17];
1015
1016         time(&now);
1017         year = localtime(&now)->tm_year;
1018         {
1019 #if !WANT_NONROOT
1020                 struct passwd *p;
1021                 struct group *g;
1022 #endif
1023
1024                 if (lstat(pathname, &buf) == -1) return 0;
1025
1026 #if WANT_NONROOT
1027                 strcpy(username, nr_get_uname(buf.st_uid));
1028                 strcpy(groupname, nr_get_gname(buf.st_gid));
1029 #else
1030                 p = getpwuid(buf.st_uid);
1031                 if (p != NULL) {
1032                         strncpy(username, p->pw_name, 16);
1033                         username[16] = 0;
1034                 } else {
1035                         snprintf(username, 16, "%u", buf.st_uid);
1036                 }
1037
1038                 g = getgrgid(buf.st_gid);
1039                 if (g != NULL) {
1040                         strncpy(groupname, g->gr_name, 16);
1041                         groupname[16] = 0;
1042                 } else {
1043                         snprintf(groupname, 16, "%u", buf.st_gid);
1044                 }
1045 #endif
1046         }
1047
1048         /*
1049          * This POSIX approximation is based on GNU ls code (and obfuscated
1050          * a bit...), to be compatible with `real' ls implementations.
1051          */
1052         t = localtime(&(buf.st_mtime));
1053         strftime(newd, 512, ((now > buf.st_mtime + 6L * 30L * 24L * 60L * 60L) ||
1054                              (now < buf.st_mtime - 60L * 60L))
1055                         ? "%b %e  %Y" : "%b %e %H:%M", t);
1056
1057         {
1058 #if WANT_NONROOT
1059                 char rights[16];
1060
1061                 if (nr_check_permission(0, pathname, 0, (S_ISDIR(buf.st_mode)), rights) == -1) {
1062                         /* no permission to even see this file */
1063                         return 0;
1064                 }
1065
1066                 snprintf(temp, 1024, "%c%s %3u %-8s %-8s %8lu %12s %s\r\n",
1067 #else
1068                 snprintf(temp, 1024, "%c%c%c%c%c%c%c%c%c%c %3u %-8s %-8s %8lu %12s %s",
1069 #endif
1070                         decode_mode(buf.st_mode),
1071 #if WANT_NONROOT
1072                         rights,
1073 #else
1074                         (buf.st_mode & S_IRUSR) ? 'r' : '-', 
1075                         (buf.st_mode & S_IWUSR) ? 'w' : '-',
1076                         (buf.st_mode & S_IXUSR) ? ((buf.st_mode & S_ISUID) ? 's' : 'x') : '-',
1077                         (buf.st_mode & S_IRGRP) ? 'r' : '-',
1078                         (buf.st_mode & S_IWGRP) ? 'w' : '-',
1079                         (buf.st_mode & S_IXGRP) ? ((buf.st_mode & S_ISGID) ? 's' : 'x') : '-',
1080                         (buf.st_mode & S_IROTH) ? 'r' : '-',
1081                         (buf.st_mode & S_IWOTH) ? 'w' : '-',
1082                         (buf.st_mode & S_IXOTH) ? ((buf.st_mode & S_ISVTX) ? 't' : 'x') : '-',
1083 #endif
1084                         buf.st_nlink, username, groupname,
1085                         (unsigned long)(buf.st_size), newd, pathname);
1086                 i = strlen(temp);
1087         
1088 #if 0
1089                 /*
1090                  * vim needs this extra character for some reason... It's too 
1091                  * bad I'll have to do it this way, but syntax colouring
1092                  * that works properly is almost a `must' for me :-)
1093                  */
1094                 )
1095 #endif
1096  
1097                 /* add an extra classification `sign' if we got -F */
1098                 if (do_classify) {
1099                         int len = strlen(temp);
1100                         temp[len] = classify(buf.st_mode);
1101                         temp[len + 1] = '\0';
1102                 }
1103         }
1104
1105         strcpy(retbuf, temp);
1106         return 1;
1107 }
1108
1109 /*
1110  * cmd_list():  Handles the LIST command (directory listing). Does a
1111  *              long listing (of type `ls -l'). The listing work is
1112  *              done by do_listing(), below.
1113  */
1114 int cmd_list(struct conn * const c)
1115 {
1116         struct list_options lo;
1117
1118         lo.recursive = 0;
1119         lo.long_listing = 1;
1120         lo.classify = 0;
1121
1122         do_listing(c, &lo);
1123         return 1;
1124 }
1125
1126 /*
1127  * cmd_nlst():  Handles the NLST command (plain directory listing).
1128  *              Does a plain listing (no dates etc.), unless overridden
1129  *              by the `-l' or `-L' flag (case insensitivity because most
1130  *              FTP clients don't have a clue about what they send out). 
1131  *              The listing work is done by do_listing(), below.
1132  */     
1133 int cmd_nlst(struct conn * const c)
1134 {
1135         struct list_options lo;
1136
1137         lo.recursive = 0;
1138         lo.long_listing = 0;
1139         lo.classify = 0;
1140
1141         do_listing(c, &lo);
1142         return 1;
1143 }
1144
1145 /*
1146  * do_listing():
1147  *              Prepares any listing buffers, temp files, etc., before
1148  *              pushing the work one step further :-)
1149  *
1150  *              If the directory listing cache is enabled, the cache
1151  *              is checked first, to see if we still have a valid entry.
1152  */
1153 void do_listing(struct conn * const c, struct list_options * const lo)
1154 {
1155         int i;
1156         char *ptr;
1157 #if HAVE_MMAP
1158         int size;
1159 #endif
1160         struct ftran * const f = c->transfer;
1161
1162 #if WANT_DCACHE
1163         char cwd[256];
1164 #endif
1165
1166 #if WANT_NONROOT
1167 #warning No nonroot checking for list_core() yet
1168 #endif
1169
1170         i = prepare_for_listing(c, &ptr, lo);
1171         if (i == -1) {
1172                 destroy_ftran(c->transfer);
1173                 return;
1174         }
1175
1176 #if WANT_DCACHE
1177         getcwd(cwd, 256);
1178 #endif
1179
1180 #if HAVE_MMAP
1181         strcpy(f->filename, "(directory listing)");
1182 #endif
1183
1184 #if WANT_DCACHE
1185         {
1186                 struct dcache *d = NULL, *next = first_dcache->next_dcache;
1187                 struct stat buf;
1188
1189                 if (stat(cwd, &buf) > -1) {
1190                         /* run through the linked list */
1191                         while (next != NULL) {
1192                                 d = next;
1193                                 next = d->next_dcache;
1194         
1195                                 if (buf.st_mtime <= d->generated &&
1196                                     strcmp(d->dir_name, cwd) == 0 &&
1197                                     strcmp(d->pattern, ptr) == 0 &&
1198                                     memcmp(&(d->lo), lo,
1199                                            sizeof(struct list_options)) == 0) {
1200                                         d->use_count++;
1201                                         f->dir_cache = d;
1202                                         f->file_data = d->dir_data;
1203                                         f->size = d->dir_size;
1204                                         f->dir_listing = 1;
1205                                         f->pos = 0;
1206                                         prepare_for_transfer(f);
1207                                         return;
1208                                 }
1209                         }
1210                 }
1211         }
1212 #endif
1213
1214 #if HAVE_MMAP
1215         {
1216                 int num_files = get_num_files(c, ptr, lo);
1217                 if (num_files == -1) return;
1218
1219                 size = num_files * 160;
1220                 f->file_data = malloc(size + 1);
1221                 TRAP_ERROR(f->file_data == NULL, 550, return);
1222                 list_core(c, ptr, "", lo, size, 0);
1223         }
1224 #else
1225         list_core(c, ptr, "", lo);
1226 #endif
1227
1228 #if WANT_DCACHE
1229         /* populate the directory listing cache */
1230         {
1231                 struct stat buf;
1232                 struct dcache *d = alloc_new_dcache();
1233                 if (d != NULL && stat(cwd, &buf) > -1) {
1234                         d->use_count++;
1235                         f->dir_cache = d;
1236                         d->dir_data = f->file_data;
1237                         d->dir_size = f->size;
1238                         d->generated = buf.st_mtime;
1239
1240                         strcpy(d->dir_name, cwd);
1241                         strncpy(d->pattern, ptr, 255);
1242                         d->pattern[255] = 0;
1243                         d->lo = *lo;
1244                 }
1245         }
1246 #endif
1247
1248 #if HAVE_MMAP
1249         f->pos = 0;
1250 #endif
1251         prepare_for_transfer(f);
1252 }
1253
1254 /*
1255  * get_num_files():
1256  *              Get the number of files in PATHNAME (optionally matching
1257  *              a pattern). Note that c is needed for TRAP_ERROR.
1258  */
1259 int get_num_files(struct conn * const c, const char * const pathname,
1260                    struct list_options * const lo)
1261 {
1262         int num_files;
1263         glob_t pglob;
1264
1265         /*
1266          * glob() fails to set errno correctly, so we simply guess on
1267          * `permission denied'... The others are far less likely to happen.
1268          */
1269         switch (glob(pathname, 0, NULL, &pglob)) {
1270 #ifdef GLOB_NOMATCH
1271         case GLOB_NOMATCH:
1272                 return 0;
1273 #endif
1274         case 0:
1275                 num_files = pglob.gl_pathc;
1276                 break;
1277         default:
1278                 numeric(c, 550, strerror(EACCES));
1279                 return -1;
1280         }
1281
1282         if (lo->recursive) {
1283                 int i;
1284                 for (i = 0; i < pglob.gl_pathc; i++) {
1285                         char *temp = pglob.gl_pathv[i];
1286                         struct stat buf;
1287
1288                         lstat(temp, &buf);
1289                         if (S_ISDIR(buf.st_mode)) {
1290                                 chdir(temp);
1291                                 num_files += get_num_files(c, "*", lo);
1292                                 chdir("..");
1293                         }
1294                 }
1295         }
1296
1297         return num_files;
1298 }
1299
1300 /*
1301  * list_core(): Enumerate all the files in PATHNAME, and formats them
1302  *              according to list_options (calling format functions if
1303  *              required).
1304  *
1305  *              Note that we don't do any realloc() yet, so if your
1306  *              _average_ file name length is over a certain size (a little
1307  *              under 80 for long listings, and a little under 160 for
1308  *              short listings), the list will be truncated. Fix...
1309  *
1310  *              The return value only makes sense if mmap()'ing, since it
1311  *              returns the number of bytes written into the buffer.
1312  *
1313  *              This function is rather long.
1314  */
1315 int list_core(struct conn * const c, const char * const pathname,
1316               char * const disp_pathname, struct list_options * const lo
1317 #if HAVE_MMAP
1318                 , const int size, int pos
1319 #endif
1320                 )
1321 {
1322         int i;
1323         glob_t pglob;
1324         struct ftran * const f = c->transfer;
1325
1326         /*
1327          * glob() fails to set errno correctly, so we simply guess on
1328          * `permission denied'... The others are far less likely to happen.
1329          */
1330         switch (glob(pathname, GLOB_MARK, NULL, &pglob)) {
1331         case 0:
1332 #ifdef GLOB_NOMATCH
1333         case GLOB_NOMATCH:
1334 #endif
1335                 break;          /* note: break, not return */
1336         default:
1337                 numeric(c, 550, strerror(EACCES));
1338 #if HAVE_MMAP
1339                 return pos;
1340 #else
1341                 return 0;
1342 #endif
1343         }
1344
1345         if (lo->recursive) {
1346                 if (disp_pathname[0] == '\0') {
1347                         mwrite(".:\r\n", 4);
1348                 } else {
1349                         char temp[1024];
1350                         int i;
1351
1352                         snprintf(temp, 1024, "%s:\r\n", disp_pathname);
1353                         i = strlen(temp);
1354                         mwrite(temp, i);
1355                 }
1356         }
1357         if (lo->long_listing) {
1358                 /* FIX: we may get too high total number if we are running nonroot! */
1359                 struct stat buf;
1360                 long unsigned int total = 0;
1361                 char temp[1024];
1362
1363                 for (i = 0; i < pglob.gl_pathc; i++) {
1364                         if (lstat(pglob.gl_pathv[i], &buf) != -1) {
1365                                 total += buf.st_blocks;
1366                         }
1367                 }
1368                 snprintf(temp, 1024, "total %lu\r\n", total >> 1);
1369                 i = strlen(temp);
1370                 mwrite(temp, i);
1371         }
1372
1373         for (i = 0; i < pglob.gl_pathc; i++) {
1374                 char * const temp = pglob.gl_pathv[i];
1375                 char buf[2048];
1376
1377                 /* strip `/' away from the pathname -- add it later if -F */
1378                 {
1379                         int len = strlen(temp);
1380                         if (temp[len - 1] == '/') {
1381                                 temp[len - 1] = '\0';
1382                         }
1383                 }
1384
1385                 if (lo->long_listing) {
1386                         if (long_listing(buf, temp, lo->classify) == 0) continue;
1387                 } else {
1388                         strcpy(buf, temp);
1389                         if (lo->classify) {
1390                                 struct stat statbuf;
1391
1392                                 if (lstat(buf, &statbuf) != -1) {
1393                                         const int len = strlen(buf);
1394
1395                                         buf[len] = classify(statbuf.st_mode);
1396                                         buf[len + 1] = 0;
1397                                 }
1398                         }
1399                 }
1400
1401                 mwrite(buf, strlen(buf));
1402                 mwrite("\r\n", 2);
1403         }
1404
1405         /*
1406          * If recursion is on, dive into any subdirectories now -- note
1407          * that each entry is stat()'ed twice, hopefully the OS will manage,
1408          * and we've got our own dcache anyways -- this could be fixed at
1409          * the expense of some memory, consider for later inclusion.
1410          */
1411         if (lo->recursive) {
1412                 for (i = 0; i < pglob.gl_pathc; i++) {
1413                         struct stat buf;
1414                         const char * const temp = pglob.gl_pathv[i];
1415
1416                         /* don't dive into `.' or `..' */
1417                         if (lstat(temp, &buf) != -1 && S_ISDIR(buf.st_mode) &&
1418                                 (temp[0] != '.' || (temp[1] != '.' && temp[1] != '\0'))) {
1419                                 char tmp2[1024];
1420
1421                                 mwrite("\r\n", 2);
1422
1423                                 /* attach the pathname to the end of the displayed path */
1424                                 if (disp_pathname[0] == '\0') {
1425                                         snprintf(tmp2, 1024, "%s", temp);
1426                                 } else {
1427                                         snprintf(tmp2, 1024, "%s/%s", disp_pathname, temp);
1428                                 }
1429
1430                                 chdir(temp);
1431                                 pos = list_core(c, "*", tmp2, lo, 
1432 #if HAVE_MMAP
1433                                         size, pos);
1434 #endif
1435                                 chdir("..");
1436                         }
1437                 }
1438         }
1439
1440 #if HAVE_MMAP
1441         f->size = pos;
1442 #else
1443         lseek(f->local_file, 0, SEEK_SET);
1444 #endif
1445
1446         globfree(&pglob);
1447 #if HAVE_MMAP
1448         return pos;
1449 #else
1450         return 0;
1451 #endif
1452 }
1453
1454 /*
1455  * cmd_noop():  Handles the NOOP command. Does nothing, doesn't even
1456  *              reset the timeout.
1457  */
1458 int cmd_noop(struct conn * const c)
1459 {
1460         numeric(c, 200, "NOOP command successful.");
1461         return 1;
1462 }
1463
1464 /*
1465  * cmd_syst():  Handles the SYST command. Returns the system identification.
1466  */
1467 int cmd_syst(struct conn * const c)
1468 {
1469         numeric(c, 215, "UNIX Type: L%u", NBBY);
1470         return 1;
1471 }
1472
1473 /*
1474  * cmd_type():  Handles the TYPE command.
1475  */
1476 int cmd_type(struct conn * const c)
1477 {
1478 #if WANT_ASCII
1479         c->recv_buf[0] &= (255-32);     /* convert to upper case */
1480         if (c->recv_buf[0] == 'A') {
1481                 c->ascii_mode = 1;
1482                 numeric(c, 200, "Type is ASCII.");
1483         } else if (c->recv_buf[0] == 'I') {
1484                 c->ascii_mode = 0;
1485                 numeric(c, 200, "Type is IMAGE.");
1486         } else {
1487                 numeric(c, 504, "Unknown type.");
1488         }
1489 #else
1490         numeric(c, 200, "TYPE ignored (always I)");
1491 #endif
1492         return 1;
1493 }
1494
1495 /*
1496  * cmd_mode():  Handles the MODE command. Only stream mode is supported.
1497  */
1498 int cmd_mode(struct conn * const c)
1499 {
1500         c->recv_buf[0] &= (255-32);     /* convert to upper case */
1501         if (c->recv_buf[0] == 'S') {
1502                 numeric(c, 200, "Mode is STREAM.");
1503         } else {
1504                 numeric(c, 504, "Unknown mode.");
1505         }
1506         return 1;
1507 }
1508
1509 /*
1510  * cmd_stru():  Handles the STRU command. Only file mode is supported.
1511  */
1512 int cmd_stru(struct conn * const c)
1513 {
1514         c->recv_buf[0] &= (255-32);     /* convert to upper case */
1515         if (c->recv_buf[0] == 'F') {
1516                 numeric(c, 200, "Structure is FILE.");
1517         } else {
1518                 numeric(c, 504, "Unknown structure.");
1519         }
1520         return 1;
1521 }
1522
1523 /*
1524  * cmd_help():  Handle the HELP command. I'm sorry, but I'm unwilling
1525  *              to use a lot of space to explain the RFCs in such a message,
1526  *              and BetaFTPD doesn't have any special things that should
1527  *              be noted anywhere. Thus, this message is close to empty. I
1528  *              feel that a 5xx entry would have been better, but that is
1529  *              disallowed.
1530  *
1531  *              As with ACCT, this command is supposed to be executed from
1532  *              everywhere, so we have to run without setuid. I don't like
1533  *              it, but at the same time I have to idea what could go
1534  *              wrong...
1535  *
1536  *              Perhaps I should make this message sound a little less
1537  *              like an error, since the error code is intended for helpful
1538  *              messages? :-)
1539  */
1540 int cmd_help(struct conn * const c)
1541 {
1542         numeric(c, 414, "Sorry, no detailed help; use standard FTP commands.");
1543         return 1;
1544 }
1545
1546 /*
1547  * cmd_quit():  Handles the QUIT command, which shuts down the control
1548  *              and data sockets.
1549  */
1550 int cmd_quit(struct conn * const c)
1551 {
1552         numeric(c, 221, "Have a nice day!");
1553         destroy_conn(c);
1554         return 0;
1555 }
1556
1557 /*
1558  * cmd_rein():  Handle the REIN command, which does close to a full reset
1559  *              of the connection. Much of the code here is intentionally
1560  *              copied directly from alloc_new_conn() -- perhaps we should
1561  *              modularize this?
1562  */
1563 int cmd_rein(struct conn * const c)
1564 {
1565         destroy_ftran(c->transfer);
1566         c->buf_len = c->auth = c->rest_pos = 0;
1567
1568         /* equals: strcpy(c->curr_dir, "/") ; strcpy(c->last_cmd, ""); */
1569         c->curr_dir[0] = '/';
1570 #if WANT_FULLSCREEN
1571         c->curr_dir[1] = c->last_cmd[0] = '\0';
1572 #else
1573         c->curr_dir[1] = '\0';
1574 #endif
1575
1576         time(&(c->last_transfer));
1577         numeric(c, 220, "BetaFTPD " VERSION " ready.");
1578
1579         return 1;
1580 }
1581
1582 #if DOING_PROFILING
1583 /*
1584  * cmd_exit():  Handles the EXIT command, my own `extension' to the
1585  *              FTP protocol... IMPORTANT: Only to be used for profiling
1586  *              purposes!! (It's needed to get some profiling data out
1587  *              of the server after compiling it with -pg, since such data
1588  *              is only written on a clear exit()). Any user (even those
1589  *              not logged in) can issue an EXIT, and make the server shut
1590  *              down without clearing any sockets etc. In other words:
1591  *              Don't use it on a production site.
1592  */
1593 void cmd_exit(struct conn * const c)
1594 {
1595         while (first_conn->next_conn)
1596                 destroy_conn(first_conn->next_conn);
1597         exit(0);
1598 }
1599 #endif
1600
1601 /*
1602  * parse_command():
1603  *              Gets a command from c->recv_buf, determines which command
1604  *              it is, sets proper effective user-ID and calls the command
1605  *              handler. Finally, it cleans up.
1606  *
1607  *              To me, this command seems optimizable, but I'm not really
1608  *              sure where :-)
1609  */
1610 void parse_command(struct conn *c)
1611 {
1612         int cmlen;
1613         const struct handler *h = handler_table;        /* first entry */
1614
1615         if (c == NULL) return;
1616
1617         /* strip any leading non-ASCII characters (including CR/LFs) */
1618         while (c->buf_len > 0 && (c->recv_buf[0] < 'a' || c->recv_buf[0] > 'z')
1619                               && (c->recv_buf[0] < 'A' || c->recv_buf[0] > 'Z')) {
1620                 remove_bytes(c, 1);             /* not good */
1621         }
1622
1623         /* scan, searching for CR or LF */      
1624         cmlen = strcspn(c->recv_buf, "\r\n");
1625         if (cmlen >= c->buf_len) return;
1626
1627 #if WANT_FULLSCREEN
1628         strncpy(c->last_cmd, c->recv_buf, cmlen);
1629         c->last_cmd[cmlen] = 0;
1630 #endif
1631
1632         do {
1633                 if ((cmlen >= (strlen(h->cmd_name) + h->add_cmlen)) &&
1634                     (strncasecmp(c->recv_buf, h->cmd_name, strlen(h->cmd_name)) == 0)) {
1635                         if (c->auth < h->min_auth) {
1636                                 numeric(c, 503, "Please login with USER and PASS.");
1637                                 while (c->recv_buf[0] != '\n') remove_bytes(c, 1);
1638                         } else {
1639                                 char schar;
1640
1641 #if !WANT_NONROOT
1642                                 if (h->do_setuid) {
1643                                         seteuid(c->uid);
1644                                 } else {
1645                                         seteuid(0);
1646                                 }
1647 #endif
1648
1649                                 remove_bytes(c, strlen(h->cmd_name));
1650                                 cmlen -= strlen(h->cmd_name);
1651                                 while (c->recv_buf[0] == ' ') {
1652                                         remove_bytes(c, 1);
1653                                         cmlen--;
1654                                 }
1655
1656                                 schar = c->recv_buf[cmlen];
1657                                 c->recv_buf[cmlen] = 0;
1658
1659                                 /* result of zero means the connection is freed */
1660                                 if (h->callback(c)) {
1661                                         c->recv_buf[cmlen] = schar;
1662 #if !WANT_NONROOT
1663                                         if (h->do_setuid) seteuid(getuid());
1664 #endif
1665                                         remove_bytes(c, cmlen);
1666                                 }
1667                         }
1668                         return;
1669                 }
1670         } while ((++h)->callback != NULL);
1671
1672         numeric(c, 500, "Sorry, no such command.");
1673         remove_bytes(c, cmlen); 
1674 }
1675
1676 /*
1677  * prepare_for_transfer():
1678  *              Prepares an ftran object for a file transfer, setting
1679  *              file size, opening sockets etc.
1680  *
1681  *              nonroot notice: prepare_for_transfer() assumes all access
1682  *              checks are already done.
1683  */
1684 void prepare_for_transfer(struct ftran *f)
1685 {
1686 #if WANT_NONROOT
1687 #warning No nonroot checking for prepare_for_transfer() yet
1688 #endif
1689
1690 #if HAVE_MMAP
1691         /* mmap doesn't make temp files for dir listings */
1692         if (!f->dir_listing) {
1693 #endif
1694
1695                 f->size = lseek(f->local_file, 0, SEEK_END);
1696                 errno = 0;
1697 #if WANT_UPLOAD
1698                 if (f->upload == 0 || f->append == 0 || f->owner->rest_pos != 0)
1699 #endif 
1700                         lseek(f->local_file, f->owner->rest_pos, SEEK_SET);
1701 #if HAVE_MMAP
1702         }
1703 #endif
1704         
1705         if (f->state == 1) {            /* PASV connection */
1706                 f->state = 2;           /* waiting */
1707         } else if (f->state == 3) {     /* PORT connection */
1708                 f->state = 4;
1709                 connect(f->sock, (struct sockaddr *)&f->sin, sizeof(f->sin));
1710                 add_fd(f->sock, POLLOUT);
1711         }
1712         time(&(f->tran_start));
1713 }
1714
1715 /*
1716  * decode_mode():
1717  *              Takes a mode_t argument (from a `struct stat'), and
1718  *              returns the proper dirlist letter for that type.
1719  *
1720  *              Note: S_IFLNK seems to be broken, or perhaps I just have
1721  *              missed something (S_IFLNK is always set for all *files* on
1722  *              my glibc 2.0.111 system).
1723  *
1724  *              The most common cases are put first, for speed :-)
1725  */
1726 char decode_mode(mode_t mode) {
1727         if (S_ISREG(mode))  return '-';
1728         if (S_ISDIR(mode))  return 'd';
1729         if (S_ISLNK(mode))  return 'l';
1730         if (S_ISBLK(mode))  return 'b';
1731         if (S_ISCHR(mode))  return 'c';
1732         if (S_ISSOCK(mode)) return 's';
1733         if (S_ISFIFO(mode))  return 'f';
1734
1735         return '-';
1736 }
1737
1738 /*
1739  * translate_path():
1740  *              Take an FTP path, do all neccessary root_dir checks,
1741  *              change to the correct directory and return the proper
1742  *              file name to open/stat/whatever. The path returned is
1743  *              relative to the current directory (NOT absolute). chdir()
1744  *              in any way will `destroy' this argument.
1745  *
1746  *              Note that `path' will be _changed_, and used as a return pointer
1747  *              base. Do not attempt to free the result from this function --
1748  *              if you need to, free path instead.
1749  */
1750 char *translate_path(struct conn * const c, char * const path)
1751 {
1752         char *ptr = NULL;
1753
1754         /* chdir to the right dir, then chop it off */
1755         chdir(c->curr_dir);
1756
1757         ptr = strrchr(path, '/');
1758         if (ptr != NULL) {
1759                 char save_char = ptr[0];
1760                 ptr[0] = 0;
1761
1762                 if (do_chdir(c, path) == -1) {
1763                         return NULL;
1764                 }
1765                 ptr[0] = save_char;
1766                 ptr++;
1767         } else {
1768                 ptr = path;
1769         }
1770         return ptr;
1771 }
1772
1773 /*
1774  * do_openfile():
1775  *              Opens the file PATH with access parameters FLAGS, translating
1776  *              paths and checking permissions as neccessary. Generally, this
1777  *              should be used whenever you need an open().
1778  *
1779  *              The parameters might be a bit confusing. To clarify them a bit:
1780  *              c:              IN/OUT (will be changed)
1781  *              path:           IN (but _will_ be changed)
1782  *              filename:       OUT
1783  *              flags:          IN
1784  *              check_perm:     IN
1785  */
1786 int do_openfile(struct conn * const c, char * const path,
1787                 char * const filename, const int flags
1788 #if WANT_NONROOT
1789                 , const int check_permission
1790 #endif
1791 )
1792 {
1793         char *ptr;
1794         struct stat buf;
1795
1796 #if WANT_NONROOT
1797         if (nr_check_permission(c->uid, path, check_permission, 0, NULL) == -1) {
1798                 return -1;
1799         }
1800 #endif
1801
1802         ptr = translate_path(c, c->recv_buf);
1803         if (ptr == NULL) return -1;
1804
1805 #if WANT_UPLOAD
1806         if ((flags & O_CREAT) == 0) {
1807 #endif
1808                 TRAP_ERROR(stat(ptr, &buf) == -1, 550, return -2);
1809                 if (!S_ISREG(buf.st_mode)) {
1810                         numeric(c, 550, "Not a plain file.", ptr);
1811                         return -2;
1812                 }
1813 #if WANT_UPLOAD
1814         }
1815 #endif
1816
1817         if (filename != NULL) { /* filename should always be != NULL */
1818                 strcpy(filename, ptr);
1819         }
1820         return open(ptr, flags, 0666);
1821 }
1822
1823 /*
1824  * prepare_for_listing():
1825  *              Parse list options, put them back into the list_options
1826  *              structure lo, and make temporary room for the list.
1827  */
1828 int prepare_for_listing(struct conn * const c, char ** const ptr,
1829                         struct list_options * const lo)
1830 {
1831 #if !HAVE_MMAP
1832         char *tfname;
1833 #endif
1834         struct ftran *f = c->transfer;
1835         char *tmp;
1836         char *optr = NULL, *fptr = NULL; 
1837 #if WANT_NONROOT
1838         char chd[512];
1839 #endif
1840
1841 #if WANT_NONROOT
1842 #warning No nonroot checking for prepare_for_listing() yet
1843 #endif
1844
1845         if ((f == NULL) || ((f->state != 1) && (f->state != 3))) {
1846                 numeric(c, 425, "No data connection set up; please use PASV or PORT.");
1847                 return -1;
1848         }
1849
1850         /*
1851          * A little parameter scanning is required here. There can only
1852          * be two parts: the directory name, and any options. We'll find
1853          * any options first.
1854          */
1855         if (c->recv_buf[0] == '-') {
1856                 optr = c->recv_buf;
1857         } else {
1858                 optr = strstr(c->recv_buf, " -");
1859         }
1860
1861         /* Then see if there are any options to parse. */
1862         if (optr != NULL) {
1863                 while (*++optr) {
1864                         switch (*optr & (255-32)) {     /* uppercase */
1865                         case 'R':       /* actually case sensitive... */
1866                                 lo->recursive = 1;
1867                                 break;
1868                         case 'L':
1869                                 lo->long_listing = 1;
1870                                 break;
1871                         case 'F':
1872                                 lo->classify = 1;
1873                                 break;
1874                         case ' ':
1875                                 fptr = optr + 1;
1876                                 *(optr--) = 0;
1877                                 break;
1878                         default:
1879                                 break;
1880                         }
1881                 }
1882         } else {
1883                 fptr = c->recv_buf;
1884         }
1885         
1886         /* then we chdir to the dir in fptr (if any) */
1887         tmp = fptr ? strrchr(fptr, '/') : NULL;
1888         if (tmp != NULL) {
1889                 tmp[0] = 0;
1890                 if (do_chdir(c, fptr) == -1) return -1;
1891                 fptr = tmp + 1;
1892         } else {
1893                 /* current directory */
1894                 TRAP_ERROR(chdir(c->curr_dir) == -1, 550, return -1);
1895         }
1896
1897         /* if no argument, choose all files */
1898         if (fptr == NULL || fptr[0] == 0) fptr = "*";
1899         *ptr = fptr;
1900
1901 #if WANT_NONROOT
1902         getcwd(chd, 512);
1903         if (nr_check_permission(c->uid, chd, 4, 1, NULL) == -1) {
1904                 numeric(c, 550, "Permission denied");
1905                 return -1;
1906         }
1907 #endif
1908
1909 #if !HAVE_MMAP
1910         tfname = tempnam(NULL, "ftp");
1911
1912 #if WANT_NONROOT
1913         if (tfname == NULL) tfname = tempnam("/", "ftp");
1914 #endif
1915
1916         TRAP_ERROR(tfname == NULL, 550, return -1);
1917         strcpy(f->filename, tfname);
1918         free(tfname);
1919
1920         f->local_file = open(f->filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
1921         TRAP_ERROR(f->local_file == -1, 550, return -1);
1922 #endif
1923         f->dir_listing = 1;
1924 #if WANT_UPLOAD
1925         f->upload = 0;
1926 #endif
1927
1928         return 0;
1929 }
1930
1931 /*
1932  * classify():  Takes a mode_t argument (from `struct stat'), and returns
1933  *              the parameter to be used in an `ls -F'-style listing.
1934  */
1935 char classify(const mode_t mode)
1936 {
1937         if (S_ISREG(mode)) {
1938                 if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
1939                         return '*';
1940                 } else {
1941                         return '\0';
1942                 }
1943         }
1944         if (S_ISDIR(mode)) return '/';
1945         if (S_ISLNK(mode)) return '@';
1946         if (S_ISSOCK(mode)) return '=';
1947         if (S_ISFIFO(mode)) return '|';
1948         return '\0'; 
1949 }