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