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