#include <ctype.h>
#include <dirent.h>
#include <fnmatch.h>
+#include <time.h>
#ifdef HAVE_CONFIG_H
#include <config.h>
#include <req.h>
#include <proc.h>
#include <resp.h>
-
-#define CH_SOCKET 0
-#define CH_FORK 1
+#include <cf.h>
#define PAT_BASENAME 0
#define PAT_PATHNAME 1
#define PAT_ALL 2
#define PAT_DEFAULT 3
+#define PT_FILE 0
+#define PT_DIR 1
+
struct config {
struct config *next, *prev;
char *path;
- time_t mtime;
+ time_t mtime, lastck;
struct child *children;
struct pattern *patterns;
-};
-
-struct child {
- struct child *next;
- char *name;
- int type;
- char **argv;
- int fd;
+ char **index;
};
struct rule {
int type;
- char *pattern;
+ char **patterns;
};
struct pattern {
struct pattern *next;
+ int type;
char *childnm;
+ char **fchild;
struct rule **rules;
};
-struct config *cflist;
+static struct config *cflist;
+static struct config *gconfig, *lconfig;
+static time_t now;
-static void freechild(struct child *ch)
+static void freerule(struct rule *rule)
{
- if(ch->fd != -1)
- close(ch->fd);
- if(ch->name != NULL)
- free(ch->name);
- if(ch->argv != NULL)
- freeca(ch->argv);
- free(ch);
+ freeca(rule->patterns);
+ free(rule);
}
static void freepattern(struct pattern *pat)
{
struct rule **rule;
- for(rule = pat->rules; *rule; rule++) {
- if((*rule)->pattern != NULL)
- free((*rule)->pattern);
- free(*rule);
- }
+ for(rule = pat->rules; *rule; rule++)
+ freerule(*rule);
if(pat->childnm != NULL)
free(pat->childnm);
+ freeca(pat->fchild);
free(pat);
}
cf->next->prev = cf->prev;
if(cf == cflist)
cflist = cf->next;
- free(cf->path);
+ if(cf->path != NULL)
+ free(cf->path);
for(ch = cf->children; ch != NULL; ch = nch) {
nch = ch->next;
freechild(ch);
npat = pat->next;
freepattern(pat);
}
+ freeca(cf->index);
free(cf);
}
-static struct child *newchild(char *name, int type)
-{
- struct child *ch;
-
- omalloc(ch);
- ch->name = sstrdup(name);
- ch->type = type;
- ch->fd = -1;
- return(ch);
-}
-
static struct child *getchild(struct config *cf, char *name)
{
struct child *ch;
for(i = 0; pat->rules[i]; i++);
pat->rules = srealloc(pat->rules, sizeof(*pat->rules) * (i + 2));
- rule = pat->rules[i] = smalloc(sizeof(*rule));
+ rule = pat->rules[i] = szmalloc(sizeof(*rule));
pat->rules[i + 1] = NULL;
return(rule);
}
return(pat);
}
-static struct config *readconfig(char *path)
+static char **cadup(char **w)
+{
+ char **ret;
+ int i, l;
+
+ l = calen(w);
+ ret = smalloc(sizeof(*ret) * (l + 1));
+ for(i = 0; i < l; i++)
+ ret[i] = sstrdup(w[i]);
+ ret[i] = NULL;
+ return(ret);
+}
+
+static struct pattern *parsepattern(struct cfstate *s)
{
- int i;
- struct config *cf;
- FILE *s;
- char line[1024];
- char *p, **w;
- int ind, eof;
- int lno;
- int state;
- int rv;
- int argc;
- struct child *child;
struct pattern *pat;
struct rule *rule;
- struct stat sb;
-
- if(stat(path, &sb))
- return(NULL);
- if((s = fopen(sprintf3("%s/.htrc", path), "r")) == NULL)
+ int sl;
+
+ if(!strcmp(s->argv[0], "match")) {
+ s->expstart = 1;
+ pat = newpattern();
+ } else {
return(NULL);
- omalloc(cf);
- cf->mtime = sb.st_mtime;
- cf->path = sstrdup(path);
- eof = 0;
- state = 0;
- w = NULL;
- lno = 0;
- do {
- if(fgets(line, sizeof(line), s) == NULL) {
- eof = 1;
- line[0] = 0;
- }
- lno++;
- for(p = line; *p; p++) {
- if(*p == '#')
+ }
+
+ if((s->argc > 1) && !strcmp(s->argv[1], "directory"))
+ pat->type = PT_DIR;
+ sl = s->lno;
+ while(1) {
+ getcfline(s);
+ if(!strcmp(s->argv[0], "filename")) {
+ if(s->argc < 2) {
+ flog(LOG_WARNING, "%s:%i: missing pattern for `filename' match", s->file, s->lno);
continue;
- if(!isspace(*p))
- break;
- }
- ind = isspace(line[0]);
- w = tokenize(line);
- argc = calen(w);
-
- retry:
- if(state == 0) {
- if(ind) {
- flog(LOG_WARNING, "%s%i: unexpected line indentation in global scope", path, lno);
- goto next;
- } else {
- if(!w[0]) {
- } else if(!strcmp(w[0], "child")) {
- if(argc < 2) {
- flog(LOG_WARNING, "%s:%i: missing name in child declaration", path, lno);
- goto next;
- }
- child = newchild(w[1], CH_SOCKET);
- state = 1;
- } else if(!strcmp(w[0], "fchild")) {
- if(argc < 2) {
- flog(LOG_WARNING, "%s:%i: missing name in child declaration", path, lno);
- goto next;
- }
- child = newchild(w[1], CH_FORK);
- state = 1;
- } else if(!strcmp(w[0], "match")) {
- pat = newpattern();
- state = 2;
- } else {
- flog(LOG_WARNING, "%s:%i: unknown directive %s", path, lno, w[0]);
- }
}
- } else if(state == 1) {
- if(ind) {
- if(!w[0]) {
- } else if(!strcmp(w[0], "exec")) {
- if(argc < 2) {
- flog(LOG_WARNING, "%s:%i: too few parameters to `exec'", path, lno);
- goto next;
- }
- child->argv = szmalloc(sizeof(*child->argv) * argc);
- for(i = 0; i < argc - 1; i++)
- child->argv[i] = sstrdup(w[i + 1]);
- } else {
- flog(LOG_WARNING, "%s:%i: unknown directive %s", path, lno, w[0]);
- }
- } else {
- state = 0;
- if(child->argv == NULL) {
- flog(LOG_WARNING, "%s:%i: missing `exec' in child declaration %s", path, lno, child->name);
- freechild(child);
- goto retry;
- }
- child->next = cf->children;
- cf->children = child;
- goto retry;
+ rule = newrule(pat);
+ rule->type = PAT_BASENAME;
+ rule->patterns = cadup(s->argv + 1);
+ } else if(!strcmp(s->argv[0], "pathname")) {
+ if(s->argc < 2) {
+ flog(LOG_WARNING, "%s:%i: missing pattern for `pathname' match", s->file, s->lno);
+ continue;
}
- } else if(state == 2) {
- if(ind) {
- if(!w[0]) {
- } else if(!strcmp(w[0], "filename")) {
- if(argc < 2) {
- flog(LOG_WARNING, "%s:%i: missing pattern for `filename' match", path, lno);
- goto next;
- }
- rule = newrule(pat);
- rule->type = PAT_BASENAME;
- rule->pattern = sstrdup(w[1]);
- } else if(!strcmp(w[0], "pathname")) {
- if(argc < 2) {
- flog(LOG_WARNING, "%s:%i: missing pattern for `pathname' match", path, lno);
- goto next;
- }
- rule = newrule(pat);
- rule->type = PAT_PATHNAME;
- rule->pattern = sstrdup(w[1]);
- } else if(!strcmp(w[0], "all")) {
- newrule(pat)->type = PAT_ALL;
- } else if(!strcmp(w[0], "default")) {
- newrule(pat)->type = PAT_DEFAULT;
- } else if(!strcmp(w[0], "handler")) {
- if(argc < 2) {
- flog(LOG_WARNING, "%s:%i: missing child name for `handler' directive", path, lno);
- goto next;
- }
- if(pat->childnm != NULL)
- free(pat->childnm);
- pat->childnm = sstrdup(w[1]);
- } else {
- flog(LOG_WARNING, "%s:%i: unknown directive %s", path, lno, w[0]);
- }
- } else {
- state = 0;
- if(pat->rules[0] == NULL) {
- flog(LOG_WARNING, "%s:%i: missing rules in match declaration", path, lno);
- freepattern(pat);
- goto retry;
- }
- if(pat->childnm == NULL) {
- flog(LOG_WARNING, "%s:%i: missing handler in match declaration", path, lno);
- freepattern(pat);
- goto retry;
- }
- pat->next = cf->patterns;
- cf->patterns = pat;
- goto retry;
+ rule = newrule(pat);
+ rule->type = PAT_PATHNAME;
+ rule->patterns = cadup(s->argv + 1);
+ } else if(!strcmp(s->argv[0], "all")) {
+ newrule(pat)->type = PAT_ALL;
+ } else if(!strcmp(s->argv[0], "default")) {
+ newrule(pat)->type = PAT_DEFAULT;
+ } else if(!strcmp(s->argv[0], "handler")) {
+ if(s->argc < 2) {
+ flog(LOG_WARNING, "%s:%i: missing child name for `handler' directive", s->file, s->lno);
+ continue;
}
+ if(pat->childnm != NULL)
+ free(pat->childnm);
+ pat->childnm = sstrdup(s->argv[1]);
+ } else if(!strcmp(s->argv[0], "fork")) {
+ pat->fchild = cadup(s->argv + 1);
+ } else if(!strcmp(s->argv[0], "end") || !strcmp(s->argv[0], "eof")) {
+ break;
+ } else {
+ flog(LOG_WARNING, "%s:%i: unknown directive `%s' in pattern declaration", s->file, s->lno, s->argv[0]);
}
-
- next:
- freeca(w);
- w = NULL;
- } while(!eof);
- rv = 0;
+ }
- if(w != NULL)
- freeca(w);
- fclose(s);
+ if(pat->rules[0] == NULL) {
+ flog(LOG_WARNING, "%s:%i: missing rules in match declaration", s->file, sl);
+ freepattern(pat);
+ return(NULL);
+ }
+ if((pat->childnm == NULL) && (pat->fchild == NULL)) {
+ flog(LOG_WARNING, "%s:%i: missing handler in match declaration", s->file, sl);
+ freepattern(pat);
+ return(NULL);
+ }
+ return(pat);
+}
+
+static struct config *emptyconfig(void)
+{
+ struct config *cf;
+
+ omalloc(cf);
+ return(cf);
+}
+
+static struct config *readconfig(char *file)
+{
+ struct cfstate *s;
+ FILE *in;
+ struct config *cf;
+ struct child *child;
+ struct pattern *pat;
+
+ if((in = fopen(file, "r")) == NULL) {
+ flog(LOG_WARNING, "%s: %s", file, strerror(errno));
+ return(NULL);
+ }
+ s = mkcfparser(in, file);
+ cf = emptyconfig();
+
+ while(1) {
+ getcfline(s);
+ if((child = parsechild(s)) != NULL) {
+ child->next = cf->children;
+ cf->children = child;
+ } else if((pat = parsepattern(s)) != NULL) {
+ pat->next = cf->patterns;
+ cf->patterns = pat;
+ } else if(!strcmp(s->argv[0], "index-file")) {
+ freeca(cf->index);
+ cf->index = NULL;
+ if(s->argc > 1)
+ cf->index = cadup(s->argv + 1);
+ } else if(!strcmp(s->argv[0], "eof")) {
+ break;
+ } else {
+ flog(LOG_WARNING, "%s:%i: unknown directive `%s'", s->file, s->lno, s->argv[0]);
+ }
+ }
+
+ freecfparser(s);
+ fclose(in);
return(cf);
}
{
struct config *cf;
struct stat sb;
+ char *fn;
+ time_t mtime;
+ fn = sprintf3("%s/.htrc", path);
for(cf = cflist; cf != NULL; cf = cf->next) {
if(!strcmp(cf->path, path)) {
- if(stat(path, &sb))
- return(NULL);
- if(sb.st_mtime != cf->mtime) {
- freeconfig(cf);
- break;
+ if(now - cf->lastck > 5) {
+ if(stat(fn, &sb) || (sb.st_mtime != cf->mtime)) {
+ freeconfig(cf);
+ break;
+ }
}
+ cf->lastck = now;
return(cf);
}
}
- if((cf = readconfig(path)) != NULL) {
- cf->next = cflist;
- cflist = cf;
+ if(access(fn, R_OK) || stat(fn, &sb)) {
+ cf = emptyconfig();
+ mtime = 0;
+ } else {
+ if((cf = readconfig(fn)) == NULL)
+ return(NULL);
+ mtime = sb.st_mtime;
}
+ cf->path = sstrdup(path);
+ cf->mtime = mtime;
+ cf->lastck = now;
+ cf->next = cflist;
+ cf->prev = NULL;
+ if(cflist != NULL)
+ cflist->prev = cf;
+ cflist = cf;
return(cf);
}
-static struct child *findchild(char *file, char *name)
+static struct config **getconfigs(char *file)
{
- char *buf, *p;
+ static struct config **ret = NULL;
+ struct {
+ struct config **b;
+ size_t s, d;
+ } buf;
struct config *cf;
- struct child *ch;
-
- buf = sstrdup(file);
+ char *tmp, *p;
+
+ if(ret != NULL)
+ free(ret);
+ bufinit(buf);
+ tmp = sstrdup(file);
while(1) {
- ch = NULL;
- if(!strcmp(buf, "."))
+ if((p = strrchr(tmp, '/')) == NULL)
break;
- if((p = strrchr(buf, '/')) != NULL)
- *p = 0;
- else
- strcpy(buf, ".");
- cf = getconfig(buf);
- if(cf == NULL)
- continue;
- if((ch = getchild(cf, name)) != NULL)
+ *p = 0;
+ if((cf = getconfig(tmp)) != NULL)
+ bufadd(buf, cf);
+ }
+ free(tmp);
+ if((cf = getconfig(".")) != NULL)
+ bufadd(buf, cf);
+ if(lconfig != NULL)
+ bufadd(buf, lconfig);
+ if(gconfig != NULL)
+ bufadd(buf, gconfig);
+ bufadd(buf, NULL);
+ return(ret = buf.b);
+}
+
+static struct child *findchild(char *file, char *name)
+{
+ int i;
+ struct config **cfs;
+ struct child *ch;
+
+ cfs = getconfigs(file);
+ for(i = 0; cfs[i] != NULL; i++) {
+ if((ch = getchild(cfs[i], name)) != NULL)
break;
}
- free(buf);
return(ch);
}
-static struct pattern *findmatch(char *file, int trydefault)
+static struct pattern *findmatch(char *file, int trydefault, int dir)
{
- int i;
- char *buf, *p, *bn;
- struct config *cf;
+ int i, o, c;
+ char *bn;
+ struct config **cfs;
struct pattern *pat;
struct rule *rule;
bn++;
else
bn = file;
- buf = sstrdup(file);
- while(1) {
- pat = NULL;
- if(!strcmp(buf, "."))
- break;
- if((p = strrchr(buf, '/')) != NULL)
- *p = 0;
- else
- strcpy(buf, ".");
- cf = getconfig(buf);
- if(cf == NULL)
- continue;
- for(pat = cf->patterns; pat != NULL; pat = pat->next) {
+ cfs = getconfigs(file);
+ for(c = 0; cfs[c] != NULL; c++) {
+ for(pat = cfs[c]->patterns; pat != NULL; pat = pat->next) {
+ if(!dir && (pat->type == PT_DIR))
+ continue;
+ if(dir && (pat->type != PT_DIR))
+ continue;
for(i = 0; (rule = pat->rules[i]) != NULL; i++) {
if(rule->type == PAT_BASENAME) {
- if(fnmatch(rule->pattern, bn, 0))
+ for(o = 0; rule->patterns[o] != NULL; o++) {
+ if(!fnmatch(rule->patterns[o], bn, 0))
+ break;
+ }
+ if(rule->patterns[o] == NULL)
break;
} else if(rule->type == PAT_PATHNAME) {
- if(fnmatch(rule->pattern, file, FNM_PATHNAME))
+ for(o = 0; rule->patterns[o] != NULL; o++) {
+ if(!fnmatch(rule->patterns[o], file, FNM_PATHNAME))
+ break;
+ }
+ if(rule->patterns[o] == NULL)
break;
} else if(rule->type == PAT_ALL) {
} else if(rule->type == PAT_DEFAULT) {
}
}
if(!rule)
- goto out;
+ return(pat);
}
}
-
-out:
- free(buf);
- return(pat);
+ if(!trydefault)
+ return(findmatch(file, 1, dir));
+ return(NULL);
}
-static void forkchild(struct child *ch)
+static void handle(struct hthead *req, int fd, char *path, struct pattern *pat)
{
- ch->fd = stdmkchild(ch->argv);
-}
+ struct child *ch;
-static void passreq(struct child *ch, struct hthead *req, int fd)
-{
- if(ch->fd < 0)
- forkchild(ch);
- if(sendreq(ch->fd, req, fd)) {
- if(errno == EPIPE) {
- /* Assume that the child has crashed and restart it. */
- forkchild(ch);
- if(!sendreq(ch->fd, req, fd))
- return;
+ headappheader(req, "X-Ash-File", path);
+ if(pat->fchild) {
+ stdforkserve(pat->fchild, req, fd);
+ } else {
+ if((ch = findchild(path, pat->childnm)) == NULL) {
+ flog(LOG_ERR, "child %s requested, but was not declared", pat->childnm);
+ simpleerror(fd, 500, "Configuration Error", "The server is erroneously configured. Handler %s was requested, but not declared.", pat->childnm);
+ return;
}
- flog(LOG_ERR, "could not pass on request to child %s: %s", ch->name, strerror(errno));
- close(ch->fd);
- ch->fd = -1;
+ if(childhandle(ch, req, fd))
+ simpleerror(fd, 500, "Server Error", "The request handler crashed.");
}
}
static void handlefile(struct hthead *req, int fd, char *path)
{
struct pattern *pat;
- struct child *ch;
- headappheader(req, "X-Ash-File", path);
- if(((pat = findmatch(path, 0)) == NULL) && ((pat = findmatch(path, 1)) == NULL)) {
- /* XXX: Send a 500 error? 404? */
- return;
- }
- if((ch = findchild(path, pat->childnm)) == NULL) {
- flog(LOG_ERR, "child %s requested, but was not declared", pat->childnm);
- simpleerror(fd, 500, "Configuration Error", "The server is erroneously configured. Handler %s was requested, but not declared.", pat->childnm);
+ if((pat = findmatch(path, 0, 0)) == NULL) {
+ simpleerror(fd, 404, "Not Found", "The requested URL has no corresponding resource.");
return;
}
-
- if(ch->type == CH_SOCKET) {
- passreq(ch, req, fd);
- } else if(ch->type == CH_FORK) {
- stdforkserve(ch->argv, req, fd);
- }
+ handle(req, fd, path, pat);
}
static void handledir(struct hthead *req, int fd, char *path)
{
- /* XXX: Todo */
- simpleerror(fd, 403, "Not Authorized", "Will not send directory listings or indices yet");
+ struct config **cfs;
+ int i, o;
+ struct stat sb;
+ char *inm, *ipath, *p, *cpath;
+ DIR *dir;
+ struct dirent *dent;
+ struct pattern *pat;
+
+ cpath = sprintf2("%s/", path);
+ cfs = getconfigs(cpath);
+ for(i = 0; cfs[i] != NULL; i++) {
+ if(cfs[i]->index != NULL) {
+ for(o = 0; cfs[i]->index[o] != NULL; o++) {
+ inm = cfs[i]->index[o];
+ ipath = sprintf2("%s/%s", path, inm);
+ if(!stat(ipath, &sb) && S_ISREG(sb.st_mode)) {
+ handlefile(req, fd, ipath);
+ free(ipath);
+ goto out;
+ }
+ free(ipath);
+
+ ipath = NULL;
+ if(!strchr(inm, '.') && ((dir = opendir(path)) != NULL)) {
+ while((dent = readdir(dir)) != NULL) {
+ if((p = strchr(dent->d_name, '.')) == NULL)
+ continue;
+ if(strncmp(dent->d_name, inm, strlen(inm)))
+ continue;
+ ipath = sprintf2("%s/%s", path, dent->d_name);
+ if(stat(ipath, &sb) || !S_ISREG(sb.st_mode)) {
+ free(ipath);
+ ipath = NULL;
+ continue;
+ }
+ break;
+ }
+ closedir(dir);
+ }
+ if(ipath != NULL) {
+ handlefile(req, fd, ipath);
+ free(ipath);
+ goto out;
+ }
+ }
+ break;
+ }
+ }
+ if((pat = findmatch(cpath, 0, 1)) != NULL) {
+ handle(req, fd, cpath, pat);
+ goto out;
+ }
+ simpleerror(fd, 403, "Not Authorized", "Will not send listings for this directory.");
+
+out:
+ free(cpath);
}
static int checkdir(struct hthead *req, int fd, char *path)
DIR *dir;
struct dirent *dent;
+ now = time(NULL);
nm = req->rest;
path = sstrdup(".");
p = nm;
} else {
*(p2++) = 0;
}
+ if((tmp = unquoteurl(p)) == NULL) {
+ simpleerror(fd, 400, "Bad Request", "The requested URL contains an invalid escape sequence.");
+ goto fail;
+ }
+ strcpy(p, tmp);
+ free(tmp);
if(!*p) {
if(p2 == NULL) {
else
path = sprintf2("%s/%s", path, p);
free(tmp);
+ if(p2 == NULL) {
+ stdredir(req, fd, 301, sprintf3("%s/", p));
+ goto out;
+ }
if(checkdir(req, fd, path))
break;
goto next;
free(path);
}
+static void usage(FILE *out)
+{
+ fprintf(out, "usage: dirplex [-hN] [-c CONFIG] DIR\n");
+}
+
int main(int argc, char **argv)
{
+ int c;
+ int nodef;
+ char *gcf, *lcf, *clcf;
struct hthead *req;
int fd;
- if(argc < 2) {
- flog(LOG_ERR, "usage: dirplex DIR");
+ nodef = 0;
+ lcf = NULL;
+ while((c = getopt(argc, argv, "hNc:")) >= 0) {
+ switch(c) {
+ case 'h':
+ usage(stdout);
+ exit(0);
+ case 'N':
+ nodef = 1;
+ break;
+ case 'c':
+ lcf = optarg;
+ break;
+ default:
+ usage(stderr);
+ exit(1);
+ }
+ }
+ if(argc - optind < 1) {
+ usage(stderr);
exit(1);
}
- if(chdir(argv[1])) {
- flog(LOG_ERR, "could not change directory to %s: %s", argv[1], strerror(errno));
+ if(!nodef) {
+ if((gcf = findstdconf("ashd/dirplex.rc")) != NULL) {
+ gconfig = readconfig(gcf);
+ free(gcf);
+ }
+ }
+ if(lcf != NULL) {
+ if(strchr(lcf, '/') == NULL) {
+ if((clcf = findstdconf(sprintf3("ashd/%s", lcf))) == NULL) {
+ flog(LOG_ERR, "could not find requested configuration `%s'", lcf);
+ exit(1);
+ }
+ if((lconfig = readconfig(clcf)) == NULL)
+ exit(1);
+ free(clcf);
+ } else {
+ if((lconfig = readconfig(lcf)) == NULL)
+ exit(1);
+ }
+ }
+ if(chdir(argv[optind])) {
+ flog(LOG_ERR, "could not change directory to %s: %s", argv[optind], strerror(errno));
exit(1);
}
signal(SIGCHLD, SIG_IGN);