#define _DEFAULT_SOURCE #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "config.h" static const char *badchars = "\\?%:|\"<> "; int writable(char *path) { struct stat s; if(!stat(path, &s)) { if(S_ISREG(s.st_mode)) return !eaccess(path, W_OK); else if(S_ISDIR(s.st_mode)) return 0; } char *dir = dirname(strdup(path)); if(!stat(dir, &s)) { if(S_ISDIR(s.st_mode)) return !eaccess(dir, W_OK); } return 0; } void head(char *page, char *link, char *what) { printf("Content-Type: text/html; charset=utf-8\n\n"); printf("\n"); printf("%s · %s\n", title, page); printf("\n", base); printf("\n", base); printf("

"); if(link && what && !strcmp(what, "rediger")) printf("%s", what, link, page); else if(link && what) printf("%s", what, base, link, page); else printf("%s", page); printf("

\n"); } void foot(int file) { printf("
\n"); printf("

"); for(int i = 0; *links[i]; i++) { if(i > 0) printf("· "); if(*links[i][1] == '/' || strstr(links[i][1], "://")) printf("%s ", links[i][1], links[i][0]); else printf("%s ", script, links[i][1], links[i][0]); } if(!file) return; printf("%s text/plain\n", id); } char *clean(char *p) { char *dup = strdup(basename(p)); char *x = dup; while(*x) { if(*x == '-') *x = ' '; x++; } return dup; } int legit(char *page) { if(!page) return 0; if(!strcmp(page, "/")) return 0; if(strstr(page, "..")) return 0; if(strcspn(page, badchars) < strlen(page)) return 0; return 1; } int problem(int status, char *message) { printf("Status: %d\n", status); printf("Content-Type: text/plain\n\n"); printf("%s\n", message); return 1; } int redirect(char *path, char *prefix) { printf("Status: 303\n"); switch(*path) { case '?': printf("Location: %s\n\n", path); break; case '#': printf("Location: %s/%s?m=", script, path); // skrive ut ++path printf("\n"); default: if(prefix) { printf("Location: %s/%s/%s\n\n", script, prefix, path); break; } else { printf("Location: %s/%s\n\n", script, path); break; } } return 0; } int unauthorized(int status) { printf("Status: %d\n", status); if(status == 401) printf("WWW-Authenticate: Basic realm='%s'\n", realm); printf("Content-Type: text/plain; charset=utf-8\n\n"); printf("No.\n"); return 1; } int edit(char *path) { if(!writable(path)) return unauthorized(403); FILE *fp = fopen(path, "r"); fp ? head(path, path, "vis") : head(path, 0, 0); char buffer[sysconf(_SC_PAGESIZE)]; printf("

\n"); printf("

\n"); if(orz) printf("@picto
\n"); printf("\n"); printf("

"); printf("

