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