1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
;; Tekuti
;; Copyright (C) 2008 Andy Wingo <wingo at pobox dot com>
;; 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, contact:
;;
;; Free Software Foundation Voice: +1-617-542-5942
;; 59 Temple Place - Suite 330 Fax: +1-617-542-2652
;; Boston, MA 02111-1307, USA gnu@gnu.org
;;; Commentary:
;;
;; This is the main script that will launch tekuti.
;;
;;; Code:
(define-module (tekuti web)
#:use-module (sxml simple)
#:use-module (sxml transform)
#:use-module (tekuti url)
#:use-module (tekuti request)
#:use-module (tekuti template)
#:use-module (tekuti page)
#:use-module (srfi srfi-1)
#:export (let-headers header-ref
handle-request))
(define *status-names*
'((200 . "OK")
(201 . "Created")
(401 . "Unauthorized")
(404 . "Not Found")
(500 . "Internal Server Error")))
(define (status->string status)
(format #f "~a ~a" status (or (assv-ref *status-names* status)
"Unknown Error")))
(define xhtml-doctype
(string-append
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" "
"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"))
(define (request-output-headers request)
(let-request request ((output-headers '())
(status 200)
(content-type "text/html"))
(acons "Status" (status->string status)
(acons "Content-Type" content-type
output-headers))))
;;; useless macro
(define-macro (let-headers headers bindings . body)
(let ((headers-var (gensym)))
`(let ((,headers-var ,headers))
(let (,@(map (lambda (binding)
`(,(car binding)
(or (assoc-ref ,headers-var ,(cadr binding))
(error "Missing header:" ,(cadr binding)))))
bindings))
,@body))))
(define (finalize request)
;; update output headers
;; templatize body
(rpush* (rcons* request
'sxml (templatize request)
'doctype xhtml-doctype)
'output-headers
(cons "Status" (status->string (rref request 'status 200)))
'output-headers
(cons "Content-Type" (rref request 'content-type "text/html"))))
(define (choose-handler request)
(request-path-case
request
((GET admin) page-admin)
((GET admin posts) page-admin-posts)
((GET admin posts post-key!) page-admin-post)
((POST admin new-post) page-admin-new-post)
((POST admin new-comment post-key!) page-admin-new-comment)
;; would be fine to have e.g. (DELETE admin posts posts-key!), but
;; web browsers don't handle that
((POST admin modify-post post-key!) page-admin-modify-post)
((POST admin delete-comment comment-key!) page-admin-delete-comment)
((POST admin delete-post post-key!) page-admin-delete-post)
((GET) page-index)
((GET archives year? month? day?) page-archives)
((GET archives year! month! day! post!) page-show-post)
((GET tags) page-show-tags)
((GET tags tag!) page-show-tag)
((GET debug) page-debug)
((POST search) page-search)
(else page-not-found)))
(define (handle-request request index)
(let ((handler (choose-handler request)))
(pk (finalize (handler request index)))))
|