Commit Diff


commit - 3c680bddab146e8c8ccf5eba1fec7e1ee978d638
commit + 5f715ce43fa60b2347d6dc64588c5ffe61ebc067
blob - a8f6f3d330e65c19d715ddf21218470499fb4e23
blob + c8149d526f57bcc39d29238aa9193c1734aafb79
--- ChangeLog
+++ ChangeLog
@@ -1,3 +1,7 @@
+2021-02-02  Omar Polo  <op@omarpolo.com>
+
+	* server.c (handle_dirlist_head): print the header in the directory listing
+
 2021-02-01  Omar Polo  <op@omarpolo.com>
 
 	* parse.y (servopt): require absolute paths in config file
blob - 1ba4a9e90b1b681d79146270694f5d845b642855
blob + dba93c368806aeb65c3a42e175d87796950ae333
--- gmid.h
+++ gmid.h
@@ -131,12 +131,13 @@ typedef void (*statefn)(struct pollfd*, struct client*
 
 /*
  * DFA: handle_handshake is the initial state, close_conn the final.
+ * Sometimes we have an enter_* function to handle the state switch.
  *
  * handle_handshake -> handle_open_conn
  * handle_handshake -> close_conn		// on err
  *
  * handle_open_conn -> handle_cgi_reply		// via open_file/dir/...
- * handle_open_conn -> send_directory_listing	// ...same
+ * handle_open_conn -> handle_dirlist		// ...same
  * handle_open_conn -> send_file		// ...same
  * handle_open_conn -> start_reply		// on error
  *
@@ -144,6 +145,9 @@ typedef void (*statefn)(struct pollfd*, struct client*
  * handle_cgi_reply -> start_reply	// on error
  *
  * handle_cgi -> close_conn
+ *
+ * handle_dirlist -> send_directory_listing
+ * handle_dirlist -> close_conn			// on error
  *
  * send_directory_listing -> close_conn
  *
@@ -229,6 +233,8 @@ void		 start_cgi(const char*, const char*, struct poll
 void		 send_file(struct pollfd*, struct client*);
 void		 open_dir(struct pollfd*, struct client*);
 void		 redirect_canonical_dir(struct pollfd*, struct client*);
+void		 enter_handle_dirlist(struct pollfd*, struct client*);
+void		 handle_dirlist(struct pollfd*, struct client*);
 int		 read_next_dir_entry(struct client*);
 void		 send_directory_listing(struct pollfd*, struct client*);
 void		 cgi_poll_on_child(struct pollfd*, struct client*);
blob - 5ffd83fbb081331521f87cd015054fe9521eb30d
blob + 6de3e30c26b75b35788d4f649a6a93d7b63ac3ea
--- regress/runtime
+++ regress/runtime
@@ -215,7 +215,7 @@ echo OK GET / with auto index
 
 eq "$(head /dir)"		"30 /dir/"		"Unexpected head for /dir"
 eq "$(head /dir/)"		"20 text/gemini"	"Unexpected head for /dir/"
-eq "$(get /dir/|wc -l|xargs)"	"3"			"Unexpected body for /dir/"
+eq "$(get /dir/|wc -l|xargs)"	"5"			"Unexpected body for /dir/"
 echo OK GET /dir/ with auto index on
 
 check "should be running"
blob - 9292e0c3b60a1ba10fc9c0ef463d9c3cb4c955fc
blob + d1b3a1079a053524226aee0e0a8b96638bcb13bf
--- server.c
+++ server.c
@@ -543,7 +543,7 @@ open_dir(struct pollfd *fds, struct client *c)
 		}
 
 		c->fd = dirfd;
-		c->next = send_directory_listing;
+		c->next = enter_handle_dirlist;
 
 		if ((c->dir = fdopendir(c->fd)) == NULL) {
 			LOGE(c, "can't fdopendir(%d) (vhost:%s) %s: %s",
@@ -580,7 +580,56 @@ redirect_canonical_dir(struct pollfd *fds, struct clie
 
 	start_reply(fds, c, TEMP_REDIRECT, c->sbuf);
 }
+
+void
+enter_handle_dirlist(struct pollfd *fds, struct client *c)
+{
+	char b[PATH_MAX];
+	size_t l;
 
+	strlcpy(b, c->iri.path, sizeof(b));
+	l = snprintf(c->sbuf, sizeof(c->sbuf),
+	    "# Index of %s\n\n", b);
+	if (l >= sizeof(c->sbuf)) {
+		/* this is impossible, given that we have enough space
+		 * in c->sbuf to hold the ancilliary string plus the
+		 * full path; but it wouldn't read nice without some
+		 * error checking, and I'd like to avoid a strlen. */
+		close_conn(fds, c);
+		return;
+	}
+	c->len = l;
+
+	c->state = handle_dirlist;
+	handle_dirlist(fds, c);
+}
+
+void
+handle_dirlist(struct pollfd *fds, struct client *c)
+{
+	ssize_t r;
+
+	while (c->len > 0) {
+		switch (r = tls_write(c->ctx, c->sbuf + c->off, c->len)) {
+		case -1:
+			close_conn(fds, c);
+			return;
+		case TLS_WANT_POLLOUT:
+			fds->events = POLLOUT;
+			return;
+		case TLS_WANT_POLLIN:
+			fds->events = POLLIN;
+			return;
+		default:
+			c->off += r;
+			c->len -= r;
+		}
+	}
+
+	c->state = send_directory_listing;
+	send_directory_listing(fds, c);
+}
+
 int
 read_next_dir_entry(struct client *c)
 {