]> git.sesse.net Git - betaftpd/blob - cmds.c
Updated documentation to tell that root can't FTP anymore.
[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, 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, return 1);
829
830         numeric(c, 250, "File renamed successfulyy.");
831         return 1;
832 }
833
834 /*
835  * cmd_mkd():   Handle the MKD/XMKD command (create a new directory).
836  *              RFC959 is not clear on the error codes for this command --
837  *              one place, 521 is cited as the correct error, but is
838  *              mentioned nowhere else. Different FTP servers differ here
839  *              as well. Thus, I've followed what appears to be the intention
840  *              (having `analogous' errors with STOR), and use 550 instead.
841  *
842  *              Making directories is probably the topic covered most
843  *              extensively by RFC959 (and in the most confusing way as
844  *              well). I try to follow the conventions, but it isn't always
845  *              easy :-) (This code isn't quite easy to understand, because
846  *              temp2 is used twice, in two different roles.)
847  */
848 int cmd_mkd(struct conn * const c)
849 {
850         const char * const fname = translate_path(c, c->recv_buf);
851         char temp[512], temp2[1024], *cdir;
852         int i, j;
853
854         TRAP_ERROR(fname == NULL || mkdir(fname, 0755) == -1, 550, return 1);
855
856         chdir(fname);
857         getcwd(temp2, 512);
858         cdir = do_pwd(c, temp, temp2);
859
860         /* double the quotes in the output */ 
861         for (i = 0, j = 0; i <= strlen(cdir); i++, j++) {
862                 temp2[j] = cdir[i];
863                 if (cdir[i] == '"') {
864                         temp2[++j] = '"';
865                 }
866         }
867         numeric(c, 257, "\"%s\" created.", temp2);
868         return 1;
869 }
870
871 /*
872  * cmd_rmd():   Handle the RMD/XRMD command. Works just like DELE, only for
873  *              directories.
874  */
875 int cmd_rmd(struct conn * const c)
876 {
877         const char * const fname = translate_path(c, c->recv_buf);
878
879         TRAP_ERROR(fname == NULL || rmdir(fname) == -1, 550, return 1);
880         numeric(c, 250, "Directory deleted.");
881         return 1;
882 }
883
884 /*
885  * cmd_allo():  Handle the ALLO command. The command does not do anything, except
886  *              sit around and play compliant. Some Windows FTP servers (Serv-U,
887  *              for instance), verifies that there is enough space on the disk,
888  *              but since we have no idea on what the filesystem will be stored on,
889  *              we just ignore the command.
890  *
891  *              We could theoretically use this information to give more information
892  *              to the full-screen mode, but close to no FTP clients send this
893  *              command, and it would touch too much code.
894  */
895 int cmd_allo(struct conn * const c)
896 {
897         numeric(c, 202, "No storage allocation necessary.");
898         return 1;
899 }
900
901 /*
902  * cmd_stat():  Handle the STAT command. Please see README for more details.
903  *              Note that this command is run with euid=root, since it has
904  *              to be able to run before USER.
905  *
906  *              Note that we need to bypass numeric(), to get a multi-line
907  *              reply.
908  */
909 #if WANT_STAT
910 char conn_state[5][27] = {
911         "Not logged in",
912         "Waiting for e-mail address",
913         "Waiting for password",
914         "Logged in",
915         "Waiting for password",         /* actually non-existant user */
916 };
917
918 char ftran_state[6][42] = {
919         "Not initialized",
920         "Decided PASV address/port",
921         "Waiting on PASV socket",
922         "Got PORT address/port",
923         "Connecting on PORT address/port",
924         "Transferring file (or connecting on PORT)"
925 };
926 #endif
927
928 int cmd_stat(struct conn * const c)
929
930 #if WANT_STAT
931         char buf[1024];
932         int i, err;
933         struct ftran *f = c->transfer;
934
935         snprintf(buf, 1024, "211- FTP server status:\r\n"
936                             "     BetaFTPD version " VERSION " (http://members.xoom.com/sneeze/betaftpd.html)\r\n"
937                             "     Connected to %s\r\n"
938                             "     Control connection state: %s\r\n"
939                             "     TYPE: Image; STRUcture: File; transfer MODE: Stream\r\n"
940                             "     Data connection state: %s\r\n"
941                             "211 End of status\r\n",
942                                 inet_ntoa(((struct sockaddr_in *)(&(c->addr)))->sin_addr),
943                                 conn_state[c->auth], (f) ? ftran_state[f->state] : ftran_state[0]);
944
945         i = strlen(buf);
946
947         err = send(c->sock, buf, i, 0);
948         if (err == -1 && errno == EPIPE) {
949                 destroy_conn(c);
950                 return 0;
951         }
952 #else
953         numeric(c, 502, "STAT command disabled for security reasons.");
954 #endif
955         return 1;
956 }
957
958 #if HAVE_MMAP
959 /*
960  * _mwrite():   This define is for mmap-listing. It works as a write()
961  *              (not in parameter, but in function), and is used in
962  *              cmd_list() and cmd_nlst() only.
963  *
964  *              Note that this function returns the new position in the
965  *              `file'. The caller is expected to send this information
966  *              back in `pos' at the next call to _mwrite().
967  */
968 int _mwrite(const char * const buf, const struct ftran * const f,
969             const int pos, const int count, const int size)
970 {
971         if (pos + count >= size) return size;   /* out of space */
972         memcpy(f->file_data + pos, buf, count);
973         return pos + count;
974 }
975 #endif
976
977 /*
978  * mwrite:      This is a short_hand define, making calls to _mwrite() very
979  *              similiar to calls to write().
980  */
981 #define mwrite(buf, count) pos = _mwrite((buf), (f), (pos), (count), (size));
982
983 /*
984  * long_listing():
985  *              Formats output in `ls -l' style. It returns one line for the
986  *              file PATHNAME, and returns it in retbuf. Setting do_classify
987  *              to nonzero has the same effect as `ls -F'.
988  *
989  *              This command is so long, because simply there is so much to
990  *              be done. GNU ls has some extra functions, but it's close to
991  *              3000 lines too...
992  */
993 int long_listing(char * const retbuf, const char * const pathname, const int do_classify)
994 {
995         int i, year;
996         char newd[512], temp[1026];
997         struct stat buf;
998         struct tm *t;
999         time_t now;
1000         char username[17], groupname[17];
1001
1002         time(&now);
1003         year = localtime(&now)->tm_year;
1004         {
1005 #if !WANT_NONROOT
1006                 struct passwd *p;
1007                 struct group *g;
1008 #endif
1009
1010                 if (lstat(pathname, &buf) == -1) return 0;
1011
1012 #if WANT_NONROOT
1013                 strcpy(username, nr_get_uname(buf.st_uid));
1014                 strcpy(groupname, nr_get_gname(buf.st_gid));
1015 #else
1016                 p = getpwuid(buf.st_uid);
1017                 if (p != NULL) {
1018                         strncpy(username, p->pw_name, 16);
1019                         username[16] = 0;
1020                 } else {
1021                         snprintf(username, 16, "%u", buf.st_uid);
1022                 }
1023
1024                 g = getgrgid(buf.st_gid);
1025                 if (g != NULL) {
1026                         strncpy(groupname, g->gr_name, 16);
1027                         groupname[16] = 0;
1028                 } else {
1029                         snprintf(groupname, 16, "%u", buf.st_gid);
1030                 }
1031 #endif
1032         }
1033
1034         /*
1035          * This POSIX approximation is based on GNU ls code (and obfuscated
1036          * a bit...), to be compatible with `real' ls implementations.
1037          */
1038         t = localtime(&(buf.st_mtime));
1039         strftime(newd, 512, ((now > buf.st_mtime + 6L * 30L * 24L * 60L * 60L) ||
1040                              (now < buf.st_mtime - 60L * 60L))
1041                         ? "%b %e  %Y" : "%b %e %H:%M", t);
1042
1043         {
1044 #if WANT_NONROOT
1045                 char rights[16];
1046
1047                 if (nr_check_permission(0, pathname, 0, (buf.st_mode & S_IFDIR), rights) == -1) {
1048                         /* no permission to even see this file */
1049                         return 0;
1050                 }
1051
1052                 i = snprintf(temp, 1024, "%c%s %3u %-8s %-8s %8lu %12s %s\r\n",
1053 #else
1054                 i = snprintf(temp, 1024, "%c%c%c%c%c%c%c%c%c%c %3u %-8s %-8s %8lu %12s %s",
1055 #endif
1056                         decode_mode(buf.st_mode),
1057 #if WANT_NONROOT
1058                         rights,
1059 #else
1060                         (buf.st_mode & S_IRUSR) ? 'r' : '-', 
1061                         (buf.st_mode & S_IWUSR) ? 'w' : '-',
1062                         (buf.st_mode & S_IXUSR) ? ((buf.st_mode & S_ISUID) ? 's' : 'x') : '-',
1063                         (buf.st_mode & S_IRGRP) ? 'r' : '-',
1064                         (buf.st_mode & S_IWGRP) ? 'w' : '-',
1065                         (buf.st_mode & S_IXGRP) ? ((buf.st_mode & S_ISGID) ? 's' : 'x') : '-',
1066                         (buf.st_mode & S_IROTH) ? 'r' : '-',
1067                         (buf.st_mode & S_IWOTH) ? 'w' : '-',
1068                         (buf.st_mode & S_IXOTH) ? ((buf.st_mode & S_ISVTX) ? 't' : 'x') : '-',
1069 #endif
1070                         buf.st_nlink, username, groupname,
1071                         (unsigned long)(buf.st_size), newd, pathname);
1072
1073 #if 0
1074                 /*
1075                  * vim needs this extra character for some reason... It's too 
1076                  * bad I'll have to do it this way, but syntax colouring
1077                  * that works properly is almost a `must' for me :-)
1078                  */
1079                 )
1080 #endif
1081  
1082                 /* add an extra classification `sign' if we got -F */
1083                 if (do_classify) {
1084                         int len = strlen(temp);
1085                         temp[len] = classify(buf.st_mode);
1086                         temp[len + 1] = '\0';
1087                 }
1088         }
1089
1090         strcpy(retbuf, temp);
1091         return 1;
1092 }
1093
1094 /*
1095  * cmd_list():  Handles the LIST command (directory listing). Does a
1096  *              long listing (of type `ls -l'). The listing work is
1097  *              done by do_listing(), below.
1098  */
1099 int cmd_list(struct conn * const c)
1100 {
1101         struct list_options lo;
1102
1103 /*      lo.recursive = 0; */
1104         lo.long_listing = 1;
1105         lo.classify = 0;
1106
1107         do_listing(c, &lo);
1108         return 1;
1109 }
1110
1111 /*
1112  * cmd_nlst():  Handles the NLST command (plain directory listing).
1113  *              Does a plain listing (no dates etc.), unless overridden
1114  *              by the `-l' or `-L' flag (case insensitivity because most
1115  *              FTP clients don't have a clue about what they send out). 
1116  *              The listing work is done by do_listing(), below.
1117  */     
1118 int cmd_nlst(struct conn * const c)
1119 {
1120         struct list_options lo;
1121
1122 /*      lo.recursive = 0; */
1123         lo.long_listing = 0;
1124         lo.classify = 0;
1125
1126         do_listing(c, &lo);
1127         return 1;
1128 }
1129
1130 /*
1131  * do_listing():
1132  *              Prepares any listing buffers, temp files, etc., before
1133  *              pushing the work one step further :-)
1134  *
1135  *              If the directory listing cache is enabled, the cache
1136  *              is checked first, to see if we still have a valid entry.
1137  */
1138 void do_listing(struct conn * const c, struct list_options * const lo)
1139 {
1140         int i;
1141         char *ptr;
1142 #if HAVE_MMAP
1143         int size;
1144 #endif
1145         struct ftran * const f = c->transfer;
1146
1147 #if WANT_DCACHE
1148         char cwd[256];
1149 #endif
1150
1151 #if WANT_NONROOT
1152 #warning No nonroot checking for list_core() yet
1153 #endif
1154
1155         i = prepare_for_listing(c, &ptr, lo);
1156         if (i == -1) {
1157                 destroy_ftran(c->transfer);
1158                 return;
1159         }
1160
1161 #if WANT_DCACHE
1162         getcwd(cwd, 256);
1163 #endif
1164
1165 #if HAVE_MMAP
1166         strcpy(f->filename, "(directory listing)");
1167 #endif
1168
1169 #if WANT_DCACHE
1170         {
1171                 struct dcache *d = NULL, *next = first_dcache->next_dcache;
1172                 struct stat buf;
1173
1174                 if (stat(cwd, &buf) > -1) {
1175                         /* run through the linked list */
1176                         while (next != NULL) {
1177                                 d = next;
1178                                 next = d->next_dcache;
1179         
1180                                 if (buf.st_mtime <= d->generated &&
1181                                     strcmp(d->dir_name, cwd) == 0 &&
1182                                     strcmp(d->pattern, ptr) == 0 &&
1183                                     memcmp(&(d->lo), lo,
1184                                            sizeof(struct list_options)) == 0) {
1185                                         d->use_count++;
1186                                         f->dir_cache = d;
1187                                         f->file_data = d->dir_data;
1188                                         f->size = d->dir_size;
1189                                         f->dir_listing = 1;
1190                                         f->pos = 0;
1191                                         prepare_for_transfer(f);
1192                                         return;
1193                                 }
1194                         }
1195                 }
1196         }
1197 #endif
1198
1199 #if HAVE_MMAP
1200         {
1201                 int num_files = get_num_files(c, ptr, lo);
1202                 if (num_files == -1) return;
1203
1204                 size = num_files * 160;
1205                 f->file_data = malloc(size + 1);
1206                 TRAP_ERROR(f->file_data == NULL, 550, return);
1207                 list_core(c, ptr, lo, size);
1208         }
1209 #else
1210         list_core(c, ptr, lo);
1211 #endif
1212
1213 #if WANT_DCACHE
1214         /* populate the directory listing cache */
1215         {
1216                 struct stat buf;
1217                 struct dcache *d = alloc_new_dcache();
1218                 if (d != NULL && stat(cwd, &buf) > -1) {
1219                         d->use_count++;
1220                         f->dir_cache = d;
1221                         d->dir_data = f->file_data;
1222                         d->dir_size = f->size;
1223                         d->generated = buf.st_mtime;
1224
1225                         strcpy(d->dir_name, cwd);
1226                         strncpy(d->pattern, ptr, 255);
1227                         d->pattern[255] = 0;
1228                         d->lo = *lo;
1229                 }
1230         }
1231 #endif
1232
1233 #if HAVE_MMAP
1234         f->pos = 0;
1235 #endif
1236         prepare_for_transfer(f);
1237 }
1238
1239 /*
1240  * get_num_files():
1241  *              Get the number of files in PATHNAME (optionally matching
1242  *              a pattern). Note that c is needed for TRAP_ERROR.
1243  */
1244 int get_num_files(struct conn * const c, const char * const pathname,
1245                    struct list_options * const lo)
1246 {
1247         int num_files;
1248         glob_t pglob;
1249
1250         /*
1251          * glob() fails to set errno correctly, so we simply guess on
1252          * `permission denied'... The others are far less likely to happen.
1253          */
1254         switch (glob(pathname, 0, NULL, &pglob)) {
1255 #ifdef GLOB_NOMATCH
1256         case GLOB_NOMATCH:
1257                 return 0;
1258 #endif
1259         case 0:
1260                 num_files = pglob.gl_pathc;
1261                 break;
1262         default:
1263                 numeric(c, 550, strerror(EACCES));
1264                 return -1;
1265         }
1266
1267 #if 0   /* the rest of the code doesn't support recursion yet */
1268         if (lo->recursive) {
1269                 for (i = 0; i < pglob.gl_pathc; i++) {
1270                         char *temp = pglob.gl_pathv[i];
1271                         struct stat buf;
1272
1273                         lstat(temp, &buf);
1274                         if (S_ISDIR(buf.st_mode)) {
1275                                 chdir(temp);
1276                                 num_files += get_num_files(c, "*", lo);
1277                                 chdir("..");
1278                         }
1279                 }
1280         }
1281 #endif
1282
1283         return num_files;
1284 }
1285
1286 /*
1287  * list_core(): Enumerate all the files in PATHNAME, and formats them
1288  *              according to list_options (calling format functions if
1289  *              required).
1290  *
1291  *              Note that we don't do any realloc() yet, so if your
1292  *              _average_ file name length is over a certain size (a little
1293  *              under 80 for long listings, and a little under 160 for
1294  *              short listings), the list will be truncated. Fix...
1295  *
1296  *              This function is rather long.
1297  */
1298 void list_core(struct conn * const c, const char * const pathname,
1299                struct list_options * const lo
1300 #if HAVE_MMAP
1301                 , const int size
1302 #endif
1303                 )
1304 {
1305         int i;
1306         glob_t pglob;
1307 #if HAVE_MMAP
1308         int pos = 0;
1309 #endif
1310         struct ftran * const f = c->transfer;
1311
1312         /*
1313          * glob() fails to set errno correctly, so we simply guess on
1314          * `permission denied'... The others are far less likely to happen.
1315          */
1316         switch (glob(pathname, GLOB_MARK, NULL, &pglob)) {
1317         case 0:
1318 #ifdef GLOB_NOMATCH
1319         case GLOB_NOMATCH:
1320 #endif
1321                 break;
1322         default:
1323                 numeric(c, 550, strerror(EACCES));
1324                 return;
1325         }
1326
1327         if (lo->long_listing) {
1328                 /* FIX: we may get too high total number if we are running nonroot! */
1329                 struct stat buf;
1330                 long unsigned int total = 0;
1331                 char temp[1024];
1332
1333                 for (i = 0; i < pglob.gl_pathc; i++) {
1334                         if (lstat(pglob.gl_pathv[i], &buf) != -1) {
1335                                 total += buf.st_blocks;
1336                         }
1337                 }
1338                 i = snprintf(temp, 1024, "total %lu\r\n", total >> 1); 
1339 #if HAVE_MMAP
1340                 mwrite(temp, i);
1341 #else
1342                 write(f->local_file, temp, i);
1343 #endif
1344         }
1345
1346         for (i = 0; i < pglob.gl_pathc; i++) {
1347                 char * const temp = pglob.gl_pathv[i];
1348                 char buf[2048];
1349
1350                 /* strip `/' away from the pathname -- add it later if -F */
1351                 {
1352                         int len = strlen(temp);
1353                         if (temp[len - 1] == '/') {
1354                                 temp[len - 1] = '\0';
1355                         }
1356                 }
1357
1358                 if (lo->long_listing) {
1359                         if (long_listing(buf, temp, lo->classify) == 0) continue;
1360                 } else {
1361                         strcpy(buf, temp);
1362                         if (lo->classify) {
1363                                 struct stat statbuf;
1364
1365                                 if (lstat(buf, &statbuf) != -1) {
1366                                         const int len = strlen(buf);
1367
1368                                         buf[len] = classify(statbuf.st_mode);
1369                                         buf[len + 1] = 0;
1370                                 }
1371                         }
1372                 }
1373
1374                 /* support recursion here some day... */
1375
1376 #if HAVE_MMAP
1377                 mwrite(buf, strlen(buf));
1378                 mwrite("\r\n", 2);
1379 #else
1380                 write(f->local_file, buf, strlen(buf));
1381                 write(f->local_file, "\r\n", 2);
1382 #endif
1383         }
1384
1385 #if HAVE_MMAP
1386         f->size = pos;
1387 #else
1388         lseek(f->local_file, 0, SEEK_SET);
1389 #endif
1390
1391         globfree(&pglob);
1392 }
1393
1394 /*
1395  * cmd_noop():  Handles the NOOP command. Does nothing, doesn't even
1396  *              reset the timeout.
1397  */
1398 int cmd_noop(struct conn * const c)
1399 {
1400         numeric(c, 200, "NOOP command successful.");
1401         return 1;
1402 }
1403
1404 /*
1405  * cmd_syst():  Handles the SYST command. Returns the system identification.
1406  */
1407 int cmd_syst(struct conn * const c)
1408 {
1409         numeric(c, 215, "UNIX Type: L%u", NBBY);
1410         return 1;
1411 }
1412
1413 /*
1414  * cmd_type():  Handles the TYPE command.
1415  */
1416 int cmd_type(struct conn * const c)
1417 {
1418 #if WANT_ASCII
1419         c->recv_buf[0] &= (255-32);     /* convert to upper case */
1420         if (c->recv_buf[0] == 'A') {
1421                 c->ascii_mode = 1;
1422                 numeric(c, 200, "Type is ASCII.");
1423         } else if (c->recv_buf[0] == 'I') {
1424                 c->ascii_mode = 0;
1425                 numeric(c, 200, "Type is IMAGE.");
1426         } else {
1427                 numeric(c, 504, "Unknown type.");
1428         }
1429 #else
1430         numeric(c, 200, "TYPE ignored (always I)");
1431 #endif
1432         return 1;
1433 }
1434
1435 /*
1436  * cmd_mode():  Handles the MODE command. We always use stream mode,
1437  *              so the argument is ignored.
1438  */
1439 int cmd_mode(struct conn * const c)
1440 {
1441         numeric(c, 200, "MODE ignored (always S)");
1442         return 1;
1443 }
1444
1445 /*
1446  * cmd_stru():  Handles the STRU command. We always use file mode,
1447  *              so the argument is ignored.
1448  */
1449 int cmd_stru(struct conn * const c)
1450 {
1451         numeric(c, 200, "STRU ignored (always F)");
1452         return 1;
1453 }
1454
1455 /*
1456  * cmd_help():  Handle the HELP command. I'm sorry, but I'm unwilling
1457  *              to use a lot of space to explain the RFCs in such a message,
1458  *              and BetaFTPD doesn't have any special things that should
1459  *              be noted anywhere. Thus, this message is close to empty. I
1460  *              feel that a 5xx entry would have been better, but that is
1461  *              disallowed.
1462  *
1463  *              As with ACCT, this command is supposed to be executed from
1464  *              everywhere, so we have to run without setuid. I don't like
1465  *              it, but at the same time I have to idea what could go
1466  *              wrong...
1467  *
1468  *              Perhaps I should make this message sound a little less
1469  *              like an error, since the error code is intended for helpful
1470  *              messages? :-)
1471  */
1472 int cmd_help(struct conn * const c)
1473 {
1474         numeric(c, 414, "Sorry, no detailed help; use standard FTP commands.");
1475         return 1;
1476 }
1477
1478 /*
1479  * cmd_quit():  Handles the QUIT command, which shuts down the control
1480  *              and data sockets.
1481  */
1482 int cmd_quit(struct conn * const c)
1483 {
1484         numeric(c, 221, "Have a nice day!");
1485         destroy_conn(c);
1486         return 0;
1487 }
1488
1489 /*
1490  * cmd_rein():  Handle the REIN command, which does close to a full reset
1491  *              of the connection. Much of the code here is intentionally
1492  *              copied directly from alloc_new_conn() -- perhaps we should
1493  *              modularize this?
1494  */
1495 int cmd_rein(struct conn * const c)
1496 {
1497         destroy_ftran(c->transfer);
1498         c->buf_len = c->auth = c->rest_pos = 0;
1499
1500         /* equals: strcpy(c->curr_dir, "/") ; strcpy(c->last_cmd, ""); */
1501         c->curr_dir[0] = '/';
1502 #if WANT_FULLSCREEN
1503         c->curr_dir[1] = c->last_cmd[0] = '\0';
1504 #else
1505         c->curr_dir[1] = '\0';
1506 #endif
1507
1508         time(&(c->last_transfer));
1509         numeric(c, 220, "BetaFTPD " VERSION " ready.");
1510
1511         return 1;
1512 }
1513
1514 #if DOING_PROFILING
1515 /*
1516  * cmd_exit():  Handles the EXIT command, my own `extension' to the
1517  *              FTP protocol... IMPORTANT: Only to be used for profiling
1518  *              purposes!! (It's needed to get some profiling data out
1519  *              of the server after compiling it with -pg, since such data
1520  *              is only written on a clear exit()). Any user (even those
1521  *              not logged in) can issue an EXIT, and make the server shut
1522  *              down without clearing any sockets etc. In other words:
1523  *              Don't use it on a production site.
1524  */
1525 void cmd_exit(struct conn * const c)
1526 {
1527         while (first_conn->next_conn)
1528                 destroy_conn(first_conn->next_conn);
1529         exit(0);
1530 }
1531 #endif
1532
1533 /*
1534  * parse_command():
1535  *              Gets a command from c->recv_buf, determines which command
1536  *              it is, sets proper effective user-ID and calls the command
1537  *              handler. Finally, it cleans up.
1538  *
1539  *              To me, this command seems optimizable, but I'm not really
1540  *              sure where :-)
1541  */
1542 void parse_command(struct conn *c)
1543 {
1544         int cmlen;
1545         const struct handler *h = handler_table;        /* first entry */
1546
1547         if (c == NULL) return;
1548
1549         /* strip any leading non-ASCII characters (including CR/LFs) */
1550         while (c->buf_len > 0 && (c->recv_buf[0] < 'a' || c->recv_buf[0] > 'z')
1551                               && (c->recv_buf[0] < 'A' || c->recv_buf[0] > 'Z')) {
1552                 remove_bytes(c, 1);             /* not good */
1553         }
1554
1555         /* scan, searching for CR or LF */      
1556         cmlen = strcspn(c->recv_buf, "\r\n");
1557         if (cmlen >= c->buf_len) return;
1558
1559 #if WANT_FULLSCREEN
1560         strncpy(c->last_cmd, c->recv_buf, cmlen);
1561         c->last_cmd[cmlen] = 0;
1562 #endif
1563
1564         do {
1565                 if ((cmlen >= (strlen(h->cmd_name) + h->add_cmlen)) &&
1566                     (strncasecmp(c->recv_buf, h->cmd_name, strlen(h->cmd_name)) == 0)) {
1567                         if (c->auth < h->min_auth) {
1568                                 numeric(c, 503, "Please login with USER and PASS.");
1569                                 while (c->recv_buf[0] != '\n') remove_bytes(c, 1);
1570                         } else {
1571                                 char schar;
1572
1573 #if !WANT_NONROOT
1574                                 if (h->do_setuid) {
1575                                         seteuid(c->uid);
1576                                 } else {
1577                                         seteuid(0);
1578                                 }
1579 #endif
1580
1581                                 remove_bytes(c, strlen(h->cmd_name));
1582                                 cmlen -= strlen(h->cmd_name);
1583                                 while (c->recv_buf[0] == ' ') {
1584                                         remove_bytes(c, 1);
1585                                         cmlen--;
1586                                 }
1587
1588                                 schar = c->recv_buf[cmlen];
1589                                 c->recv_buf[cmlen] = 0;
1590
1591                                 /* result of zero means the connection is freed */
1592                                 if (h->callback(c)) {
1593                                         c->recv_buf[cmlen] = schar;
1594 #if !WANT_NONROOT
1595                                         if (h->do_setuid) seteuid(getuid());
1596 #endif
1597                                         remove_bytes(c, cmlen);
1598                                 }
1599                         }
1600                         return;
1601                 }
1602         } while ((++h)->callback != NULL);
1603
1604         numeric(c, 500, "Sorry, no such command.");
1605         remove_bytes(c, cmlen); 
1606 }
1607
1608 /*
1609  * prepare_for_transfer():
1610  *              Prepares an ftran object for a file transfer, setting
1611  *              file size, opening sockets etc.
1612  *
1613  *              nonroot notice: prepare_for_transfer() assumes all access
1614  *              checks are already done.
1615  */
1616 void prepare_for_transfer(struct ftran *f)
1617 {
1618 #if WANT_NONROOT
1619 #warning No nonroot checking for prepare_for_transfer() yet
1620 #endif
1621
1622 #if HAVE_MMAP
1623         /* mmap doesn't make temp files for dir listings */
1624         if (!f->dir_listing) {
1625 #endif
1626
1627                 f->size = lseek(f->local_file, 0, SEEK_END);
1628                 errno = 0;
1629 #if WANT_UPLOAD
1630                 if (f->upload == 0 || f->append == 0 || f->owner->rest_pos != 0)
1631 #endif 
1632                         lseek(f->local_file, f->owner->rest_pos, SEEK_SET);
1633 #if HAVE_MMAP
1634         }
1635 #endif
1636         
1637         if (f->state == 1) {            /* PASV connection */
1638                 f->state = 2;           /* waiting */
1639         } else if (f->state == 3) {     /* PORT connection */
1640                 f->state = 4;
1641                 connect(f->sock, (struct sockaddr *)&f->sin, sizeof(f->sin));
1642                 add_fd(f->sock, POLLOUT);
1643         }
1644         time(&(f->tran_start));
1645 }
1646
1647 /*
1648  * decode_mode():
1649  *              Takes a mode_t argument (from a `struct stat'), and
1650  *              returns the proper dirlist letter for that type.
1651  *
1652  *              Note: S_IFLNK seems to be broken, or perhaps I just have
1653  *              missed something (S_IFLNK is always set for all *files* on
1654  *              my glibc 2.0.111 system).
1655  *
1656  *              The most common cases are put first, for speed :-)
1657  */
1658 char decode_mode(mode_t mode) {
1659         if (mode & S_IFREG)  return '-';
1660         if (mode & S_IFDIR)  return 'd';
1661         if (mode & S_IFLNK)  return 'l';
1662         if (mode & S_IFBLK)  return 'b';
1663         if (mode & S_IFCHR)  return 'c';
1664         if (mode & S_IFSOCK) return 's';
1665         if (mode & S_IFIFO)  return 'f';
1666
1667         return '-';
1668 }
1669
1670 /*
1671  * translate_path():
1672  *              Take an FTP path, do all neccessary root_dir checks,
1673  *              change to the correct directory and return the proper
1674  *              file name to open/stat/whatever. The path returned is
1675  *              relative to the current directory (NOT absolute). chdir()
1676  *              in any way will `destroy' this argument.
1677  *
1678  *              Note that `path' will be _changed_, and used as a return pointer
1679  *              base. Do not attempt to free the result from this function --
1680  *              if you need to, free path instead.
1681  */
1682 char *translate_path(struct conn * const c, char * const path)
1683 {
1684         char *ptr = NULL;
1685
1686         /* chdir to the right dir, then chop it off */
1687         chdir(c->curr_dir);
1688
1689         ptr = strrchr(path, '/');
1690         if (ptr != NULL) {
1691                 char save_char = ptr[0];
1692                 ptr[0] = 0;
1693
1694                 if (do_chdir(c, path) == -1) {
1695                         return NULL;
1696                 }
1697                 ptr[0] = save_char;
1698                 ptr++;
1699         } else {
1700                 ptr = path;
1701         }
1702         return ptr;
1703 }
1704
1705 /*
1706  * do_openfile():
1707  *              Opens the file PATH with access parameters FLAGS, translating
1708  *              paths and checking permissions as neccessary. Generally, this
1709  *              should be used whenever you need an open().
1710  *
1711  *              The parameters might be a bit confusing. To clarify them a bit:
1712  *              c:              IN/OUT (will be changed)
1713  *              path:           IN (but _will_ be changed)
1714  *              filename:       OUT
1715  *              flags:          IN
1716  *              check_perm:     IN
1717  */
1718 int do_openfile(struct conn * const c, char * const path,
1719                 char * const filename, const int flags
1720 #if WANT_NONROOT
1721                 , const int check_permission
1722 #endif
1723 )
1724 {
1725         char *ptr;
1726         struct stat buf;
1727
1728 #if WANT_NONROOT
1729         if (nr_check_permission(c->uid, path, check_permission, 0, NULL) == -1) {
1730                 return -1;
1731         }
1732 #endif
1733
1734         ptr = translate_path(c, c->recv_buf);
1735         if (ptr == NULL) return -1;
1736
1737 #if WANT_UPLOAD
1738         if ((flags & O_CREAT) == 0) {
1739 #endif
1740                 stat(ptr, &buf);
1741                 if (!S_ISREG(buf.st_mode)) {
1742                         numeric(c, 550, "%s: Not a plain file.", ptr);
1743                         return -2;
1744                 }
1745 #if WANT_UPLOAD
1746         }
1747 #endif
1748
1749         if (filename != NULL) { /* filename should always be != NULL */
1750                 strcpy(filename, ptr);
1751         }
1752         return open(ptr, flags, 0666);
1753 }
1754
1755 /*
1756  * prepare_for_listing():
1757  *              Parse list options, put them back into the list_options
1758  *              structure lo, and make temporary room for the list.
1759  */
1760 int prepare_for_listing(struct conn * const c, char ** const ptr,
1761                         struct list_options * const lo)
1762 {
1763 #if !HAVE_MMAP
1764         char *tfname;
1765 #endif
1766         struct ftran *f = c->transfer;
1767         char *tmp;
1768         char *optr = NULL, *fptr = NULL; 
1769 #if WANT_NONROOT
1770         char chd[512];
1771 #endif
1772
1773 #if WANT_NONROOT
1774 #warning No nonroot checking for prepare_for_listing() yet
1775 #endif
1776
1777         if ((f == NULL) || ((f->state != 1) && (f->state != 3))) {
1778                 numeric(c, 425, "No data connection set up; please use PASV or PORT.");
1779                 return -1;
1780         }
1781
1782         /*
1783          * A little parameter scanning is required here. There can only
1784          * be two parts: the directory name, and any options. We'll find
1785          * any options first.
1786          */
1787         if (c->recv_buf[0] == '-') {
1788                 optr = c->recv_buf;
1789         } else {
1790                 optr = strstr(c->recv_buf, " -");
1791         }
1792
1793         /* Then see if there are any options to parse. */
1794         if (optr != NULL) {
1795                 while (*++optr) {
1796                         switch (*optr & (255-32)) {     /* uppercase */
1797 #if 0
1798                         case 'R':       /* actually case sensitive... */
1799                                 lo->recursive = 1;
1800                                 break;
1801 #endif
1802                         case 'L':
1803                                 lo->long_listing = 1;
1804                                 break;
1805                         case 'F':
1806                                 lo->classify = 1;
1807                                 break;
1808                         case ' ':
1809                                 fptr = optr + 1;
1810                                 *(optr--) = 0;
1811                                 break;
1812                         default:
1813                                 break;
1814                         }
1815                 }
1816         } else {
1817                 fptr = c->recv_buf;
1818         }
1819         
1820         /* then we chdir to the dir in fptr (if any) */
1821         tmp = fptr ? strrchr(fptr, '/') : NULL;
1822         if (tmp != NULL) {
1823                 tmp[0] = 0;
1824                 if (do_chdir(c, fptr) == -1) return -1;
1825                 fptr = tmp + 1;
1826         } else {
1827                 /* current directory */
1828                 TRAP_ERROR(chdir(c->curr_dir) == -1, 550, return -1);
1829         }
1830
1831         /* if no argument, choose all files */
1832         if (fptr == NULL || fptr[0] == 0) fptr = "*";
1833         *ptr = fptr;
1834
1835 #if WANT_NONROOT
1836         getcwd(chd, 512);
1837         if (nr_check_permission(c->uid, chd, 4, 1, NULL) == -1) {
1838                 numeric(c, 550, "Permission denied");
1839                 return -1;
1840         }
1841 #endif
1842
1843 #if !HAVE_MMAP
1844         tfname = tempnam(NULL, "ftp");
1845
1846 #if WANT_NONROOT
1847         if (tfname == NULL) tfname = tempnam("/", "ftp");
1848 #endif
1849
1850         TRAP_ERROR(tfname == NULL, 550, return -1);
1851         strcpy(f->filename, tfname);
1852         free(tfname);
1853
1854         f->local_file = open(f->filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
1855         TRAP_ERROR(f->local_file == -1, 550, return -1);
1856 #endif
1857         f->dir_listing = 1;
1858 #if WANT_UPLOAD
1859         f->upload = 0;
1860 #endif
1861
1862         return 0;
1863 }
1864
1865 /*
1866  * classify():  Takes a mode_t argument (from `struct stat'), and returns
1867  *              the parameter to be used in an `ls -F'-style listing.
1868  */
1869 char classify(const mode_t mode)
1870 {
1871         if (S_ISREG(mode)) {
1872                 if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
1873                         return '*';
1874                 } else {
1875                         return '\0';
1876                 }
1877         }
1878         if (S_ISDIR(mode)) return '/';
1879         if (S_ISLNK(mode)) return '@';
1880         if (S_ISSOCK(mode)) return '=';
1881         if (S_ISFIFO(mode)) return '|';
1882         return '\0'; 
1883 }