"); foot(1); if(fp) fclose(fp); return 0; } int store(char *raw, int len) { char *dir = dirname(strdup(raw)); char *id = basename(strdup(raw)); if(chdir(dir)) return problem(404, "Not found"); size_t siz = strlen(loft) + strlen(id) + 256; char path[siz]; time_t epoch = time(0); snprintf(path, siz, "%s/%s.%d", loft, id, (int) epoch); FILE *fp = fopen(path, "w"); if(!fp) return redirect("fff", 0); int pos = 0; unsigned int decoded; char buffer[3] = { 0 }; for(int i = 0; i < len; i++) { buffer[pos] = getchar(); if(buffer[pos] == '+') buffer[pos] = ' '; if(buffer[pos] == '&') { buffer[pos] = '\0'; break; } if(pos == 2) { if(buffer[0] == '%' && isxdigit(buffer[1]) && isxdigit(buffer[2])) { sscanf(buffer, "%%%2x", &decoded); fprintf(fp, "%c", decoded); memset(buffer, 0, 3); pos = 0; } else { fprintf(fp, "%c", buffer[0]); memmove(buffer, &buffer[1], 2); buffer[2] = 0; } } else pos++; } fprintf(fp, "%.3s", buffer); fputc('\0', fp); fclose(fp); unlink(id); symlink(path, id); return 0; } int post(char *path) { char *header; int clen; header = getenv("CONTENT_LENGTH"); if(!header) return redirect(path, 0); clen = atoi(header); while(clen > 1) if(clen--, getchar() == 't') if(clen--, getchar() == '=') store(path, clen); return redirect(path, 0); } // gjør denne penere! int list(char *raw, int dir, int inc) { int len = strlen(raw); char *pattern = malloc(len + 3); if(len > 0 && raw[len - 1] == '/') raw[len - 1] = '\0'; if(dir) snprintf(pattern, len + 3, "%s/*", raw); else snprintf(pattern, len + 1, "%s", raw); glob_t res; if(glob(pattern, GLOB_MARK, 0, &res)) { if(!inc) { head("ingen treff", 0, 0); foot(0); } return 0; } if(!inc) head("treff", 0, 0); char *path; printf("
    "); for(int i = 0; i < res.gl_pathc; i++) { path = res.gl_pathv[i]; if(*path == '.' && *path + 1 == '/') path += 2; printf("
  • %s", script, path, path); } printf("
"); if(!inc) foot(0); free(pattern); return 0; } int text(char *path, char *type) { FILE *fp = fopen(path, "r"); if(fp) { char buffer[sysconf(_SC_PAGESIZE)]; printf("Content-Type: %s; charset=utf-8\n\n", type); while(fgets(buffer, sizeof(buffer), fp)) printf("%s", buffer); } fclose(fp); return 0; } void include(FILE *out, char *path) { struct stat s; int plain = 0; if(*path == 't') plain = 1, path++; while(isspace(*path)) path++; if(!legit(path)) return; if(*path == '/') return; stat(path, &s); if(S_ISDIR(s.st_mode)) { list(path, 1, 1); return; } FILE *fp = fopen(path, "r"); if(!fp) return; if(plain) { printf("
");
    int c;
    while((c = fgetc(fp)) != EOF) {
      switch(c) {
        case '&': printf("&"); break;
        case '<': printf("<"); break;
        case '>': printf(">"); break;
        default: putc(c, stdout);
      }
    }
    printf("
"); } else { marxup(fp, out, 0); } fclose(fp); } void magic(FILE *out, char *line) { switch(*line++) { case 'i': include(out, line); break; default: fprintf(out, "WHAT: %s\n", line); return; } } int view(char *path) { struct stat s; stat(path, &s); if(S_ISDIR(s.st_mode)) { if(dirlist) return list(path, 1, 0); return redirect(home, path); } FILE *fp = fopen(path, "r"); if(!fp) return redirect("?e", 0); writable(path) ? head(path, "?e", "rediger") : head(path, 0, 0); marxup(fp, stdout, magic); foot(1); return 0; } int main(int argc, char **argv) { char *page = getenv("PATH_INFO"); char *qstr = getenv("QUERY_STRING"); char *verb = getenv("REQUEST_METHOD"); if(!page && argc > 1) page = argv[1]; if(!qstr && argc > 2) qstr = argv[2]; if(!verb) verb = "GET"; if(!script) script = getenv("SCRIPT_NAME"); if(!script) script = ""; if(!base) base = strdup(dirname(strdup(script))); if(authenticate && !(id = authenticate())) return unauthorized(401); if(!legit(page++)) return redirect(home, 0); setenv("MARXUP_HEADER", "2", 1); setenv("MARXUP_PREFIX", base, 1); setenv("MARXUP_WIKI", "1", 1); if(chdir(pages)) return problem(503, "Service unavailable"); if(!strncmp(verb, "POST", 4)) return post(page); if(strchr(page, '*')) return list(page, 0, 0); while(qstr && *qstr) { switch(*qstr) { case 'e': return edit(page); case 't': return text(page, "text/plain"); case 'c': return text(page, "text/css"); } qstr++; } return view(page); }