Commit Diff


commit - f12795235b9f8bfe4bdde737426259ce46356932
commit + 65124267f8546533ef276a4658f63d856332aa3e
blob - 4b82127462bb14619c937b3ac27b16c5fc1aea2e
blob + ccb515253662f4140e300944221476f898be23e9
--- fs.c
+++ fs.c
@@ -23,7 +23,9 @@
 #include "compat.h"
 
 #include <sys/stat.h>
+#include <sys/types.h>
 
+#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
@@ -172,8 +174,8 @@ send_hdr(uint32_t peerid, int code, const char *meta)
 	fs_send_ui(IMSG_GOT_META, peerid, -1, meta, strlen(meta)+1);
 }
 
-static void
-handle_get_file(struct imsg *imsg, size_t datalen)
+static inline const char *
+file_type(const char *path)
 {
 	struct mapping {
 		const char	*ext;
@@ -187,41 +189,83 @@ handle_get_file(struct imsg *imsg, size_t datalen)
 		{"patch",	"text/x-patch"},
 		{"txt",		"text/plain"},
 		{NULL, NULL},
-	},		*m;
+	}, *m;
+	char *dot;
+
+	if ((dot = strrchr(path, '.')) == NULL)
+		return NULL;
+
+	dot++;
+
+	for (m = ms; m->ext != NULL; ++m)
+		if (!strcmp(m->ext, dot))
+			return m->mime;
+
+	return NULL;
+}
+
+static inline void
+send_dir(uint32_t peerid, const char *path)
+{
+	struct dirent	**names;
+	struct evbuffer	 *ev;
+	int		  i, len;
+
+	if ((ev = evbuffer_new()) == NULL ||
+	    (len = scandir(path, &names, NULL, alphasort)) == -1) {
+		evbuffer_free(ev);
+		send_hdr(peerid, 40, "failure reading the directory");
+		return;
+	}
+
+	evbuffer_add_printf(ev, "# Index of %s\n\n", path);
+	for (i = 0; i < len; ++i) {
+		evbuffer_add_printf(ev, "=> %s", names[i]->d_name);
+		if (names[i]->d_type == DT_DIR)
+			evbuffer_add(ev, "/", 1);
+		evbuffer_add(ev, "\n", 1);
+	}
+
+	send_hdr(peerid, 20, "text/gemini");
+	fs_send_ui(IMSG_BUF, peerid, -1,
+	    EVBUFFER_DATA(ev), EVBUFFER_LENGTH(ev));
+	fs_send_ui(IMSG_EOF, peerid, -1, NULL, 0);
+
+	evbuffer_free(ev);
+	free(names);
+}
+
+static void
+handle_get_file(struct imsg *imsg, size_t datalen)
+{
+	struct stat	 sb;
 	FILE		*f;
-	char		*data, *dot;
+	char		*data;
 	const char	*meta = NULL;
-	int		 code;
 
 	data = imsg->data;
 	data[datalen-1] = '\0';
 
-	if ((dot = strrchr(data, '.')) == NULL) {
-		send_hdr(imsg->hdr.peerid, 51,
-		    "don't know how to visualize this file");
+	if ((f = fopen(data, "r")) == NULL) {
+		send_hdr(imsg->hdr.peerid, 51, "can't open the file");
 		return;
 	}
-	dot++;
 
-	for (m = ms; m->ext != NULL; ++m) {
-		if (!strcmp(m->ext, dot)) {
-			meta = m->mime;
-			break;
-		}
+	if (fstat(fileno(f), &sb) == -1) {
+		send_hdr(imsg->hdr.peerid, 40, "fstat failed");
+		return;
 	}
 
-	if (meta == NULL) {
-		send_hdr(imsg->hdr.peerid, 51,
-		    "don't know how to visualize this file");
+	if (S_ISDIR(sb.st_mode)) {
+		fclose(f);
+		send_dir(imsg->hdr.peerid, data);
 		return;
 	}
 
-	if ((f = fopen(data, "r")) == NULL) {
-		/*
-		 * If errno is EDIR it would be nice to serve the
-		 * directory listing.  See how it's done in gmid.
-		 */
-		send_hdr(imsg->hdr.peerid, 51, "can't open the file");
+	if ((meta = file_type(data)) == NULL) {
+		fclose(f);
+		send_hdr(imsg->hdr.peerid, 51,
+		    "don't know how to visualize this file");
 		return;
 	}