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