Copyright (C) 1999-2000 Steinar H. Gunderson
This program is is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License, version 2 if the
+ it under the terms of the GNU General Public License, version 2 of the
License as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
#include <config.h>
#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
#if HAVE_STROPTS_H
#include <stropts.h>
#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
#if HAVE_SYS_CONF_H
#include <sys/conf.h>
#endif
#include <fcntl.h>
#endif
-#if HAVE_PWD_H
-#include <pwd.h>
-#endif
-
#if HAVE_GRP_H
#include <grp.h>
#endif
#include <sys/ioctl.h>
#endif
-#if HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif
-
#if HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#include <sys/conf.h>
#endif
-#if HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-
#if HAVE_SHADOW_H
#include <shadow.h>
#endif
#include <cmds.h>
#include <nonroot.h>
+#if WANT_DCACHE
+#include <dcache.h>
+#endif
+
#define lstat stat
extern struct conn *first_conn;
struct handler {
char cmd_name[6];
char add_cmlen; /* =1 if the command takes an argument */
- int (*callback)(struct conn * const);
+ void (*callback)(struct conn * const);
char min_auth;
#if !WANT_NONROOT
char do_setuid; /* =1 if root is not *really* needed */
* authentication work. User names are limited to 16
* characters, by force...
*/
-int cmd_user(struct conn * const c)
+void cmd_user(struct conn * const c)
{
strncpy(c->username, c->recv_buf, 16);
c->username[16] = 0;
numeric(c, 331, "Password required for %s.", c->username);
c->auth = 2;
}
- return 1;
}
/*
* don't even support PAM or real shadow passwords (with
* expiry etc) yet...
*/
-int cmd_pass(struct conn * const c)
+void cmd_pass(struct conn * const c)
{
#if WANT_NONROOT
c->auth = nr_userinfo(c->username, &c->uid, c->curr_dir, c->root_dir,
c->auth = 0;
} else {
c->uid = p->pw_uid;
+ c->gid = p->pw_gid;
strncpy(c->curr_dir, p->pw_dir, 254);
c->curr_dir[254] = 0;
}
) {
c->auth = 0;
} else {
- c->auth = 3;
+ c->auth = 4;
}
}
#endif /* !WANT_NONROOT */
+ /* root should not be allowed to FTP */
+ if (c->uid == 0) {
+ c->auth = 0;
+ }
if (c->auth == 0) {
numeric(c, 530, "Login incorrect.");
} else {
chdir(c->curr_dir);
dump_file(c, 230, "welcome.msg");
#endif
+ /* Have a different message for anonymous users? */
numeric(c, 230, "User logged in.");
}
- return 1;
}
/*
* I feel that the RFC959 intention is having it _before_
* USER/PASS. Therefore, this one runs with root privilegies :-)
*/
-int cmd_acct(struct conn * const c)
+void cmd_acct(struct conn * const c)
{
numeric(c, 202, "ACCT ignored OK -- not applicable on this system.");
- return 1;
}
/*
* the whole way), in case there are some weird overflows
* somewhere.
*/
-int cmd_port(struct conn * const c)
+void cmd_port(struct conn * const c)
{
short int a0, a1, a2, a3, p0, p1;
int i, sock, err;
if ((c->transfer != NULL) && (c->transfer->state >= 4)) {
numeric(c, 500, "Sorry, only one transfer at a time.");
- return 1;
+ return;
}
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- TRAP_ERROR(sock == -1, 500, return 1);
+ TRAP_ERROR(sock == -1, 500, return);
destroy_ftran(c->transfer);
c->transfer = f = alloc_new_ftran(sock, c);
/* bind to own address, port 20 (FTP data) */
tmp = sizeof(sin);
err = getsockname(c->sock, (struct sockaddr *)&sin, &tmp);
- TRAP_ERROR(err == -1, 500, return 1);
+ TRAP_ERROR(err == -1, 500, return);
sin.sin_port = FTP_PORT - 1;
numeric(c, 200, "PORT command OK.");
#if !WANT_NONROOT
/* need root privilegies for a short while */
seteuid(getuid());
+ setegid(getgid());
#endif
bind(sock, (struct sockaddr *)&sin, sizeof(sin));
#if !WANT_NONROOT
+ setegid(c->gid);
seteuid(c->uid);
#endif
i = 1;
ioctl(f->sock, FIONBIO, &one);
}
- return 1;
}
/*
* cmd_pasv(): Handles the PASV command, and sets up the data socket.
* Uses port numbers > 1024, since it doesn't run as root.
*/
-int cmd_pasv(struct conn * const c)
+void cmd_pasv(struct conn * const c)
{
struct ftran *f;
int tmp, sock, err;
if ((c->transfer != NULL) && (c->transfer->state >= 4)) {
numeric(c, 503, "Sorry, only one transfer at once.");
- return 1;
+ return;
}
destroy_ftran(c->transfer);
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- TRAP_ERROR(sock == -1, 500, return 1);
+ TRAP_ERROR(sock == -1, 500, return);
err = add_fd(sock, POLLIN);
- TRAP_ERROR(err != 0, 501, return 1);
+ TRAP_ERROR(err != 0, 501, return);
c->transfer = f = alloc_new_ftran(sock, c);
/* setup socket */
tmp = sizeof(addr);
err = getsockname(c->sock, (struct sockaddr *)&addr, &tmp);
- TRAP_ERROR(err == -1, 500, return 1);
+ TRAP_ERROR(err == -1, 500, return);
addr.sin_port = 0; /* let the system choose */
err = bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr));
- TRAP_ERROR(err == -1, 500, return 1);
+ TRAP_ERROR(err == -1, 500, return);
tmp = sizeof(addr);
err = getsockname(sock, (struct sockaddr *)&addr, &tmp);
- TRAP_ERROR(err == -1, 500, return 1);
+ TRAP_ERROR(err == -1, 500, return);
err = listen(f->sock, 1);
- TRAP_ERROR(err == -1, 500, return 1);
+ TRAP_ERROR(err == -1, 500, return);
f->state = 1;
numeric(c, 227, "Entering passive mode (%u,%u,%u,%u,%u,%u)",
(htonl(addr.sin_addr.s_addr) & 0x000000ff),
(htons(addr.sin_port) & 0xff00) >> 8,
(htons(addr.sin_port) & 0x00ff));
- return 1;
}
/*
* /betaftpd.users file, if you use nonroot. If not, it's a bug.
* Try to get it _reproducible_, and mail it to me.
*/
-int cmd_pwd(struct conn * const c)
+void cmd_pwd(struct conn * const c)
{
char temp[512], *cdir = NULL;
if (cdir != NULL) {
numeric(c, 257, "\"%s\" is current working directory.", cdir);
}
- return 1;
}
/*
char *cdir = NULL;
strcpy(retbuf, dir);
+
if (strncmp(retbuf, c->root_dir, strlen(c->root_dir)) != 0) {
numeric(c, 550, "curr_dir is outside root_dir, please contact site administrator.");
return NULL;
* cmd_cwd(): Handles CWD command (change working directory). Uses
* cmd_cwd_internal() (see below).
*/
-int cmd_cwd(struct conn * const c)
+void cmd_cwd(struct conn * const c)
{
cmd_cwd_internal(c, c->recv_buf);
- return 1;
}
/*
* an error, instead of just staying in the root directory (as
* the OS and thus wu-ftpd does).
*/
-int cmd_cdup(struct conn * const c)
+void cmd_cdup(struct conn * const c)
{
cmd_cwd_internal(c, "..");
- return 1;
}
/*
* sending functions to start at the correct number. We should
* perhaps add some better error checking to this?
*/
-int cmd_rest(struct conn * const c)
+void cmd_rest(struct conn * const c)
{
c->rest_pos = abs(atoi(c->recv_buf));
numeric(c, 350, "Setting resume at %u bytes.", c->rest_pos);
- return 1;
}
/*
* connection occurs (or succeeds, if we're using PORT mode),
* the actual file transfer begins.
*/
-int cmd_retr(struct conn * const c)
+void cmd_retr(struct conn * const c)
{
struct ftran *f = c->transfer;
if ((f == NULL) || ((f->state != 1) && (f->state != 3))) {
numeric(c, 425, "No data connection set up; please use PASV or PORT.");
- return 1;
+ return;
}
#if WANT_ASCII
if ((c->rest_pos > 0) && (c->ascii_mode == 1)) {
numeric(c, 500, "Cannot resume while in ASCII mode.");
- return 1;
+ return;
}
#endif
#endif
prepare_for_transfer(f);
}
- return 1;
}
#if WANT_UPLOAD
* cmd_stor(): Handles the STOR command (upload file). Pushes the
* work down to do_store(), below.
*/
-int cmd_stor(struct conn * const c)
+void cmd_stor(struct conn * const c)
{
do_store(c, 0);
- return 1;
}
/*
* cmd_appe(): Handles the APPE command (append to file). Pushes
* the work down to do_store(), below.
*/
-int cmd_appe(struct conn * const c)
+void cmd_appe(struct conn * const c)
{
do_store(c, 1);
- return 1;
}
/*
* size of all the files in the directory, rather how
* much space the directory inode uses.
*/
-int cmd_size(struct conn * const c)
+void cmd_size(struct conn * const c)
{
#if WANT_ASCII
if (c->ascii_mode) {
numeric(c, 550, "SIZE not available in ASCII mode.");
- return 1;
+ return;
}
#endif
{
const char * const fname = translate_path(c, c->recv_buf);
struct stat buf;
- TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return 1);
+ TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return);
numeric(c, 213, "%lu", (unsigned long)(buf.st_size));
- return 1;
}
}
* date/time of a file. See the comments on cmd_size(),
* above.
*/
-int cmd_mdtm(struct conn * const c)
+void cmd_mdtm(struct conn * const c)
{
const char * const fname = translate_path(c, c->recv_buf);
struct stat buf;
struct tm *m;
- TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return 1);
+ TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return);
m = gmtime(&(buf.st_mtime)); /* at least wu-ftpd does it in GMT */
numeric(c, 213, "%u%02u%02u%02u%02u%02u", m->tm_year + 1900,
m->tm_mon + 1, m->tm_mday, m->tm_hour, m->tm_min, m->tm_sec);
- return 1;
}
/*
* cmd_abor(): Handle the ABOR command (abort a file transfer). This should
* be clean enough, but isn't tested extensively.
*/
-int cmd_abor(struct conn * const c)
+void cmd_abor(struct conn * const c)
{
if (c->transfer != NULL) {
numeric(c, 426, "File transfer aborted.");
destroy_ftran(c->transfer);
}
numeric(c, 226, "ABOR command processed OK.");
- return 1;
}
/*
* cmd_dele(): Handle the DELE command (delete a file).
*/
-int cmd_dele(struct conn * const c)
+void cmd_dele(struct conn * const c)
{
const char * const fname = translate_path(c, c->recv_buf);
- TRAP_ERROR(fname == NULL || unlink(fname) == -1, 550, return 1);
+ TRAP_ERROR(fname == NULL || unlink(fname) == -1, 550, return);
numeric(c, 250, "File deleted OK.");
- return 1;
}
/*
* cmd_rnfr(): Handle the RNFR command (take a filename to rename from).
*/
-int cmd_rnfr(struct conn * const c)
+void cmd_rnfr(struct conn * const c)
{
const char * const fname = translate_path(c, c->recv_buf);
char cwd[256];
struct stat buf;
c->rename_from[0] = '\0';
- if (fname == NULL) return 1;
+ if (fname == NULL) return;
getcwd(cwd, 256);
snprintf(c->rename_from, 256, "%s/%s", cwd, fname);
/* Just check that the file exists. */
- TRAP_ERROR(lstat(c->rename_from, &buf) == -1, 550, return 1);
+ TRAP_ERROR(lstat(c->rename_from, &buf) == -1, 550, c->rename_from[0] = '\0'; return);
numeric(c, 350, "File exists, send RNTO.");
- return 1;
}
/*
* cmd_rnto(): Handle the RNTO command (do the actual renaming).
*/
-int cmd_rnto(struct conn * const c)
+void cmd_rnto(struct conn * const c)
{
const char * const fname = translate_path(c, c->recv_buf);
- if (fname == NULL) return 1;
+ if (fname == NULL) return;
if (c->rename_from[0] == '\0') {
numeric(c, 503, "Please send RNFR first.");
- return 1;
+ return;
}
- TRAP_ERROR(rename(c->rename_from, fname) == -1, 550, return 1);
+ TRAP_ERROR(rename(c->rename_from, fname) == -1, 550, c->rename_from[0] = '\0'; return);
+ c->rename_from[0] = '\0';
- numeric(c, 250, "File renamed successfulyy.");
- return 1;
+ numeric(c, 250, "File renamed successfully.");
}
/*
* easy :-) (This code isn't quite easy to understand, because
* temp2 is used twice, in two different roles.)
*/
-int cmd_mkd(struct conn * const c)
+void cmd_mkd(struct conn * const c)
{
const char * const fname = translate_path(c, c->recv_buf);
char temp[512], temp2[1024], *cdir;
int i, j;
- TRAP_ERROR(fname == NULL || mkdir(fname, 0755) == -1, 550, return 1);
+ TRAP_ERROR(fname == NULL || mkdir(fname, 0755) == -1, 550, return);
chdir(fname);
getcwd(temp2, 512);
cdir = do_pwd(c, temp, temp2);
+ if (do_pwd == NULL) return;
+
/* double the quotes in the output */
for (i = 0, j = 0; i <= strlen(cdir); i++, j++) {
temp2[j] = cdir[i];
}
}
numeric(c, 257, "\"%s\" created.", temp2);
- return 1;
}
/*
* cmd_rmd(): Handle the RMD/XRMD command. Works just like DELE, only for
* directories.
*/
-int cmd_rmd(struct conn * const c)
+void cmd_rmd(struct conn * const c)
{
const char * const fname = translate_path(c, c->recv_buf);
- TRAP_ERROR(fname == NULL || rmdir(fname) == -1, 550, return 1);
+ TRAP_ERROR(fname == NULL || rmdir(fname) == -1, 550, return);
numeric(c, 250, "Directory deleted.");
- return 1;
}
/*
* to the full-screen mode, but close to no FTP clients send this
* command, and it would touch too much code.
*/
-int cmd_allo(struct conn * const c)
+void cmd_allo(struct conn * const c)
{
numeric(c, 202, "No storage allocation necessary.");
- return 1;
}
/*
"Waiting for e-mail address",
"Waiting for password",
"Logged in",
- "Waiting for password", /* actually non-existant user */
+ "Logged in", /* non-anonymous */
};
char ftran_state[6][42] = {
};
#endif
-int cmd_stat(struct conn * const c)
+void cmd_stat(struct conn * const c)
{
#if WANT_STAT
char buf[1024];
" BetaFTPD version " VERSION " (http://members.xoom.com/sneeze/betaftpd.html)\r\n"
" Connected to %s\r\n"
" Control connection state: %s\r\n"
+#if WANT_ASCII
+ " TYPE: %s; STRUcture: File; transfer MODE: Stream\r\n"
+#else
" TYPE: Image; STRUcture: File; transfer MODE: Stream\r\n"
+#endif
" Data connection state: %s\r\n"
"211 End of status\r\n",
inet_ntoa(((struct sockaddr_in *)(&(c->addr)))->sin_addr),
- conn_state[c->auth], (f) ? ftran_state[f->state] : ftran_state[0]);
+ conn_state[c->auth],
+#if WANT_ASCII
+ (c->ascii_mode == 1) ? "ASCII, FORM: Nonprint" : "Image",
+#endif
+ (f) ? ftran_state[f->state] : ftran_state[0]);
i = strlen(buf);
err = send(c->sock, buf, i, 0);
if (err == -1 && errno == EPIPE) {
- destroy_conn(c);
- return 0;
+ this->free_me = 1;
}
#else
numeric(c, 502, "STAT command disabled for security reasons.");
#endif
- return 1;
}
#if HAVE_MMAP
/*
* mwrite: This is a short_hand define, making calls to _mwrite() very
- * similiar to calls to write().
+ * similiar to calls to write(). It works both with and without
+ * mmap().
*/
+#if HAVE_MMAP
#define mwrite(buf, count) pos = _mwrite((buf), (f), (pos), (count), (size));
+#else
+#define mwrite(buf, count) write(f->local_file, buf, count);
+#endif
/*
* long_listing():
#if WANT_NONROOT
char rights[16];
- if (nr_check_permission(0, pathname, 0, (buf.st_mode & S_IFDIR), rights) == -1) {
+ if (nr_check_permission(0, pathname, 0, (S_ISDIR(buf.st_mode)), rights) == -1) {
/* no permission to even see this file */
return 0;
}
- i = snprintf(temp, 1024, "%c%s %3u %-8s %-8s %8lu %12s %s\r\n",
+ snprintf(temp, 1024, "%c%s %3u %-8s %-8s %8lu %12s %s\r\n",
#else
- i = snprintf(temp, 1024, "%c%c%c%c%c%c%c%c%c%c %3u %-8s %-8s %8lu %12s %s",
+ snprintf(temp, 1024, "%c%c%c%c%c%c%c%c%c%c %3u %-8s %-8s %8lu %12s %s",
#endif
decode_mode(buf.st_mode),
#if WANT_NONROOT
#endif
buf.st_nlink, username, groupname,
(unsigned long)(buf.st_size), newd, pathname);
-
+ i = strlen(temp);
+
#if 0
/*
* vim needs this extra character for some reason... It's too
* long listing (of type `ls -l'). The listing work is
* done by do_listing(), below.
*/
-int cmd_list(struct conn * const c)
+void cmd_list(struct conn * const c)
{
struct list_options lo;
-/* lo.recursive = 0; */
+ lo.recursive = 0;
lo.long_listing = 1;
lo.classify = 0;
do_listing(c, &lo);
- return 1;
}
/*
* FTP clients don't have a clue about what they send out).
* The listing work is done by do_listing(), below.
*/
-int cmd_nlst(struct conn * const c)
+void cmd_nlst(struct conn * const c)
{
struct list_options lo;
-/* lo.recursive = 0; */
+ lo.recursive = 0;
lo.long_listing = 0;
lo.classify = 0;
do_listing(c, &lo);
- return 1;
}
/*
#if WANT_DCACHE
{
- struct dcache *d = NULL, *next = first_dcache->next_dcache;
- struct stat buf;
+ struct dcache *d = find_dcache(cwd, ptr, lo);
+ if (d != NULL) {
+ d->use_count++;
+ f->dir_cache = d;
+ f->file_data = d->dir_data;
+ f->size = d->dir_size;
+ f->dir_listing = 1;
+ f->pos = 0;
- if (stat(cwd, &buf) > -1) {
- /* run through the linked list */
- while (next != NULL) {
- d = next;
- next = d->next_dcache;
-
- if (buf.st_mtime <= d->generated &&
- strcmp(d->dir_name, cwd) == 0 &&
- strcmp(d->pattern, ptr) == 0 &&
- memcmp(&(d->lo), lo,
- sizeof(struct list_options)) == 0) {
- d->use_count++;
- f->dir_cache = d;
- f->file_data = d->dir_data;
- f->size = d->dir_size;
- f->dir_listing = 1;
- f->pos = 0;
- prepare_for_transfer(f);
- return;
- }
- }
+ prepare_for_transfer(f);
+ return;
}
}
#endif
size = num_files * 160;
f->file_data = malloc(size + 1);
TRAP_ERROR(f->file_data == NULL, 550, return);
- list_core(c, ptr, lo, size);
+ list_core(c, ptr, "", lo, size, 0);
}
#else
- list_core(c, ptr, lo);
+ list_core(c, ptr, "", lo);
#endif
#if WANT_DCACHE
- /* populate the directory listing cache */
- {
- struct stat buf;
- struct dcache *d = alloc_new_dcache();
- if (d != NULL && stat(cwd, &buf) > -1) {
- d->use_count++;
- f->dir_cache = d;
- d->dir_data = f->file_data;
- d->dir_size = f->size;
- d->generated = buf.st_mtime;
-
- strcpy(d->dir_name, cwd);
- strncpy(d->pattern, ptr, 255);
- d->pattern[255] = 0;
- d->lo = *lo;
- }
- }
+ populate_dcache(f, cwd, ptr, lo);
#endif
#if HAVE_MMAP
return -1;
}
-#if 0 /* the rest of the code doesn't support recursion yet */
if (lo->recursive) {
+ int i;
for (i = 0; i < pglob.gl_pathc; i++) {
char *temp = pglob.gl_pathv[i];
struct stat buf;
}
}
}
-#endif
+
+ globfree(&pglob);
return num_files;
}
* under 80 for long listings, and a little under 160 for
* short listings), the list will be truncated. Fix...
*
+ * The return value only makes sense if mmap()'ing, since it
+ * returns the number of bytes written into the buffer.
+ *
* This function is rather long.
*/
-void list_core(struct conn * const c, const char * const pathname,
- struct list_options * const lo
+int list_core(struct conn * const c, const char * const pathname,
+ char * const disp_pathname, struct list_options * const lo
#if HAVE_MMAP
- , const int size
+ , const int size, int pos
#endif
)
{
int i;
glob_t pglob;
-#if HAVE_MMAP
- int pos = 0;
-#endif
struct ftran * const f = c->transfer;
/*
#ifdef GLOB_NOMATCH
case GLOB_NOMATCH:
#endif
- break;
+ break; /* note: break, not return */
default:
numeric(c, 550, strerror(EACCES));
- return;
+#if HAVE_MMAP
+ return pos;
+#else
+ return 0;
+#endif
}
+ if (lo->recursive) {
+ if (disp_pathname[0] == '\0') {
+ mwrite(".:\r\n", 4);
+ } else {
+ char temp[1024];
+ int i;
+
+ snprintf(temp, 1024, "%s:\r\n", disp_pathname);
+ i = strlen(temp);
+ mwrite(temp, i);
+ }
+ }
if (lo->long_listing) {
/* FIX: we may get too high total number if we are running nonroot! */
struct stat buf;
total += buf.st_blocks;
}
}
- i = snprintf(temp, 1024, "total %lu\r\n", total >> 1);
-#if HAVE_MMAP
+ snprintf(temp, 1024, "total %lu\r\n", total >> 1);
+ i = strlen(temp);
mwrite(temp, i);
-#else
- write(f->local_file, temp, i);
-#endif
}
for (i = 0; i < pglob.gl_pathc; i++) {
}
}
- /* support recursion here some day... */
-
-#if HAVE_MMAP
mwrite(buf, strlen(buf));
mwrite("\r\n", 2);
-#else
- write(f->local_file, buf, strlen(buf));
- write(f->local_file, "\r\n", 2);
+ }
+
+ /*
+ * If recursion is on, dive into any subdirectories now -- note
+ * that each entry is stat()'ed twice, hopefully the OS will manage,
+ * and we've got our own dcache anyways -- this could be fixed at
+ * the expense of some memory, consider for later inclusion.
+ */
+ if (lo->recursive) {
+ for (i = 0; i < pglob.gl_pathc; i++) {
+ struct stat buf;
+ const char * const temp = pglob.gl_pathv[i];
+
+ /* don't dive into `.' or `..' */
+ if (lstat(temp, &buf) != -1 && S_ISDIR(buf.st_mode) &&
+ (temp[0] != '.' || (temp[1] != '.' && temp[1] != '\0'))) {
+ char tmp2[1024];
+
+ mwrite("\r\n", 2);
+
+ /* attach the pathname to the end of the displayed path */
+ if (disp_pathname[0] == '\0') {
+ snprintf(tmp2, 1024, "%s", temp);
+ } else {
+ snprintf(tmp2, 1024, "%s/%s", disp_pathname, temp);
+ }
+
+ chdir(temp);
+ pos = list_core(c, "*", tmp2, lo,
+#if HAVE_MMAP
+ size, pos);
#endif
+ chdir("..");
+ }
+ }
}
#if HAVE_MMAP
#endif
globfree(&pglob);
+#if HAVE_MMAP
+ return pos;
+#else
+ return 0;
+#endif
}
/*
* cmd_noop(): Handles the NOOP command. Does nothing, doesn't even
* reset the timeout.
*/
-int cmd_noop(struct conn * const c)
+void cmd_noop(struct conn * const c)
{
numeric(c, 200, "NOOP command successful.");
- return 1;
}
/*
* cmd_syst(): Handles the SYST command. Returns the system identification.
*/
-int cmd_syst(struct conn * const c)
+void cmd_syst(struct conn * const c)
{
numeric(c, 215, "UNIX Type: L%u", NBBY);
- return 1;
}
/*
* cmd_type(): Handles the TYPE command.
*/
-int cmd_type(struct conn * const c)
+void cmd_type(struct conn * const c)
{
#if WANT_ASCII
c->recv_buf[0] &= (255-32); /* convert to upper case */
#else
numeric(c, 200, "TYPE ignored (always I)");
#endif
- return 1;
}
/*
- * cmd_mode(): Handles the MODE command. We always use stream mode,
- * so the argument is ignored.
+ * cmd_mode(): Handles the MODE command. Only stream mode is supported.
*/
-int cmd_mode(struct conn * const c)
+void cmd_mode(struct conn * const c)
{
- numeric(c, 200, "MODE ignored (always S)");
- return 1;
+ c->recv_buf[0] &= (255-32); /* convert to upper case */
+ if (c->recv_buf[0] == 'S') {
+ numeric(c, 200, "Mode is STREAM.");
+ } else {
+ numeric(c, 504, "Unknown mode.");
+ }
}
/*
- * cmd_stru(): Handles the STRU command. We always use file mode,
- * so the argument is ignored.
+ * cmd_stru(): Handles the STRU command. Only file mode is supported.
*/
-int cmd_stru(struct conn * const c)
+void cmd_stru(struct conn * const c)
{
- numeric(c, 200, "STRU ignored (always F)");
- return 1;
+ c->recv_buf[0] &= (255-32); /* convert to upper case */
+ if (c->recv_buf[0] == 'F') {
+ numeric(c, 200, "Structure is FILE.");
+ } else {
+ numeric(c, 504, "Unknown structure.");
+ }
}
/*
* like an error, since the error code is intended for helpful
* messages? :-)
*/
-int cmd_help(struct conn * const c)
+void cmd_help(struct conn * const c)
{
numeric(c, 414, "Sorry, no detailed help; use standard FTP commands.");
- return 1;
}
/*
* cmd_quit(): Handles the QUIT command, which shuts down the control
* and data sockets.
*/
-int cmd_quit(struct conn * const c)
+void cmd_quit(struct conn * const c)
{
numeric(c, 221, "Have a nice day!");
- destroy_conn(c);
- return 0;
+ c->free_me = 1;
}
/*
* copied directly from alloc_new_conn() -- perhaps we should
* modularize this?
*/
-int cmd_rein(struct conn * const c)
+void cmd_rein(struct conn * const c)
{
destroy_ftran(c->transfer);
c->buf_len = c->auth = c->rest_pos = 0;
time(&(c->last_transfer));
numeric(c, 220, "BetaFTPD " VERSION " ready.");
-
- return 1;
}
#if DOING_PROFILING
#if !WANT_NONROOT
if (h->do_setuid) {
+ setegid(c->gid);
seteuid(c->uid);
} else {
- seteuid(0);
+ seteuid(getuid());
+ setegid(getgid());
}
#endif
schar = c->recv_buf[cmlen];
c->recv_buf[cmlen] = 0;
-
- /* result of zero means the connection is freed */
- if (h->callback(c)) {
+
+ message_buf[0] = '\0';
+ h->callback(c);
+ if (message_buf[0] != '\0') {
+ /* send any feedback we might have */
+ int err = send(c->sock, message_buf, strlen(message_buf), 0);
+ if (err == -1 && errno == EPIPE) {
+ c->free_me = 1;
+ }
+ }
+
+ if (!c->free_me) {
c->recv_buf[cmlen] = schar;
#if !WANT_NONROOT
- if (h->do_setuid) seteuid(getuid());
+ if (h->do_setuid) {
+ seteuid(getuid());
+ setegid(getgid());
+ }
#endif
remove_bytes(c, cmlen);
}
}
} while ((++h)->callback != NULL);
- numeric(c, 500, "Sorry, no such command.");
remove_bytes(c, cmlen);
+ {
+ char error[] = "500 Sorry, no such command.\r\n";
+ if (send(c->sock, error, strlen(error), 0) == -1 && errno == EPIPE)
+ destroy_conn(c);
+ }
}
/*
* The most common cases are put first, for speed :-)
*/
char decode_mode(mode_t mode) {
- if (mode & S_IFREG) return '-';
- if (mode & S_IFDIR) return 'd';
- if (mode & S_IFLNK) return 'l';
- if (mode & S_IFBLK) return 'b';
- if (mode & S_IFCHR) return 'c';
- if (mode & S_IFSOCK) return 's';
- if (mode & S_IFIFO) return 'f';
+ if (S_ISREG(mode)) return '-';
+ if (S_ISDIR(mode)) return 'd';
+ if (S_ISLNK(mode)) return 'l';
+ if (S_ISBLK(mode)) return 'b';
+ if (S_ISCHR(mode)) return 'c';
+ if (S_ISSOCK(mode)) return 's';
+ if (S_ISFIFO(mode)) return 'f';
return '-';
}
#if WANT_UPLOAD
if ((flags & O_CREAT) == 0) {
#endif
- stat(ptr, &buf);
+ TRAP_ERROR(stat(ptr, &buf) == -1, 550, return -2);
if (!S_ISREG(buf.st_mode)) {
- numeric(c, 550, "%s: Not a plain file.", ptr);
+ numeric(c, 550, "Not a plain file.", ptr);
return -2;
}
#if WANT_UPLOAD
if (optr != NULL) {
while (*++optr) {
switch (*optr & (255-32)) { /* uppercase */
-#if 0
case 'R': /* actually case sensitive... */
lo->recursive = 1;
break;
-#endif
case 'L':
lo->long_listing = 1;
break;
case 'F':
lo->classify = 1;
break;
- case ' ':
+ case '\0':
fptr = optr + 1;
*(optr--) = 0;
break;
} else {
fptr = c->recv_buf;
}
-
+
/* then we chdir to the dir in fptr (if any) */
tmp = fptr ? strrchr(fptr, '/') : NULL;
if (tmp != NULL) {
}
/* if no argument, choose all files */
- if (fptr == NULL || fptr[0] == 0) fptr = "*";
+ if (fptr == NULL || fptr[0] == 0) {
+ fptr = "*";
+ } else {
+ /* we need to check if the last part is a directory (no -d switch) */
+ struct stat buf;
+ if (stat(fptr, &buf) == 0 && S_ISDIR(buf.st_mode)) {
+ TRAP_ERROR(chdir(fptr) == -1, 550, return -1);
+ fptr = "*";
+ }
+ }
*ptr = fptr;
#if WANT_NONROOT