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