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;
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 */
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;
#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
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 1);
numeric(c, 350, "File exists, send RNTO.");
return 1;
return 1;
}
- 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 1);
+ c->rename_from[0] = '\0';
- numeric(c, 250, "File renamed successfulyy.");
+ numeric(c, 250, "File renamed successfully.");
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] = {
/*
* 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():
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
{
struct list_options lo;
-/* lo.recursive = 0; */
+ lo.recursive = 0;
lo.long_listing = 1;
lo.classify = 0;
{
struct list_options lo;
-/* lo.recursive = 0; */
+ lo.recursive = 0;
lo.long_listing = 0;
lo.classify = 0;
#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_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)
{
- numeric(c, 200, "MODE ignored (always S)");
+ 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.");
+ }
return 1;
}
/*
- * 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)
{
- numeric(c, 200, "STRU ignored (always F)");
+ 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.");
+ }
return 1;
}
* down without clearing any sockets etc. In other words:
* Don't use it on a production site.
*/
-void cmd_exit(struct conn * const c)
+int cmd_exit(struct conn * const c)
{
while (first_conn->next_conn)
destroy_conn(first_conn->next_conn);
#if !WANT_NONROOT
if (h->do_setuid) {
seteuid(c->uid);
+ setegid(c->gid);
} else {
- seteuid(0);
+ seteuid(getuid());
+ setegid(getgid());
}
#endif
if (h->callback(c)) {
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);
}
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;
}
/* 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