Index: fabulist.scm ================================================================== --- fabulist.scm +++ fabulist.scm @@ -6,10 +6,17 @@ (chicken io) (chicken string) (chicken bitwise) (chicken random)) +(begin-for-syntax (import (chicken io))) +(include "scmlib/lisp-macro.scm") +(define-macro (static-import-string . body) + (call-with-input-file (car body) + (cut read-string #f <>))) +(define *stylesheet* (static-import-string "fabulist.css")) + (tcp-read-timeout 300) (define-record db (setter stories) (setter pages)) (define-record story title genre desc key start) @@ -81,12 +88,15 @@ query origin accept ref ua) (define (netreq-post-params req) (map (cut string-split <> "=") (string-split (netreq-body req) "&"))) -(define (post-field arr p) (let ((v (assoc p arr))) - (if (eq? #f v) "" (url-decode (cadr v))))) +(define (post-field arr p) + (let ((v (assoc p arr))) (if (or (null? v) + (eq? #f v) + (null? (cdr v))) "" + (url-decode (cadr v))))) (define (alist->netreq al body) (define (method->symbol m) (cond [(equal? m "GET") 'get] [(equal? m "POST") 'post] [(equal? m "PUT") 'put] [else 'unknown])) @@ -224,11 +234,11 @@ (let* ([kind-pair (determine-response-kind (netreq-accept req) (netreq-ua req))] [kind (car kind-pair)] [mime (cdr kind-pair)] [header (string-append (if (equal? user-header "") "" - " :: ") user-header)]) + " :: ") user-header)]) (define (open) (case kind ('html (string-append "\n\nfabulist" header "\n" "" @@ -258,83 +268,22 @@ (define (get-new-story req) 0) (define (get-stylesheet req) - (define style #<<_END_ - body { - background: #370017; - color: #FFE1ED; - max-width: 35em; - padding: 1em; - margin: auto; - font-family: Alegreya, Junicode, GaramondNo8, "Garamond Premier Pro", "Adobe Garamond", Garamond, serif; - font-size: 15pt; - } - input, textarea { - font-family: Alegreya, Junicode, GaramondNo8, "Garamond Premier Pro", "Adobe Garamond", Garamond, serif; - font-size: 14pt; - border: 1px solid #FF4C96; - background: #610028; - color: white; - padding: 0.3em; - flex-grow: 2; - text-shadow: 0 0 10px #E84B8C; - } - code { - font-family: Inconsolata, Monaco, monocode; - padding: 0.2em 0.3em; - background: #740C38; - border: 1px solid #260010; - } - textarea { - height: 6em; - } - input[type=submit] { - position: absolute; - flex-grow: 0; - width: 30%; - right: 0; - top: 1em; - text-shadow: 0 0 10px #E84B8C; - } - form > div { - position: relative; - display: flex; - width: 100%; - align-items: center; - } - form label { - margin-right: 0.5em; - margin-bottom: 0.5em; - margin-top: 0.5em; - font-weight: bold; - } - a[href] { - color: #FF2E84; - } - h1 { - font-weight: 100; - margin-bottom: 0.0em; - } - h1 a[href] { - color: #FF7AB1; - text-decoration: none; - } -_END_ - ) - (make-netresp 'ok (list (cons "Server" "") + (make-netresp 'ok (list (cons "Server" "fabulist") (cons "Content-Type" "text/css") (cons "Content-Length" (number->string - (string-length style)))) - style)) + (string-length *stylesheet*)))) + *stylesheet*)) (define (get-new-chapter req) (let* ([path (netreq-path req)] [place (follow-path (cdr path))] [params (netreq-post-params req)]) - (if (eq? #f place) (error-not-found req) + (if (or (eq? #f place) + (eq? #f (text-path-story-rec place))) (error-not-found req) (let* ([story-key (text-path-story-key place)] [story (text-path-story-rec place)] [chapter-key (text-path-chapter-key place)]) (new-chapter-page req story-key story chapter-key))))) @@ -343,36 +292,44 @@ (define (error-not-found req) (respond req 'nofile "error" '((p "the path you have entered is not meaningful.")) '(("start over" . "/")))) + +(define (error-bad-request req back) + (respond req 'badreq "error" + '((p "the data you have supplied is not valid.")) + `(("return to story" . ,back) + ("start over" . "/")))) (define (display-index-page req) (respond req 'ok "" '((p "welcome to this " (b "fabulist" ) " instance.") (p "fabulist is a server for hosting collaborative interactive fiction." ) (p "select one of the links below.")) '(("start a new story" . "/init") ("play an existing story" . "/find") - ("start a random story" . "/rand")))) + ("start a random story" . "/rand") + ("set your author details" . "/auth") + ))) (define (new-chapter-page req key story last-page) (let ([new-url (string-append "/new/" key (if (eq? #f last-page) "" (string-append "/" last-page)))]) (respond req 'ok (string-append (story-title story) " :: new chapter") - (list - '(p "write your chapter and hit " (b "commit") " to add it to the story") - `(form "POST" ,new-url - ,(if (eq? #f last-page) '(text "") - (list 'field "choice that leads here" "choice-link" "text")) - (field "chapter body" "chapter-body" "textarea"))) + (list + '(p "write your chapter and hit " (b "commit") " to add it to the story") + `(form "POST" ,new-url + ,(if (eq? #f last-page) '(text "") + (list 'field "choice that leads here" "choice-link" "text")) + (field "chapter body" "chapter-body" "textarea"))) (if (eq? last-page #f) '() - (list - (cons "abandon chapter" - (string-append "/p/" key "/" last-page)) - (cons "link to existing chapter" - (string-append "/link/" key "/" last-page))))))) + (list + (cons "abandon chapter" + (string-append "/p/" key "/" last-page)) + (cons "link to existing chapter" + (string-append "/link/" key "/" last-page))))))) (define-record text-path story-key story-rec chapter-key) (define (follow-path path) @@ -385,11 +342,11 @@ equal?)] [chapter-key (cond [(= pathlen 2) (cadr path)] [(eq? #f story-ent) #f] [else (story-start story-ent)])]) (make-text-path story-key story-ent chapter-key))]))) - + (define (format-user-text str) (map (cut list 'p <>) (string-split str "\n"))) (define (post-new-chapter req) @@ -401,40 +358,49 @@ (define (add-to-page story-key dest new-key) (let* ([destkey (string-append story-key "." dest)] [dest-pg (alist-ref destkey (db-pages *database*) equal?)] [old-choices (page-choices dest-pg)]) (page-choices-set! dest-pg (append old-choices (list new-key))))) - + (let* ([path (netreq-path req)] [place (follow-path (cdr path))] - [params (netreq-post-params req)]) + [params (netreq-post-params req)] + [raw-path (foldl string-append "" (intersperse (cdr path) "/"))]) (if (eq? #f place) (error-not-found req) (let* ([story-key (text-path-story-key place)] [story (text-path-story-rec place)] [chapter-key (text-path-chapter-key place)] + [chapter-full-key (if (eq? #f chapter-key) #f + (string-append story-key "." chapter-key))] [choice-link (post-field params "choice-link")] [chapter-body (post-field params "chapter-body")]) - (let* ([new-key (generate-key 4)] - [full-key (string-append story-key "." new-key)] - [new-page - (make-page choice-link - story-key - (format-user-text chapter-body) - '())]) - (if (= (length path) 2) - (if (eq? #f (story-start story)) - (begin (save-chapter full-key new-page) - (save-to-start story new-key) - (redirect (string-append "/p/" story-key "/" new-key))) - (error-not-found req)) - ; FIXME: insert error checking to make sure [chapter-key] - ; really exists; right now this will crash the program! - (begin (save-chapter full-key new-page) - (add-to-page story-key chapter-key new-key) - (redirect (string-append "/p/" story-key "/" chapter-key)))) - - ))))) + (cond [(or (equal? chapter-body "") + (and (equal? choice-link "") + (not (eq? chapter-key #f)))) + (error-bad-request req (string-append "/p/" raw-path))] + [else + (let* ([new-key (generate-key 4)] + [full-key (string-append story-key "." new-key)] + [new-page (make-page choice-link + story-key + (format-user-text chapter-body) + '())]) + (if (= (length path) 2); new story or new branch? + (if (eq? #f (story-start story)) + (begin (save-chapter full-key new-page) + (save-to-start story new-key) + (redirect (string-append "/p/" story-key "/" new-key))) + (error-not-found req)) + ; FIXME: insert error checking to make sure [chapter-key] + ; really exists; right now this will crash the program! + (if (eq? #f (alist-ref chapter-full-key (db-pages *database*) equal?)) + (error-not-found req) + (begin (save-chapter full-key new-page) + (add-to-page story-key chapter-key new-key) + (redirect (string-append "/p/" story-key "/" chapter-key)))) + + ))]))))) (define (get-story-chapter req) (define (render-chapter story-key story chapter) (define (lookup-chapter ch) (alist-ref (string-append story-key "." ch) @@ -445,17 +411,17 @@ (string-append "/p/" story-key "/" ch)))) (let ([chap (lookup-chapter chapter)]) (if (eq? #f chap) (error-not-found req) (respond req 'ok (story-title story) - (page-body chap) - (append - (map format-choice (page-choices chap)) - (list (cons '(i "[branch]") - (string-append - "/new/" story-key - "/" chapter)))))))) + (page-body chap) + (append + (map format-choice (page-choices chap)) + (list (cons '(i "[branch]") + (string-append + "/new/" story-key + "/" chapter)))))))) (let* ([path (netreq-path req)] [place (follow-path (cdr path))]) (if (eq? #f place) (error-not-found req) (let* ([story-key (text-path-story-key place)] @@ -617,7 +583,7 @@ (await-connections server) (tcp-close server))) (serve 4056) #;(cond-expand - (chicken-script ) - (else)) +(chicken-script ) +(else)) Index: nkvd.c ================================================================== --- nkvd.c +++ nkvd.c @@ -108,10 +108,14 @@ * too special to simply name their config file * "~/.(argv[0])". * * TODO: exempt xdg dirs beginning with ~/. from proscription * in nkvd_interdict_all=1 mode + * + * TODO: instead of function passthrough, alter environment + * to delete LD_PRELOAD and re-exec whitelisted apps + * without nkvd loading at all */ #include #include #include