X-Git-Url: http://www.dolda2000.com/gitweb/?p=ashd.git;a=blobdiff_plain;f=src%2Fdirplex%2Fdirplex.c;fp=src%2Fdirplex%2Fdirplex.c;h=f1f99e429df29c44c467d4b7e6f0608265e96dd6;hp=0000000000000000000000000000000000000000;hb=600a1ce79471493f8cad5fcf118dc9797331d5aa;hpb=7f5a46defa51aef52a9b2aca958d90c01b8804ed diff --git a/src/dirplex/dirplex.c b/src/dirplex/dirplex.c new file mode 100644 index 0000000..f1f99e4 --- /dev/null +++ b/src/dirplex/dirplex.c @@ -0,0 +1,315 @@ +/* + ashd - A Sane HTTP Daemon + Copyright (C) 2008 Fredrik Tolf + + 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 . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include +#include + +#include "dirplex.h" + +time_t now; + +static void handle(struct hthead *req, int fd, char *path, struct pattern *pat) +{ + struct child *ch; + + 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; + } + 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; + + if((pat = findmatch(path, 0, 0)) == NULL) { + simpleerror(fd, 404, "Not Found", "The requested URL has no corresponding resource."); + return; + } + handle(req, fd, path, pat); +} + +static char *findfile(char *path, char *name, struct stat *sb) +{ + DIR *dir; + struct stat sbuf; + struct dirent *dent; + char *p, *fp, *ret; + + if(sb == NULL) + sb = &sbuf; + if((dir = opendir(path)) == NULL) + return(NULL); + ret = NULL; + while((dent = readdir(dir)) != NULL) { + /* Ignore backup files. + * XXX: There is probably a better and more extensible way to + * do this. */ + if(dent->d_name[strlen(dent->d_name) - 1] == '~') + continue; + if((p = strchr(dent->d_name, '.')) == NULL) + continue; + if(p - dent->d_name != strlen(name)) + continue; + if(strncmp(dent->d_name, name, strlen(name))) + continue; + fp = sprintf3("%s/%s", path, dent->d_name); + if(stat(fp, sb)) + continue; + if(!S_ISREG(sb->st_mode)) + continue; + ret = sstrdup(fp); + break; + } + closedir(dir); + return(ret); +} + +static void handledir(struct hthead *req, int fd, char *path) +{ + struct config **cfs; + int i, o; + struct stat sb; + char *inm, *ipath, *cpath; + 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); + + if(!strchr(inm, '.') && ((ipath = findfile(path, inm, NULL)) != 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 checkpath(struct hthead *req, int fd, char *path, char *rest); + +static int checkentry(struct hthead *req, int fd, char *path, char *rest, char *el) +{ + struct stat sb; + char *newpath; + int rv; + + if(!el == '.') { + simpleerror(fd, 404, "Not Found", "The requested URL has no corresponding resource."); + return(1); + } + if(!stat(sprintf3("%s/%s", path, el), &sb)) { + if(S_ISDIR(sb.st_mode)) { + if(!*rest) { + stdredir(req, fd, 301, sprintf3("%s/", el)); + return(1); + } + newpath = sprintf2("%s/%s", path, el); + rv = checkpath(req, fd, newpath, rest + 1); + free(newpath); + return(rv); + } else if(S_ISREG(sb.st_mode)) { + newpath = sprintf2("%s/%s", path, el); + replrest(req, rest); + handlefile(req, fd, newpath); + free(newpath); + return(1); + } + simpleerror(fd, 404, "Not Found", "The requested URL has no corresponding resource."); + return(1); + } + if(!strchr(el, '.') && ((newpath = findfile(path, el, NULL)) != NULL)) { + replrest(req, rest); + handlefile(req, fd, newpath); + free(newpath); + return(1); + } + return(0); +} + +static int checkpath(struct hthead *req, int fd, char *path, char *rest) +{ + struct config *cf; + char *p, *el; + int rv; + + el = NULL; + rv = 0; + + if(!strncmp(path, "./", 2)) + path += 2; + cf = getconfig(path); + + if((p = strchr(rest, '/')) == NULL) { + el = unquoteurl(rest); + rest = ""; + } else { + char buf[p - rest + 1]; + memcpy(buf, rest, p - rest); + buf[p - rest] = 0; + el = unquoteurl(buf); + rest = p; + } + if(el == NULL) { + simpleerror(fd, 400, "Bad Request", "The requested URL contains an invalid escape sequence."); + rv = 1; + goto out; + } + if(strchr(el, '/') || (!*el && *rest)) { + simpleerror(fd, 404, "Not Found", "The requested URL has no corresponding resource."); + rv = 1; + goto out; + } + if(!*el) { + replrest(req, rest); + handledir(req, fd, path); + return(1); + } + rv = checkentry(req, fd, path, rest, el); + +out: + if(el != NULL) + free(el); + return(rv); +} + +static void serve(struct hthead *req, int fd) +{ + now = time(NULL); + if(!checkpath(req, fd, ".", req->rest)) + simpleerror(fd, 404, "Not Found", "The requested URL has no corresponding resource."); +} + +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; + + 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(!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); + while(1) { + if((fd = recvreq(0, &req)) < 0) { + if(errno != 0) + flog(LOG_ERR, "recvreq: %s", strerror(errno)); + break; + } + serve(req, fd); + freehthead(req); + close(fd); + } + return(0); +}