Added a htls program for directory listings.
authorFredrik Tolf <fredrik@dolda2000.com>
Sun, 29 Aug 2010 21:37:23 +0000 (23:37 +0200)
committerFredrik Tolf <fredrik@dolda2000.com>
Sun, 29 Aug 2010 21:37:23 +0000 (23:37 +0200)
lib/resp.h
src/.gitignore
src/Makefile.am
src/htls.c [new file with mode: 0644]

index 00f1872..4578b03 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <req.h>
 
+char *htmlquote(char *text);
 void simpleerror(int fd, int code, char *msg, char *fmt, ...);
 void stdredir(struct hthead *req, int fd, int code, char *dst);
 char *fmthttpdate(time_t time);
index 4179187..98d71b8 100644 (file)
@@ -5,3 +5,4 @@
 /callcgi
 /patplex
 /userplex
+/htls
index fd9c71d..32e50fb 100644 (file)
@@ -1,4 +1,4 @@
-bin_PROGRAMS = htparser dirplex sendfile callcgi patplex userplex
+bin_PROGRAMS = htparser dirplex sendfile callcgi patplex userplex htls
 noinst_PROGRAMS = debugsink
 
 htparser_SOURCES = htparser.c htparser.h plaintcp.c
diff --git a/src/htls.c b/src/htls.c
new file mode 100644 (file)
index 0000000..e16004d
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+    ashd - A Sane HTTP Daemon
+    Copyright (C) 2008  Fredrik Tolf <fredrik@dolda2000.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <stdint.h>
+#include <locale.h>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <utils.h>
+#include <resp.h>
+#include <log.h>
+#include <resp.h>
+
+struct dentry {
+    char *name;
+    struct stat sb;
+    int w, x;
+};
+
+static int dispmtime = 0;
+static int dispsize = 0;
+
+static void checkcache(struct stat *sb)
+{
+    char *hdr;
+    
+    if((hdr = getenv("REQ_IF_MODIFIED_SINCE")) != NULL) {
+       if(parsehttpdate(hdr) < sb->st_mtime)
+           return;
+       printf("HTTP/1.1 304 Not Modified\n");
+       printf("Date: %s\n", fmthttpdate(time(NULL)));
+       printf("Content-Length: 0\n");
+       printf("\n");
+       exit(0);
+    }
+}
+
+static int dcmp(const void *ap, const void *bp)
+{
+    const struct dentry *a = ap, *b = bp;
+    
+    if(S_ISDIR(a->sb.st_mode) && !S_ISDIR(b->sb.st_mode))
+       return(-1);
+    if(!S_ISDIR(a->sb.st_mode) && S_ISDIR(b->sb.st_mode))
+       return(1);
+    return(strcoll(a->name, b->name));
+}
+
+static void head(char *name, struct charbuf *dst)
+{
+    char *title;
+    
+    bprintf(dst, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+    bprintf(dst, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n");
+    bprintf(dst, "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n");
+    bprintf(dst, "<head>\n");
+    title = htmlquote(name);
+    bprintf(dst, "<title>Index of %s</title>\n", title);
+    bprintf(dst, "</head>\n");
+    bprintf(dst, "<body>\n");
+    bprintf(dst, "<h1>Index of %s</h1>\n", title);
+    free(title);
+}
+
+static void foot(struct charbuf *dst)
+{
+    bprintf(dst, "</body>\n");
+    bprintf(dst, "</html>\n");
+}
+
+static void mkindex(char *name, DIR *dir, struct charbuf *dst)
+{
+    struct {
+       struct dentry *b;
+       size_t s, d;
+    } dirbuf;
+    struct dentry f;
+    struct dirent *dent;
+    char *fn;
+    int i;
+    
+    bufinit(dirbuf);
+    while((dent = readdir(dir)) != NULL) {
+       if(*dent->d_name == '.')
+           continue;
+       memset(&f, 0, sizeof(f));
+       f.name = sstrdup(dent->d_name);
+       fn = sprintf3("%s/%s", name, dent->d_name);
+       if(access(fn, R_OK))
+           continue;
+       if(stat(fn, &f.sb))
+           continue;
+       if(!access(fn, W_OK))
+           f.w = 1;
+       if(!access(fn, X_OK))
+           f.x = 1;
+       else if(S_ISDIR(f.sb.st_mode))
+           continue;
+       bufadd(dirbuf, f);
+    }
+    qsort(dirbuf.b, dirbuf.d, sizeof(struct dentry), dcmp);
+    bprintf(dst, "<table class=\"dirindex\">\n");
+    for(i = 0; i < dirbuf.d; i++) {
+       bprintf(dst, "<tr class=\"dentry");
+       if(S_ISDIR(dirbuf.b[i].sb.st_mode))
+           bprintf(dst, " dir");
+       if(dirbuf.b[i].w)
+           bprintf(dst, " writable");
+       if(dirbuf.b[i].x)
+           bprintf(dst, " exec");
+       bprintf(dst, "\">");    
+       fn = htmlquote(dirbuf.b[i].name);
+       bprintf(dst, "<td class=\"filename\"><a href=\"%s\">%s</a></td>", fn, fn);
+       free(fn);
+       if(dispsize && !S_ISDIR(dirbuf.b[i].sb.st_mode))
+           bprintf(dst, "<td class=\"filesize\">%ji</td>", (intmax_t)dirbuf.b[i].sb.st_size);
+       if(dispmtime)
+           bprintf(dst, "<td class=\"filemtime\">%s</td>", fmthttpdate(dirbuf.b[i].sb.st_mtime));
+       bprintf(dst, "</tr>\n");
+       free(dirbuf.b[i].name);
+    }
+    bprintf(dst, "</table>\n");
+}
+
+static void usage(void)
+{
+    flog(LOG_ERR, "usage: htls [-hms] METHOD URL REST");
+}
+
+int main(int argc, char **argv)
+{
+    int c;
+    char *dname;
+    DIR *dir;
+    struct charbuf buf;
+    struct stat sb;
+    
+    setlocale(LC_ALL, "");
+    while((c = getopt(argc, argv, "hms")) >= 0) {
+       switch(c) {
+       case 'h':
+           usage();
+           exit(0);
+       case 'm':
+           dispmtime = 1;
+           break;
+       case 's':
+           dispsize = 1;
+           break;
+       default:
+           usage();
+           exit(1);
+       }
+    }
+    if(argc - optind < 3) {
+       usage();
+       exit(1);
+    }
+    if((dname = getenv("REQ_X_ASH_FILE")) == NULL) {
+       flog(LOG_ERR, "htls: needs to be called with the X-Ash-File header");
+       exit(1);
+    }
+    if(*argv[optind + 2]) {
+       simpleerror(1, 404, "Not Found", "The requested URL has no corresponding resource.");
+       exit(0);
+    }
+
+    if(stat(dname, &sb) || ((dir = opendir(dname)) == NULL)) {
+       flog(LOG_ERR, "htls: could not open directory `%s': %s", dname, strerror(errno));
+       simpleerror(1, 500, "Server Error", "Could not produce directory index.");
+       exit(1);
+    }
+    checkcache(&sb);
+    
+    bufinit(buf);
+    head(argv[optind + 1], &buf);
+    mkindex(dname, dir, &buf);
+    foot(&buf);
+    closedir(dir);
+    
+    printf("HTTP/1.1 200 OK\n");
+    printf("Content-Type: text/html; charset=UTF-8\n");
+    printf("Last-Modified: %s\n", fmthttpdate(sb.st_mtime));
+    printf("Content-Length: %zi\n", buf.d);
+    printf("\n");
+    fwrite(buf.b, 1, buf.d, stdout);
+    buffree(buf);
+    return(0);
+}