Commit Diff


commit - 2339a71178cc4a29dd2eada458c84b6092b056ce
commit + e2003e7e305adabd1ee575e401a55e6d7e050297
blob - 8cad48585a7dd8e2efc2a9660b19aac44fa6ba21
blob + 885a7a7d669e99bb45a7818e2104d0e7beaff221
--- gmid.h
+++ gmid.h
@@ -323,12 +323,6 @@ struct connreq {
 	char	host[NI_MAXHOST];
 	char	port[NI_MAXSERV];
 	int	flag;
-};
-
-enum {
-	FILE_EXISTS,
-	FILE_DIRECTORY,
-	FILE_MISSING,
 };
 
 enum imsg_type {
blob - 9ab4f134aab3e73b0d4da0645b4ec987012a51d8
blob + ab93b1657221a6aed50e3863bbf7143693d3aa6d
--- server.c
+++ server.c
@@ -52,8 +52,6 @@ void tls_config_use_fake_private_key(struct tls_config
 
 static inline int matches(const char*, const char*);
 
-static int	 check_path(struct client*, const char*, int*);
-static void	 open_file(struct client*);
 static void	 handle_handshake(int, short, void*);
 static const char *strip_path(const char*, int);
 static void	 fmt_sbuf(const char*, struct client*, const char*);
@@ -343,75 +341,8 @@ vhost_disable_log(struct vhost *v, const char *path)
 
 	loc = TAILQ_FIRST(&v->locations);
 	return loc->disable_log;
-}
-
-static int
-check_path(struct client *c, const char *path, int *fd)
-{
-	struct stat sb;
-	const char *p;
-	int dirfd, strip;
-
-	assert(path != NULL);
-
-	/*
-	 * in send_dir we add an initial / (to be redirect-friendly),
-	 * but here we want to skip it
-	 */
-	if (*path == '/')
-		path++;
-
-	strip = vhost_strip(c->host, path);
-	p = strip_path(path, strip);
-
-	if (*p == '/')
-		p = p+1;
-	if (*p == '\0')
-		p = ".";
-
-	dirfd = vhost_dirfd(c->host, path, &c->loc);
-	log_debug("check_path: strip=%d path=%s original=%s",
-	    strip, p, path);
-	if (*fd == -1 && (*fd = openat(dirfd, p, O_RDONLY)) == -1) {
-		if (errno == EACCES)
-			log_info("can't open %s: %s", p, strerror(errno));
-		return FILE_MISSING;
-	}
-
-	if (fstat(*fd, &sb) == -1) {
-		log_warn("fstat %s", path);
-		return FILE_MISSING;
-	}
-
-	if (S_ISDIR(sb.st_mode))
-		return FILE_DIRECTORY;
-
-	return FILE_EXISTS;
 }
 
-static void
-open_file(struct client *c)
-{
-	switch (check_path(c, c->iri.path, &c->pfd)) {
-	case FILE_EXISTS:
-		c->type = REQUEST_FILE;
-		start_reply(c, SUCCESS, mime(c->conf, c->host, c->iri.path));
-		return;
-
-	case FILE_DIRECTORY:
-		open_dir(c);
-		return;
-
-	case FILE_MISSING:
-		start_reply(c, NOT_FOUND, "not found");
-		return;
-
-	default:
-		/* unreachable */
-		abort();
-	}
-}
-
 void
 mark_nonblock(int fd)
 {
@@ -827,81 +758,75 @@ apply_require_ca(struct client *c)
 }
 
 static void
-open_dir(struct client *c)
+server_dir_listing(struct client *c)
 {
-	size_t len;
-	int dirfd, root;
-	char *before_file;
+	int root;
 
 	root = !strcmp(c->iri.path, "/") || *c->iri.path == '\0';
+
+	if (!vhost_auto_index(c->host, c->iri.path)) {
+		start_reply(c, NOT_FOUND, "not found");
+		return;
+	}
 
+	c->dirlen = scandir_fd(c->pfd, &c->dir,
+	    root ? select_non_dotdot : select_non_dot,
+	    alphasort);
+	if (c->dirlen == -1) {
+		log_warn("scandir_fd(%d) (vhost:%s) %s",
+		    c->pfd, c->host->domain, c->iri.path);
+		start_reply(c, TEMP_FAILURE, "internal server error");
+		return;
+	}
+
+	c->type = REQUEST_DIR;
+	start_reply(c, SUCCESS, "text/gemini");
+	evbuffer_add_printf(EVBUFFER_OUTPUT(c->bev),
+	    "# Index of /%s\n\n", c->iri.path);
+}
+
+static void
+open_dir(struct client *c)
+{
+	struct stat sb;
+	const char *index;
+	char path[PATH_MAX];
+	size_t len;
+	int fd = -1;
+
 	len = strlen(c->iri.path);
 	if (len > 0 && !ends_with(c->iri.path, "/")) {
 		redirect_canonical_dir(c);
 		return;
 	}
 
-	strlcpy(c->sbuf, "/", sizeof(c->sbuf));
-	strlcat(c->sbuf, c->iri.path, sizeof(c->sbuf));
-	if (!ends_with(c->sbuf, "/"))
-		strlcat(c->sbuf, "/", sizeof(c->sbuf));
-	before_file = strchr(c->sbuf, '\0');
-	len = strlcat(c->sbuf, vhost_index(c->host, c->iri.path),
-	    sizeof(c->sbuf));
-	if (len >= sizeof(c->sbuf)) {
-		start_reply(c, TEMP_FAILURE, "internal server error");
+	index = vhost_index(c->host, c->iri.path);
+	fd = openat(c->pfd, index, O_RDONLY);
+	if (fd == -1) {
+		server_dir_listing(c);
 		return;
 	}
 
-	c->iri.path = c->sbuf;
-
-	/* close later unless we have to generate the dir listing */
-	dirfd = c->pfd;
-	c->pfd = -1;
-
-	switch (check_path(c, c->iri.path, &c->pfd)) {
-	case FILE_EXISTS:
-		c->type = REQUEST_FILE;
-		start_reply(c, SUCCESS, mime(c->conf, c->host, c->iri.path));
-		break;
-
-	case FILE_DIRECTORY:
-		start_reply(c, TEMP_REDIRECT, c->sbuf);
-		break;
-
-	case FILE_MISSING:
-		*before_file = '\0';
-
-		if (!vhost_auto_index(c->host, c->iri.path)) {
-			start_reply(c, NOT_FOUND, "not found");
-			break;
-		}
-
-		c->type = REQUEST_DIR;
-
-		c->dirlen = scandir_fd(dirfd, &c->dir,
-		    root ? select_non_dotdot : select_non_dot,
-		    alphasort);
-		if (c->dirlen == -1) {
-			log_warn("scandir_fd(%d) (vhost:%s) %s",
-			    c->pfd, c->host->domain, c->iri.path);
-			start_reply(c, TEMP_FAILURE, "internal server error");
-			return;
-		}
-		c->diroff = 0;
-		c->off = 0;
-
-		start_reply(c, SUCCESS, "text/gemini");
-		evbuffer_add_printf(EVBUFFER_OUTPUT(c->bev),
-		    "# Index of %s\n\n", c->iri.path);
+	if (fstat(fd, &sb) == -1) {
+		log_warn("fstat");
+		close(fd);
+		start_reply(c, TEMP_FAILURE, "internal server error");
 		return;
+	}
 
-	default:
-		/* unreachable */
-		abort();
+	if (!S_ISREG(sb.st_mode)) {
+		close(fd);
+		server_dir_listing(c);
+		return;
 	}
 
-	close(dirfd);
+	strlcpy(path, c->iri.path, sizeof(path));
+	strlcat(path, index, sizeof(path));
+
+	close(c->pfd);
+	c->pfd = fd;
+	c->type = REQUEST_FILE;
+	start_reply(c, SUCCESS, mime(c->conf, c->host, path));
 }
 
 static void
@@ -1029,9 +954,10 @@ err:
 static void
 client_read(struct bufferevent *bev, void *d)
 {
+	struct stat	 sb;
 	struct client	*c = d;
 	struct evbuffer	*src = EVBUFFER_INPUT(bev);
-	const char	*parse_err = "invalid request";
+	const char	*path, *p, *parse_err = "invalid request";
 	char		 decoded[DOMAIN_NAME_LEN];
 
 	bufferevent_disable(bev, EVBUFFER_READ);
@@ -1085,7 +1011,34 @@ client_read(struct bufferevent *bev, void *d)
 	    apply_fastcgi(c))
 		return;
 
-	open_file(c);
+	path = c->iri.path;
+	p = strip_path(path, vhost_strip(c->host, path));
+	while (*p == '/')
+		p++;
+	if (*p == '\0')
+		p = ".";
+
+	c->pfd = openat(vhost_dirfd(c->host, path, &c->loc), p, O_RDONLY);
+	if (c->pfd == -1) {
+		if (errno == EACCES)
+			log_info("can't open %s: %s", p, strerror(errno));
+		start_reply(c, NOT_FOUND, "not found");
+		return;
+	}
+
+	if (fstat(c->pfd, &sb) == -1) {
+		log_warnx("fstat %s", path);
+		start_reply(c, TEMP_FAILURE, "internal server error");
+		return;
+	}
+
+	if (S_ISDIR(sb.st_mode)) {
+		open_dir(c);
+		return;
+	}
+
+	c->type = REQUEST_FILE;
+	start_reply(c, SUCCESS, mime(c->conf, c->host, p));
 }
 
 void