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