#define _POSIX_C_SOURCE 200809L #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 !access(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 !access(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); if(link && what && !strcmp(what, "rediger")) printf("

%s

\n", what, link, page); else if(link && what) printf("

%s

\n", what, base, link, page); else printf("

%s

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

"); for(int i = 0; *links[i]; i++) printf("%s ", script, links[i][1], links[i][0]); if(!file) return; printf(""); printf("%s ", id); printf("text/plain"); printf("\n"); } 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) { printf("Status: 303\n"); if(*path == '?') printf("Location: %s\n\n", path); else printf("Location: %s/%s\n\n", script, path); return 0; } int unauthorized(int status) { printf("Status: %d\n", status); if(status == 401) printf("WWW-Authenticate: Basic realm='Kya'\n"); 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, NULL, NULL); char buffer[sysconf(_SC_PAGESIZE)]; printf("

\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"); char path[strlen(id) + strlen(loft) + 256]; time_t epoch = time(NULL); snprintf(path, 512, "%s/%s.%d", loft, id, (int) epoch); FILE *fp = fopen(path, "w"); if(!fp) return redirect("FFFF"); int pos = 0; unsigned int decoded; char buffer[3]; 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); clen = atoi(header); while(clen > 1) if(clen--, getchar() == 't') if(clen--, getchar() == '=') store(path, clen); return redirect(path); } // 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, NULL, &res)) { if(!inc) { head("ingen treff", NULL, NULL); foot(0); } return 0; } if(!inc) head("treff", NULL, NULL); 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) { FILE *fp = fopen(path, "r"); if(fp) { char buffer[sysconf(_SC_PAGESIZE)]; printf("Content-Type: text/plain; charset=utf-8\n\n"); while(fgets(buffer, sizeof(buffer), fp)) printf("%s", buffer); } fclose(fp); return 0; } void include(FILE *out, char *path) { struct stat s; 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; marxup(fp, out, NULL); 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)) return list(path, 1, 0); FILE *fp = fopen(path, "r"); if(!fp) return redirect("?e"); writable(path) ? head(path, "?e", "rediger") : head(path, NULL, NULL); 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); 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); // case 'p': return } qstr++; } return view(page); }