]> git.sesse.net Git - betaftpd/blob - cmds.c
cmd_type(), cmd_stru(): Unknown arguments are now refused instead of being ignored...
[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. Only stream mode is supported.
1446  */
1447 int cmd_mode(struct conn * const c)
1448 {
1449         c->recv_buf[0] &= (255-32);     /* convert to upper case */
1450         if (c->recv_buf[0] == 'S') {
1451                 numeric(c, 200, "Mode is STREAM.");
1452         } else {
1453                 numeric(c, 504, "Unknown mode.");
1454         }
1455         return 1;
1456 }
1457
1458 /*
1459  * cmd_stru():  Handles the STRU command. Only file mode is supported.
1460  */
1461 int cmd_stru(struct conn * const c)
1462 {
1463         c->recv_buf[0] &= (255-32);     /* convert to upper case */
1464         if (c->recv_buf[0] == 'F') {
1465                 numeric(c, 200, "Structure is FILE.");
1466         } else {
1467                 numeric(c, 504, "Unknown structure.");
1468         }
1469         return 1;
1470 }
1471
1472 /*
1473  * cmd_help():  Handle the HELP command. I'm sorry, but I'm unwilling
1474  *              to use a lot of space to explain the RFCs in such a message,
1475  *              and BetaFTPD doesn't have any special things that should
1476  *              be noted anywhere. Thus, this message is close to empty. I
1477  *              feel that a 5xx entry would have been better, but that is
1478  *              disallowed.
1479  *
1480  *              As with ACCT, this command is supposed to be executed from
1481  *              everywhere, so we have to run without setuid. I don't like
1482  *              it, but at the same time I have to idea what could go
1483  *              wrong...
1484  *
1485  *              Perhaps I should make this message sound a little less
1486  *              like an error, since the error code is intended for helpful
1487  *              messages? :-)
1488  */
1489 int cmd_help(struct conn * const c)
1490 {
1491         numeric(c, 414, "Sorry, no detailed help; use standard FTP commands.");
1492         return 1;
1493 }
1494
1495 /*
1496  * cmd_quit():  Handles the QUIT command, which shuts down the control
1497  *              and data sockets.
1498  */
1499 int cmd_quit(struct conn * const c)
1500 {
1501         numeric(c, 221, "Have a nice day!");
1502         destroy_conn(c);
1503         return 0;
1504 }
1505
1506 /*
1507  * cmd_rein():  Handle the REIN command, which does close to a full reset
1508  *              of the connection. Much of the code here is intentionally
1509  *              copied directly from alloc_new_conn() -- perhaps we should
1510  *              modularize this?
1511  */
1512 int cmd_rein(struct conn * const c)
1513 {
1514         destroy_ftran(c->transfer);
1515         c->buf_len = c->auth = c->rest_pos = 0;
1516
1517         /* equals: strcpy(c->curr_dir, "/") ; strcpy(c->last_cmd, ""); */
1518         c->curr_dir[0] = '/';
1519 #if WANT_FULLSCREEN
1520         c->curr_dir[1] = c->last_cmd[0] = '\0';
1521 #else
1522         c->curr_dir[1] = '\0';
1523 #endif
1524
1525         time(&(c->last_transfer));
1526         numeric(c, 220, "BetaFTPD " VERSION " ready.");
1527
1528         return 1;
1529 }
1530
1531 #if DOING_PROFILING
1532 /*
1533  * cmd_exit():  Handles the EXIT command, my own `extension' to the
1534  *              FTP protocol... IMPORTANT: Only to be used for profiling
1535  *              purposes!! (It's needed to get some profiling data out
1536  *              of the server after compiling it with -pg, since such data
1537  *              is only written on a clear exit()). Any user (even those
1538  *              not logged in) can issue an EXIT, and make the server shut
1539  *              down without clearing any sockets etc. In other words:
1540  *              Don't use it on a production site.
1541  */
1542 void cmd_exit(struct conn * const c)
1543 {
1544         while (first_conn->next_conn)
1545                 destroy_conn(first_conn->next_conn);
1546         exit(0);
1547 }
1548 #endif
1549
1550 /*
1551  * parse_command():
1552  *              Gets a command from c->recv_buf, determines which command
1553  *              it is, sets proper effective user-ID and calls the command
1554  *              handler. Finally, it cleans up.
1555  *
1556  *              To me, this command seems optimizable, but I'm not really
1557  *              sure where :-)
1558  */
1559 void parse_command(struct conn *c)
1560 {
1561         int cmlen;
1562         const struct handler *h = handler_table;        /* first entry */
1563
1564         if (c == NULL) return;
1565
1566         /* strip any leading non-ASCII characters (including CR/LFs) */
1567         while (c->buf_len > 0 && (c->recv_buf[0] < 'a' || c->recv_buf[0] > 'z')
1568                               && (c->recv_buf[0] < 'A' || c->recv_buf[0] > 'Z')) {
1569                 remove_bytes(c, 1);             /* not good */
1570         }
1571
1572         /* scan, searching for CR or LF */      
1573         cmlen = strcspn(c->recv_buf, "\r\n");
1574         if (cmlen >= c->buf_len) return;
1575
1576 #if WANT_FULLSCREEN
1577         strncpy(c->last_cmd, c->recv_buf, cmlen);
1578         c->last_cmd[cmlen] = 0;
1579 #endif
1580
1581         do {
1582                 if ((cmlen >= (strlen(h->cmd_name) + h->add_cmlen)) &&
1583                     (strncasecmp(c->recv_buf, h->cmd_name, strlen(h->cmd_name)) == 0)) {
1584                         if (c->auth < h->min_auth) {
1585                                 numeric(c, 503, "Please login with USER and PASS.");
1586                                 while (c->recv_buf[0] != '\n') remove_bytes(c, 1);
1587                         } else {
1588                                 char schar;
1589
1590 #if !WANT_NONROOT
1591                                 if (h->do_setuid) {
1592                                         seteuid(c->uid);
1593                                 } else {
1594                                         seteuid(0);
1595                                 }
1596 #endif
1597
1598                                 remove_bytes(c, strlen(h->cmd_name));
1599                                 cmlen -= strlen(h->cmd_name);
1600                                 while (c->recv_buf[0] == ' ') {
1601                                         remove_bytes(c, 1);
1602                                         cmlen--;
1603                                 }
1604
1605                                 schar = c->recv_buf[cmlen];
1606                                 c->recv_buf[cmlen] = 0;
1607
1608                                 /* result of zero means the connection is freed */
1609                                 if (h->callback(c)) {
1610                                         c->recv_buf[cmlen] = schar;
1611 #if !WANT_NONROOT
1612                                         if (h->do_setuid) seteuid(getuid());
1613 #endif
1614                                         remove_bytes(c, cmlen);
1615                                 }
1616                         }
1617                         return;
1618                 }
1619         } while ((++h)->callback != NULL);
1620
1621         numeric(c, 500, "Sorry, no such command.");
1622         remove_bytes(c, cmlen); 
1623 }
1624
1625 /*
1626  * prepare_for_transfer():
1627  *              Prepares an ftran object for a file transfer, setting
1628  *              file size, opening sockets etc.
1629  *
1630  *              nonroot notice: prepare_for_transfer() assumes all access
1631  *              checks are already done.
1632  */
1633 void prepare_for_transfer(struct ftran *f)
1634 {
1635 #if WANT_NONROOT
1636 #warning No nonroot checking for prepare_for_transfer() yet
1637 #endif
1638
1639 #if HAVE_MMAP
1640         /* mmap doesn't make temp files for dir listings */
1641         if (!f->dir_listing) {
1642 #endif
1643
1644                 f->size = lseek(f->local_file, 0, SEEK_END);
1645                 errno = 0;
1646 #if WANT_UPLOAD
1647                 if (f->upload == 0 || f->append == 0 || f->owner->rest_pos != 0)
1648 #endif 
1649                         lseek(f->local_file, f->owner->rest_pos, SEEK_SET);
1650 #if HAVE_MMAP
1651         }
1652 #endif
1653         
1654         if (f->state == 1) {            /* PASV connection */
1655                 f->state = 2;           /* waiting */
1656         } else if (f->state == 3) {     /* PORT connection */
1657                 f->state = 4;
1658                 connect(f->sock, (struct sockaddr *)&f->sin, sizeof(f->sin));
1659                 add_fd(f->sock, POLLOUT);
1660         }
1661         time(&(f->tran_start));
1662 }
1663
1664 /*
1665  * decode_mode():
1666  *              Takes a mode_t argument (from a `struct stat'), and
1667  *              returns the proper dirlist letter for that type.
1668  *
1669  *              Note: S_IFLNK seems to be broken, or perhaps I just have
1670  *              missed something (S_IFLNK is always set for all *files* on
1671  *              my glibc 2.0.111 system).
1672  *
1673  *              The most common cases are put first, for speed :-)
1674  */
1675 char decode_mode(mode_t mode) {
1676         if (S_ISREG(mode))  return '-';
1677         if (S_ISDIR(mode))  return 'd';
1678         if (S_ISLNK(mode))  return 'l';
1679         if (S_ISBLK(mode))  return 'b';
1680         if (S_ISCHR(mode))  return 'c';
1681         if (S_ISSOCK(mode)) return 's';
1682         if (S_ISFIFO(mode))  return 'f';
1683
1684         return '-';
1685 }
1686
1687 /*
1688  * translate_path():
1689  *              Take an FTP path, do all neccessary root_dir checks,
1690  *              change to the correct directory and return the proper
1691  *              file name to open/stat/whatever. The path returned is
1692  *              relative to the current directory (NOT absolute). chdir()
1693  *              in any way will `destroy' this argument.
1694  *
1695  *              Note that `path' will be _changed_, and used as a return pointer
1696  *              base. Do not attempt to free the result from this function --
1697  *              if you need to, free path instead.
1698  */
1699 char *translate_path(struct conn * const c, char * const path)
1700 {
1701         char *ptr = NULL;
1702
1703         /* chdir to the right dir, then chop it off */
1704         chdir(c->curr_dir);
1705
1706         ptr = strrchr(path, '/');
1707         if (ptr != NULL) {
1708                 char save_char = ptr[0];
1709                 ptr[0] = 0;
1710
1711                 if (do_chdir(c, path) == -1) {
1712                         return NULL;
1713                 }
1714                 ptr[0] = save_char;
1715                 ptr++;
1716         } else {
1717                 ptr = path;
1718         }
1719         return ptr;
1720 }
1721
1722 /*
1723  * do_openfile():
1724  *              Opens the file PATH with access parameters FLAGS, translating
1725  *              paths and checking permissions as neccessary. Generally, this
1726  *              should be used whenever you need an open().
1727  *
1728  *              The parameters might be a bit confusing. To clarify them a bit:
1729  *              c:              IN/OUT (will be changed)
1730  *              path:           IN (but _will_ be changed)
1731  *              filename:       OUT
1732  *              flags:          IN
1733  *              check_perm:     IN
1734  */
1735 int do_openfile(struct conn * const c, char * const path,
1736                 char * const filename, const int flags
1737 #if WANT_NONROOT
1738                 , const int check_permission
1739 #endif
1740 )
1741 {
1742         char *ptr;
1743         struct stat buf;
1744
1745 #if WANT_NONROOT
1746         if (nr_check_permission(c->uid, path, check_permission, 0, NULL) == -1) {
1747                 return -1;
1748         }
1749 #endif
1750
1751         ptr = translate_path(c, c->recv_buf);
1752         if (ptr == NULL) return -1;
1753
1754 #if WANT_UPLOAD
1755         if ((flags & O_CREAT) == 0) {
1756 #endif
1757                 TRAP_ERROR(stat(ptr, &buf) == -1, 550, return -2);
1758                 if (!S_ISREG(buf.st_mode)) {
1759                         numeric(c, 550, "Not a plain file.", ptr);
1760                         return -2;
1761                 }
1762 #if WANT_UPLOAD
1763         }
1764 #endif
1765
1766         if (filename != NULL) { /* filename should always be != NULL */
1767                 strcpy(filename, ptr);
1768         }
1769         return open(ptr, flags, 0666);
1770 }
1771
1772 /*
1773  * prepare_for_listing():
1774  *              Parse list options, put them back into the list_options
1775  *              structure lo, and make temporary room for the list.
1776  */
1777 int prepare_for_listing(struct conn * const c, char ** const ptr,
1778                         struct list_options * const lo)
1779 {
1780 #if !HAVE_MMAP
1781         char *tfname;
1782 #endif
1783         struct ftran *f = c->transfer;
1784         char *tmp;
1785         char *optr = NULL, *fptr = NULL; 
1786 #if WANT_NONROOT
1787         char chd[512];
1788 #endif
1789
1790 #if WANT_NONROOT
1791 #warning No nonroot checking for prepare_for_listing() yet
1792 #endif
1793
1794         if ((f == NULL) || ((f->state != 1) && (f->state != 3))) {
1795                 numeric(c, 425, "No data connection set up; please use PASV or PORT.");
1796                 return -1;
1797         }
1798
1799         /*
1800          * A little parameter scanning is required here. There can only
1801          * be two parts: the directory name, and any options. We'll find
1802          * any options first.
1803          */
1804         if (c->recv_buf[0] == '-') {
1805                 optr = c->recv_buf;
1806         } else {
1807                 optr = strstr(c->recv_buf, " -");
1808         }
1809
1810         /* Then see if there are any options to parse. */
1811         if (optr != NULL) {
1812                 while (*++optr) {
1813                         switch (*optr & (255-32)) {     /* uppercase */
1814 #if 0
1815                         case 'R':       /* actually case sensitive... */
1816                                 lo->recursive = 1;
1817                                 break;
1818 #endif
1819                         case 'L':
1820                                 lo->long_listing = 1;
1821                                 break;
1822                         case 'F':
1823                                 lo->classify = 1;
1824                                 break;
1825                         case ' ':
1826                                 fptr = optr + 1;
1827                                 *(optr--) = 0;
1828                                 break;
1829                         default:
1830                                 break;
1831                         }
1832                 }
1833         } else {
1834                 fptr = c->recv_buf;
1835         }
1836         
1837         /* then we chdir to the dir in fptr (if any) */
1838         tmp = fptr ? strrchr(fptr, '/') : NULL;
1839         if (tmp != NULL) {
1840                 tmp[0] = 0;
1841                 if (do_chdir(c, fptr) == -1) return -1;
1842                 fptr = tmp + 1;
1843         } else {
1844                 /* current directory */
1845                 TRAP_ERROR(chdir(c->curr_dir) == -1, 550, return -1);
1846         }
1847
1848         /* if no argument, choose all files */
1849         if (fptr == NULL || fptr[0] == 0) fptr = "*";
1850         *ptr = fptr;
1851
1852 #if WANT_NONROOT
1853         getcwd(chd, 512);
1854         if (nr_check_permission(c->uid, chd, 4, 1, NULL) == -1) {
1855                 numeric(c, 550, "Permission denied");
1856                 return -1;
1857         }
1858 #endif
1859
1860 #if !HAVE_MMAP
1861         tfname = tempnam(NULL, "ftp");
1862
1863 #if WANT_NONROOT
1864         if (tfname == NULL) tfname = tempnam("/", "ftp");
1865 #endif
1866
1867         TRAP_ERROR(tfname == NULL, 550, return -1);
1868         strcpy(f->filename, tfname);
1869         free(tfname);
1870
1871         f->local_file = open(f->filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
1872         TRAP_ERROR(f->local_file == -1, 550, return -1);
1873 #endif
1874         f->dir_listing = 1;
1875 #if WANT_UPLOAD
1876         f->upload = 0;
1877 #endif
1878
1879         return 0;
1880 }
1881
1882 /*
1883  * classify():  Takes a mode_t argument (from `struct stat'), and returns
1884  *              the parameter to be used in an `ls -F'-style listing.
1885  */
1886 char classify(const mode_t mode)
1887 {
1888         if (S_ISREG(mode)) {
1889                 if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
1890                         return '*';
1891                 } else {
1892                         return '\0';
1893                 }
1894         }
1895         if (S_ISDIR(mode)) return '/';
1896         if (S_ISLNK(mode)) return '@';
1897         if (S_ISSOCK(mode)) return '=';
1898         if (S_ISFIFO(mode)) return '|';
1899         return '\0'; 
1900 }