forked from brmlab/brmelect-github
2025 version
This commit is contained in:
parent
1d1468d87f
commit
ee4de955de
13 changed files with 2025 additions and 0 deletions
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
*~
|
||||
.*.sw?
|
||||
*.o
|
||||
*.link
|
||||
/eggs/
|
||||
*.import.scm
|
||||
52
Makefile
Normal file
52
Makefile
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#
|
||||
# Makefile
|
||||
#
|
||||
# Wrapper for src/
|
||||
#
|
||||
# ISC License
|
||||
#
|
||||
# Copyright 2023 Brmlab, z.s.
|
||||
# Dominik Pantůček <dominik.pantucek@trustica.cz>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software
|
||||
# for any purpose with or without fee is hereby granted, provided
|
||||
# that the above copyright notice and this permission notice appear
|
||||
# in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
||||
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
|
||||
.PHONY: default
|
||||
default:
|
||||
@make -C src
|
||||
|
||||
.PHONY: static
|
||||
static:
|
||||
@make -C src static
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@make -C src clean
|
||||
|
||||
.PHONY: install
|
||||
install:
|
||||
@make -C src install
|
||||
|
||||
.PHONY: install-dev
|
||||
install-dev:
|
||||
@make -C src install-dev
|
||||
|
||||
.PHONY: gendoc
|
||||
gendoc:
|
||||
@make -C src gendoc
|
||||
|
||||
.PHONY: doc
|
||||
doc: gendoc
|
||||
@./gendoc
|
||||
71
install-eggs.sh
Normal file
71
install-eggs.sh
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# install-eggs.sh
|
||||
#
|
||||
# Local installer of CHICKEN eggs required for building.
|
||||
#
|
||||
# ISC License
|
||||
#
|
||||
# Copyright 2023 Brmlab, z.s.
|
||||
# Dominik Pantůček <dominik.pantucek@trustica.cz>
|
||||
# Copyright 2025 Brmlab, z.s.
|
||||
# TMA
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software
|
||||
# for any purpose with or without fee is hereby granted, provided
|
||||
# that the above copyright notice and this permission notice appear
|
||||
# in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
||||
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
|
||||
# Source root directory
|
||||
owd=$(pwd)
|
||||
cd $(dirname "$0")
|
||||
SRCDIR=$(pwd)
|
||||
cd "$owd"
|
||||
|
||||
# Make temporary prefix directory (eggs shared throwaway files)
|
||||
TMPDIR=$(mktemp -d)
|
||||
|
||||
# Installs given egg locally
|
||||
chicken_install() {
|
||||
echo "Installing $1 ..."
|
||||
CHICKEN_INSTALL_PREFIX="$TMPDIR" \
|
||||
CHICKEN_REPOSITORY_PATH="$SRCDIR/eggs":`chicken-install -repository` \
|
||||
CHICKEN_INSTALL_REPOSITORY="$SRCDIR/eggs" \
|
||||
chicken-install "$1" 2>&1 | \
|
||||
sed -u 's/^/ /'
|
||||
}
|
||||
|
||||
# Removes throwaway files
|
||||
chicken_cleanup() {
|
||||
echo "Cleaning up ..."
|
||||
rm -fr ${TMPDIR}
|
||||
}
|
||||
|
||||
# Always cleanup
|
||||
trap chicken_cleanup INT QUIT
|
||||
|
||||
# Install required eggs
|
||||
chicken_install sqlite3
|
||||
chicken_install srfi-1
|
||||
chicken_install regex
|
||||
chicken_install srfi-13
|
||||
chicken_install srfi-18
|
||||
chicken_install srfi-69
|
||||
#chicken_install srfi-98 #builtin
|
||||
chicken_install srfi-99
|
||||
chicken_install miscmacros
|
||||
chicken_install cairo
|
||||
chicken_install crypt
|
||||
|
||||
# Normal termination cleanup
|
||||
chicken_cleanup
|
||||
95
src/Makefile
Normal file
95
src/Makefile
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
#
|
||||
# Makefile
|
||||
#
|
||||
# Building the project.
|
||||
#
|
||||
# ISC License
|
||||
#
|
||||
# Copyright 2023 Brmlab, z.s.
|
||||
# Dominik Pantůček <dominik.pantucek@trustica.cz>
|
||||
# Copyright 2025 Brmlab, z.s.
|
||||
# TMA
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software
|
||||
# for any purpose with or without fee is hereby granted, provided
|
||||
# that the above copyright notice and this permission notice appear
|
||||
# in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
||||
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
||||
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
|
||||
BRMELECT-BIN = ../brmelect.pl
|
||||
.PHONY: default
|
||||
default: imports
|
||||
|
||||
.PHONY: static
|
||||
static: $(BRMELECT-BIN)
|
||||
|
||||
# Uses local repository first, then system. Be sure to run
|
||||
# install-eggs.sh in the parent directory first!
|
||||
SCRP=$(shell chicken-install -repository)
|
||||
CSC=CHICKEN_REPOSITORY_PATH=../eggs:$(SCRP) csc
|
||||
|
||||
BRMELECT-DEPS=brmelect.scm webgate-utils.import.scm webgate-core.import.scm
|
||||
brmelect.scm.X = -X at-expr-on.scm
|
||||
|
||||
BRMELECT-OBJS=brmelect.o webgate-core.o webgate-utils.o
|
||||
|
||||
.PHONY: imports
|
||||
imports: $(BRMELECT-DEPS)
|
||||
|
||||
$(BRMELECT-BIN): $(BRMELECT-OBJS)
|
||||
$(CSC) -L --no-lto -L -Wl,-static -L -Wl,-lsqlite3 -L -Wl,-Bdynamic -strip -static -o $@ $(BRMELECT-OBJS)
|
||||
chmod u+s $@
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f *.c *.link *.o *.import.scm $(BRMELECT-BIN)
|
||||
|
||||
.PHONY: install
|
||||
install: static
|
||||
#install -m 0755 -d /usr/local/bin
|
||||
#install -m 0755 $(BRMELECT-BIN) /usr/local/bin/
|
||||
#install -m 0755 -d /usr/local/man/man1
|
||||
#install -m 0755 ../doc/brmelect.1 /usr/local/man/man1/brmelect.1
|
||||
|
||||
.PHONY: install-dev
|
||||
install-dev: static
|
||||
#install -m 0755 -d /usr/local/bin
|
||||
#install -m 0755 $(BRMELECT-BIN) /usr/local/bin/brmelect-dev
|
||||
#install -m 0755 -d /usr/local/man/man1
|
||||
#install -m 0755 ../doc/brmelect.1 /usr/local/man/man1/brmelect-dev.1
|
||||
|
||||
################################################################
|
||||
# Module static and shared object and import source compilation
|
||||
|
||||
%.o: %.scm
|
||||
$(CSC) $($<.X) -c -static $<
|
||||
|
||||
%.import.scm: %.scm
|
||||
$(CSC) $($<.X) -regenerate-import-libraries -P -J $<
|
||||
|
||||
################################################################
|
||||
# Main programs
|
||||
|
||||
brmelect.o: $(BRMELECT-DEPS)
|
||||
|
||||
################################################################
|
||||
# Modules
|
||||
|
||||
WEBGATE-CORE-SOURCES=webgate-core.scm
|
||||
|
||||
webgate-core.o: webgate-core.import.scm
|
||||
webgate-core.import.scm: $(WEBGATE-CORE-SOURCES)
|
||||
|
||||
WEBGATE-UTILS-SOURCES=webgate-utils.scm
|
||||
|
||||
webgate-utils.o: webgate-utils.import.scm
|
||||
webgate-utils.import.scm: $(WEBGATE-UTILS-SOURCES)
|
||||
2
src/at-expr-on.scm
Normal file
2
src/at-expr-on.scm
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
(import (only webgate-utils use-at-read-table))
|
||||
(use-at-read-table #:list-arguments? #t)
|
||||
113
src/brmelect.scm
Normal file
113
src/brmelect.scm
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
;;#!/usr/bin/env csi -s
|
||||
|
||||
;; Import necessary modules for CGI handling
|
||||
;(use posix)
|
||||
;(use srfi-19) ; For basic string manipulation
|
||||
|
||||
(import scheme
|
||||
(chicken base)
|
||||
(chicken format)
|
||||
;(chicken miscmacros)
|
||||
miscmacros
|
||||
sqlite3
|
||||
webgate-core
|
||||
;configuration
|
||||
)
|
||||
(import-for-syntax
|
||||
srfi-1
|
||||
webgate-core)
|
||||
|
||||
;(eval-when (eval)
|
||||
;(cond-expand
|
||||
; (chicken
|
||||
; (begin
|
||||
; (import (only webgate-utils use-at-read-table))
|
||||
; (use-at-read-table #:list-arguments? #t)))
|
||||
; ;(else #f)
|
||||
; )
|
||||
|
||||
;; Thread-local parameter to re-use SQLite3 DB handle for subsequent queries
|
||||
(define *cached-brmelect-db* (make-parameter #f))
|
||||
(define =brmelect-db-path= "/home/brmelect/brmelect/brmelect.sqlite3")
|
||||
|
||||
;; Returns (possibly cached) SQLite3 DB handle
|
||||
(define (brmelect-db)
|
||||
(let ((cdb (*cached-brmelect-db*)))
|
||||
(if cdb
|
||||
cdb
|
||||
(begin
|
||||
(*cached-brmelect-db*
|
||||
(let ((handler (make-busy-timeout 2000)))
|
||||
(let ((db (open-database =brmelect-db-path=)))
|
||||
(set-busy-handler! db handler)
|
||||
db)))
|
||||
(*cached-brmelect-db*)))))
|
||||
|
||||
(define (initialize-db)
|
||||
(let ((db (brmelect-db)))
|
||||
;; admin passwords
|
||||
(execute db "create table if not exists admins (login text not null primary key, password text not null)")
|
||||
;; GA
|
||||
(execute db "create table if not exists general_assemblies (id integer primary key, ga_date text not null, ga_open integer not null)")
|
||||
(execute db "insert or ignore into general_assemblies (id, ga_date, ga_open) values (17, '2025-01-21', 1)")
|
||||
;; elections
|
||||
(execute db "create table if not exists elections (id integer primary key autoincrement, ga_id integer not null, election_name text not null, election_running integer not null, election_file text not null, foreign key (ga_id) references general_assemblies(id))")
|
||||
;; candidates
|
||||
(execute db "create table if not exists candidates (id integer primary key autoincrement, election_id integer not null, candidate_name text not null, candidate_nick text not null, foreign key (election_id) references elections(id)")
|
||||
))
|
||||
|
||||
(initialize-db)
|
||||
|
||||
(define-resource (root* parameters)
|
||||
(make-html-response
|
||||
200
|
||||
;@li{@a[(href ,(resource-uri calc "add"))]{Suspensions}}
|
||||
;@,common-head
|
||||
`@html{
|
||||
@head{
|
||||
@title{foo}
|
||||
}
|
||||
@body{
|
||||
@div[(class "navbar navbar-inverse navbar-fixed-top")]{
|
||||
@div[(class "navbar-inner")]{
|
||||
@div[(class "container")]{
|
||||
@a[(class "brand") (href "#")]{WebGate}
|
||||
@div[(class "nav-collapse collapse")]{
|
||||
@ul[(class "nav")]{
|
||||
@li[(class "active")]{@a[(href "#")]{Miscellaneous}}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
(define brmelect-style
|
||||
"p, li { font-family: monospace; }
|
||||
#ballot { margin-left: auto; margin-right: auto; border: 1pt solid; width: 20em; padding: 1ex 1em; }
|
||||
.error { text-width: bold; color: red }
|
||||
#blurb { margin: 8ex 2em; }
|
||||
table { margin-left: auto; margin-right: auto; border: 1pt solid; border-collapse: collapse; }
|
||||
td { border: 1pt solid; padding: 0.5ex 0.5em; }")
|
||||
|
||||
(define-resource (elect "elect" parameters)
|
||||
(make-html-response
|
||||
200
|
||||
`@html{
|
||||
@head{@title{brmelect Web Ballot}
|
||||
@style[(type "text/css")]{@,brmelect-style}}
|
||||
@body{
|
||||
|
||||
}}))
|
||||
|
||||
(define-resource (elect "elect" "config" parameters)
|
||||
(make-html-response
|
||||
200
|
||||
`@html{
|
||||
@head{@title{brmelect web config}}
|
||||
@body{
|
||||
|
||||
}}))
|
||||
|
||||
(cgi-main-loop handle-request)
|
||||
328
src/example.scm
Normal file
328
src/example.scm
Normal file
|
|
@ -0,0 +1,328 @@
|
|||
;; -*- mode: Scheme; -*-
|
||||
|
||||
;; https://chust.org/repos/chicken-webgate/file?name=example.scm&ci=tip
|
||||
|
||||
;;
|
||||
;; This file is part of WebGate for CHICKEN.
|
||||
;; Copyright (c) 2011-2013 by Thomas Chust. All rights reserved.
|
||||
;;
|
||||
;; Permission is hereby granted, free of charge, to any person
|
||||
;; obtaining a copy of this software and associated documentation
|
||||
;; files (the Software), to deal in the Software without restriction,
|
||||
;; including without limitation the rights to use, copy, modify,
|
||||
;; merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
;; Software, and to permit persons to whom the Software is furnished
|
||||
;; to do so, subject to the following conditions:
|
||||
;;
|
||||
;; The above copyright notice and this permission notice shall be
|
||||
;; included in all copies or substantial portions of the Software.
|
||||
;;
|
||||
;; THE SOFTWARE IS PROVIDED ASIS, WITHOUT WARRANTY OF ANY KIND,
|
||||
;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
;; SOFTWARE.
|
||||
|
||||
(require-library webgate)
|
||||
|
||||
;; Use -extend at-expr during compilation!
|
||||
(eval-when (eval)
|
||||
(import (only webgate-utils use-at-read-table))
|
||||
(use-at-read-table #:list-arguments? #t))
|
||||
|
||||
(import
|
||||
webgate (only webgate-utils base64-encode))
|
||||
|
||||
(define common-head
|
||||
'@head{
|
||||
@meta[(charset "utf-8")]
|
||||
@meta[(name "viewport") (content "width=device-width, initial-scale=1.0")]
|
||||
@title{WebGate}
|
||||
@meta[(name "description") (content "CHICKEN WebGate example")]
|
||||
@meta[(name "author") (content "Thomas Chust")]
|
||||
@link[(rel "stylesheet") (href "/css/bootstrap.min.css")]
|
||||
@link[(rel "stylesheet") (href "/css/bootstrap-responsive.min.css")]
|
||||
@style[(type "text/css")]{body{padding-top:60px; padding-bottom:40px}}
|
||||
})
|
||||
|
||||
(define common-foot
|
||||
'@{
|
||||
@script[(src "/js/jquery.min.js")]
|
||||
@script[(src "/js/bootstrap.min.js")]
|
||||
})
|
||||
|
||||
(define-resource (root* parameters)
|
||||
(make-redirect-response 301 (resource-uri root)))
|
||||
|
||||
(define-resource (root "root" parameters)
|
||||
(make-html-response
|
||||
200
|
||||
`@html{
|
||||
@,common-head
|
||||
@body{
|
||||
@div[(class "navbar navbar-inverse navbar-fixed-top")]{
|
||||
@div[(class "navbar-inner")]{
|
||||
@div[(class "container")]{
|
||||
@a[(class "brand") (href "#")]{WebGate}
|
||||
@div[(class "nav-collapse collapse")]{
|
||||
@ul[(class "nav")]{
|
||||
@li[(class "active")]{@a[(href "#")]{Miscellaneous}}
|
||||
@li{@a[(href ,(resource-uri calc "add"))]{Suspensions}}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@div[(class "container")]{
|
||||
@div[(class "hero-unit")]{
|
||||
@h1{Application Example}
|
||||
@p{
|
||||
This sample program just shows some information
|
||||
extracted from the incoming request.
|
||||
}
|
||||
}
|
||||
@div[(class "row")]{
|
||||
@div[(class "span8")]{
|
||||
@h2{Present Context}
|
||||
@table[(class "zebra-striped")]{
|
||||
@thead{
|
||||
@tr{@th{Key} @th{Value}}
|
||||
}
|
||||
@tbody{
|
||||
@,@(let ((getenv (resource-context-getenv
|
||||
(current-resource-context))))
|
||||
`((tr (td "SCRIPT_NAME")
|
||||
(td (code ,(or (getenv "SCRIPT_NAME") "<unknown>"))))
|
||||
(tr (td "PATH_INFO")
|
||||
(td (code ,(or (getenv "PATH_INFO") "<unknown>"))))
|
||||
(tr (td "REQUEST_METHOD")
|
||||
(td (code ,(or (getenv "REQUEST_METHOD") "<unknown>"))))
|
||||
(tr (td "REMOTE_ADDR")
|
||||
(td (code ,(or (getenv "REMOTE_ADDR") "<unknown>"))))
|
||||
(tr (td "REMOTE_PORT")
|
||||
(td (code ,(or (getenv "REMOTE_PORT") "<unknown>"))))))
|
||||
}
|
||||
}
|
||||
}
|
||||
@div[(class "span8")]{
|
||||
@h2{Present Parameters}
|
||||
@table[(class "zebra-striped")]{
|
||||
@thead{
|
||||
@tr{@th{Key} @th{Messages}}
|
||||
}
|
||||
@tbody{
|
||||
@,@(map
|
||||
(lambda (key+msgs)
|
||||
(let-values (((key msgs) (car+cdr key+msgs)))
|
||||
`(tr
|
||||
(td ,key)
|
||||
(td
|
||||
(ol
|
||||
,@(map
|
||||
(lambda (msg)
|
||||
`(li
|
||||
(p
|
||||
,(let ((type (message-type msg)))
|
||||
(cond
|
||||
((message-text msg)
|
||||
=> (lambda (txt)
|
||||
`(span
|
||||
(span
|
||||
((class "label notice"))
|
||||
"Text Content:")
|
||||
" " ,txt)))
|
||||
((string-prefix? "image/" type)
|
||||
`(span
|
||||
(span
|
||||
((class "label notice"))
|
||||
"Image Content:")
|
||||
" "
|
||||
(img
|
||||
((src ,(string-append
|
||||
"data:" type ";base64,"
|
||||
(base64-encode
|
||||
(message-body msg))))))))
|
||||
(else
|
||||
`(span
|
||||
(span
|
||||
((class "label notice"))
|
||||
"Omitted Content:")
|
||||
" "
|
||||
(code ,type)))))
|
||||
,@(map
|
||||
(lambda (header)
|
||||
(let-values (((key value) (car+cdr header)))
|
||||
`(span
|
||||
", "
|
||||
(span ((class "label")) ,key ":")
|
||||
" "
|
||||
(code ,value))))
|
||||
(message-headers msg)))))
|
||||
msgs))))))
|
||||
(hash-table->alist parameters))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@div[(class "row")]{
|
||||
@div[(class "span8")]{
|
||||
@h2{GET with Parameters}
|
||||
@form[(method "GET") (action ,(resource-uri root))]{
|
||||
@fieldset{
|
||||
@legend{Stuff}
|
||||
@div[(class "clearfix")]{
|
||||
@label[(for "some-thing0")]{Some value}
|
||||
@div[(class "input")]{
|
||||
@input[(type "text") (id "some-thing0") (class "medium")
|
||||
(name "some-thing") (size "30")]
|
||||
}
|
||||
}
|
||||
@div[(class "clearfix")]{
|
||||
@label[(for "other-things0")]{Other values}
|
||||
@div[(class "input")]{
|
||||
@select[(id "other-things0") (class "medium")
|
||||
(name "other-things") (multiple "multiple")]{
|
||||
@option{foobaz}
|
||||
@option{dosh}
|
||||
@option{gostak}
|
||||
}
|
||||
}
|
||||
}
|
||||
@div[(class "actions")]{
|
||||
@input[(type "submit") (class "btn primary")
|
||||
(value "Submit")]
|
||||
@nbsp
|
||||
@input[(type "reset") (class "btn")
|
||||
(value "Reset")]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@div[(class "span8")]{
|
||||
@h2{POST with Parameters}
|
||||
@form[(method "POST") (enctype "multipart/form-data")
|
||||
(action ,(resource-uri root))]{
|
||||
@fieldset{
|
||||
@legend{Upload}
|
||||
@div[(class "clearfix")]{
|
||||
@label[(for "some-thing1")]{Some value}
|
||||
@div[(class "input")]{
|
||||
@input[(type "text") (id "some-thing1") (class "medium")
|
||||
(name "some-thing") (size "30")]
|
||||
}
|
||||
}
|
||||
@div[(class "clearfix")]{
|
||||
@label[(for "file-thing1")]{Some file}
|
||||
@div[(class "input")]{
|
||||
@input[(type "file") (id "file-thing1") (class "medium")
|
||||
(name "file-thing")]
|
||||
}
|
||||
}
|
||||
@div[(class "actions")]{
|
||||
@input[(type "submit") (class "btn primary")
|
||||
(value "Submit")]
|
||||
@nbsp
|
||||
@input[(type "reset") (class "btn")
|
||||
(value "Reset")]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@footer{@copy 2011-2015 by Thomas Chust}
|
||||
}
|
||||
@,@common-foot
|
||||
}
|
||||
}))
|
||||
|
||||
(define numeric-parameter
|
||||
(cute
|
||||
parameter-ref <> <>
|
||||
(lambda (v)
|
||||
(cond
|
||||
((message-text v) => string->number)
|
||||
(else #f)))))
|
||||
|
||||
(define-resource (calc "calc" op parameters)
|
||||
(if (string=? op "add")
|
||||
(let* ((common-topbar
|
||||
`@div[(class "navbar navbar-inverse navbar-fixed-top")]{
|
||||
@div[(class "navbar-inner")]{
|
||||
@div[(class "container")]{
|
||||
@a[(class "brand") (href "#")]{WebGate}
|
||||
@div[(class "nav-collapse collapse")]{
|
||||
@ul[(class "nav")]{
|
||||
@li{@a[(href ,(resource-uri root))]{Miscellaneous}}
|
||||
@li[(class "active")]{@a[(href "#")]{Suspensions}}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
(parameters
|
||||
(send/suspend
|
||||
(lambda (resume-uri)
|
||||
(make-html-response
|
||||
200
|
||||
`@html{
|
||||
@,common-head
|
||||
@body{
|
||||
@,common-topbar
|
||||
@div[(class "container")]{
|
||||
@form[(method "GET") (action ,resume-uri)]{
|
||||
@fieldset{
|
||||
@legend{Add Numbers}
|
||||
@div[(class "clearfix")]{
|
||||
@label[(for "a")]{First Summand}
|
||||
@div[(class "input")]{
|
||||
@input[(type "text") (id "a") (class "medium")
|
||||
(name "a") (size "30")]
|
||||
}
|
||||
}
|
||||
@div[(class "clearfix")]{
|
||||
@label[(for "a")]{Second Summand}
|
||||
@div[(class "input")]{
|
||||
@input[(type "text") (id "b") (class "medium")
|
||||
(name "b") (size "30")]
|
||||
}
|
||||
}
|
||||
@div[(class "actions")]{
|
||||
@input[(type "submit") (class "btn primary")
|
||||
(value "Submit")]
|
||||
@nbsp
|
||||
@input[(type "reset") (class "btn")
|
||||
(value "Reset")]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}))))
|
||||
(a
|
||||
(or (numeric-parameter parameters "a") 0))
|
||||
(b
|
||||
(or (numeric-parameter parameters "b") 0)))
|
||||
(make-html-response
|
||||
200
|
||||
`@html{
|
||||
@,common-head
|
||||
@body{
|
||||
@,common-topbar
|
||||
@div[(class "container")]{
|
||||
@div[(class "hero-unit")]{
|
||||
@h1{@,(number->string (+ a b))}
|
||||
@p{@hellip is the answer}
|
||||
}
|
||||
}
|
||||
@,@common-foot
|
||||
}
|
||||
}))
|
||||
(make-error-response
|
||||
400 "Don't know how to perform the requested calculation.")))
|
||||
|
||||
(webgate-main)
|
||||
|
||||
;;This page was generated in about 0.01s by Fossil 2.24 [8be0372c10] 2024-04-23 13:25:26
|
||||
97
src/suspension.scm
Normal file
97
src/suspension.scm
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
;; -*- mode: Scheme; -*-
|
||||
|
||||
|
||||
;; https://chust.org/repos/chicken-webgate/file?name=suspension.scm&ci=tip
|
||||
|
||||
;;
|
||||
;; This file is distributed with WebGate for CHICKEN.
|
||||
;; Copyright (c) 2006-2010 by Felix L. Winkelmann. All rights reserved.
|
||||
;;
|
||||
;; Redistribution and use in source and binary forms, with or without
|
||||
;; modification, are permitted provided that the following conditions
|
||||
;; are met:
|
||||
;;
|
||||
;; 1. Redistributions of source code must retain the above copyright
|
||||
;; notice, this list of conditions and the following disclaimer.
|
||||
;;
|
||||
;; 2. Redistributions in binary form must reproduce the above
|
||||
;; copyright notice, this list of conditions and the following
|
||||
;; disclaimer in the documentation and/or other materials provided
|
||||
;; with the distribution.
|
||||
;;
|
||||
;; 3. The name of the authors may not be used to endorse or promote
|
||||
;; products derived from this software without specific prior
|
||||
;; written permission.
|
||||
;;
|
||||
;; THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
|
||||
;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
;; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
||||
;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
(require-library srfi-18 ports protobuf)
|
||||
(declare (disable-interrupts))
|
||||
|
||||
(module suspension
|
||||
(with-limited-continuation
|
||||
continuation-drop
|
||||
continuation-suspend
|
||||
continuation-resume)
|
||||
(import
|
||||
scheme chicken
|
||||
srfi-18 ports (only protobuf-generic serialize deserialize))
|
||||
|
||||
(define error-output ##sys#standard-error)
|
||||
(define standard-output ##sys#standard-output)
|
||||
(define standard-input ##sys#standard-input)
|
||||
|
||||
(define (exception-handler ex)
|
||||
(thread-signal! (thread-specific ##sys#current-thread) ex)
|
||||
(continuation-drop #f) )
|
||||
|
||||
(define (with-limited-continuation thunk)
|
||||
(let* ((t (make-thread
|
||||
(lambda ()
|
||||
(##sys#call-with-cthulhu
|
||||
(lambda ()
|
||||
(##sys#call-with-values thunk continuation-drop) ) ) ) ) )
|
||||
(state (##sys#slot t 5)) )
|
||||
(##sys#setislot state 0 '())
|
||||
(##sys#setslot state 1 standard-input)
|
||||
(##sys#setslot state 2 standard-output)
|
||||
(##sys#setslot state 3 error-output)
|
||||
(##sys#setslot state 4 exception-handler)
|
||||
(thread-specific-set! t ##sys#current-thread)
|
||||
(thread-start! t)
|
||||
(thread-suspend! ##sys#current-thread)
|
||||
(##sys#setslot (##sys#slot t 5) 5 (##sys#slot state 5))
|
||||
(##sys#apply-values (##sys#slot t 2)) ) )
|
||||
|
||||
(define (continuation-drop . results)
|
||||
(##sys#setslot ##sys#current-thread 2 results)
|
||||
(thread-resume! (thread-specific ##sys#current-thread))
|
||||
(##sys#thread-kill! ##sys#current-thread 'dead)
|
||||
(##sys#schedule) )
|
||||
|
||||
(define (continuation-suspend store)
|
||||
(##sys#apply-values
|
||||
(##sys#call-with-direct-continuation
|
||||
(lambda (k)
|
||||
(let ((o (open-output-string)))
|
||||
(serialize k o)
|
||||
(##sys#call-with-values
|
||||
(lambda () (store (get-output-string o)))
|
||||
continuation-drop) ) ) ) ) )
|
||||
|
||||
(define (continuation-resume k . results)
|
||||
(##sys#direct-return (with-input-from-string k deserialize) results) )
|
||||
|
||||
)
|
||||
|
||||
;;This page was generated in about 0.008s by Fossil 2.24 [8be0372c10] 2024-04-23 13:25:26
|
||||
36
src/volitelni.awk
Normal file
36
src/volitelni.awk
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#!/bin/awk -f
|
||||
|
||||
# vh_number=17; hackerbase -sheet /dev/stdout $vh_number -quiet |
|
||||
|
||||
BEGIN {
|
||||
FS=" *& *";
|
||||
OFS=",";
|
||||
print "create table if not exists members (";
|
||||
print "id integer primary key not null,";
|
||||
print "nick text not null,";
|
||||
print "name text not null,";
|
||||
print "surname text not null,";
|
||||
print "in_debt integer not null,";
|
||||
print "short_tenure integer not null,";
|
||||
print "eligible integer not null);";
|
||||
print "insert into members"
|
||||
print "(id,nick,name,surname,in_debt,short_tenure,eligible) values";
|
||||
valsep="(";
|
||||
}
|
||||
{
|
||||
dluh=1;
|
||||
kratky=1;
|
||||
gsub(/\\small */,"")
|
||||
gsub(/\\_/,"_")
|
||||
}
|
||||
/Bez.dluhu/ { dluh=0; }
|
||||
/9\/12/ { kratky = 0; }
|
||||
/1[012]\/12/ { kratky = 0; }
|
||||
/^[0-9][0-9][0-9][0-9] / {
|
||||
print valsep $1,"'" $2 "'","'" $3 "'","'" $4 "'",dluh,kratky,!(dluh||kratky) ")";
|
||||
valsep=",("
|
||||
}
|
||||
END {
|
||||
print ";";
|
||||
}
|
||||
|
||||
10
src/volitelni.txt
Normal file
10
src/volitelni.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
dzoe> TMA: co chces na tom vypise volitelnych?
|
||||
TMA> to, co se tiskne na volebni listky: jmeno, nick
|
||||
dzoe> TMA: hackerbase -sheet /dev/stdout 17 -quiet | egrep '^[0-9]{4} '|egrep 'Bez.dluhu'|egrep '9/12|10/12|11/12|12/12'|awk
|
||||
'BEGIN{FS=" *& *";OFS=","}{print $1,$2,$3,$4}'|sed 's/[\\]small *//g'
|
||||
dzoe> IMHO ten oneliner, co jsem pastnul, vygeneruje pricetne CSV, pro potreby generovani volebnich listku - ale samozrejme
|
||||
idealne by to rovnou generovalo PDF(ka).
|
||||
TMA> dzoe: jako kdo musim byt prihlasen, aby to chodilo?
|
||||
dzoe> No, jako hackerbase@brmlab.cz
|
||||
TMA> ok
|
||||
|
||||
552
src/webgate-core.scm
Normal file
552
src/webgate-core.scm
Normal file
|
|
@ -0,0 +1,552 @@
|
|||
;; -*- mode: Scheme; -*-
|
||||
|
||||
;; modified for chicken 5 and brmelect
|
||||
;; Copyright (c) 2011-2013 by TMA. All rights reserved.
|
||||
|
||||
;; https://chust.org/repos/chicken-webgate/file?name=webgate-core.scm&ci=tip
|
||||
|
||||
;; This file is part of WebGate for CHICKEN.
|
||||
;; Copyright (c) 2011-2013 by Thomas Chust. All rights reserved.
|
||||
;;
|
||||
;; Permission is hereby granted, free of charge, to any person
|
||||
;; obtaining a copy of this software and associated documentation
|
||||
;; files (the Software), to deal in the Software without restriction,
|
||||
;; including without limitation the rights to use, copy, modify,
|
||||
;; merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
;; Software, and to permit persons to whom the Software is furnished
|
||||
;; to do so, subject to the following conditions:
|
||||
;;
|
||||
;; The above copyright notice and this permission notice shall be
|
||||
;; included in all copies or substantial portions of the Software.
|
||||
;;
|
||||
;; THE SOFTWARE IS PROVIDED ASIS, WITHOUT WARRANTY OF ANY KIND,
|
||||
;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
;; SOFTWARE.
|
||||
|
||||
;;; Message base type
|
||||
|
||||
(declare (unit webgate-core))
|
||||
|
||||
(module webgate-core
|
||||
(message make-message message?
|
||||
message-type message-headers message-body message-text
|
||||
write-message
|
||||
max-request-size
|
||||
request-method-handler
|
||||
request-body-handler
|
||||
request-parameter-handler
|
||||
parameter-list-ref parameter-ref
|
||||
resource-context current-resource-context resource-context?
|
||||
resource-context-getenv resource-context-method resource-context-path
|
||||
response make-response response?
|
||||
collect-response make-html-response make-error-response
|
||||
make-redirect-response
|
||||
response-status response-status-message
|
||||
write-response
|
||||
resource-handler resource-uri
|
||||
(define-resource resource-handler extend-procedure procedure-data)
|
||||
handle-query-parameters
|
||||
handle-request
|
||||
cgi-main-loop)
|
||||
(import
|
||||
scheme
|
||||
(chicken base)
|
||||
(chicken format)
|
||||
(chicken string)
|
||||
(chicken port)
|
||||
(chicken io)
|
||||
(chicken memory representation)
|
||||
(chicken time)
|
||||
;(chicken continuation)
|
||||
(chicken condition)
|
||||
srfi-1
|
||||
srfi-4
|
||||
srfi-13
|
||||
srfi-18
|
||||
srfi-69
|
||||
srfi-98
|
||||
srfi-99
|
||||
;data-structures extras lolevel
|
||||
(chicken irregex)
|
||||
;suspension
|
||||
webgate-utils
|
||||
)
|
||||
|
||||
(define-record-type message
|
||||
%make-message #t
|
||||
type headers
|
||||
body)
|
||||
|
||||
(define (make-message
|
||||
body #!key
|
||||
(type "application/octet-stream") (headers '()))
|
||||
(%make-message type headers body))
|
||||
|
||||
(define message-text
|
||||
(let ((text/plain-rx (irregex '(: bos "text/plain" (or ";" eos)))))
|
||||
(lambda (msg)
|
||||
(and (irregex-search text/plain-rx (message-type msg))
|
||||
(message-body msg)))))
|
||||
|
||||
(define (write-message msg #!optional (port (current-output-port)))
|
||||
(let ((type (message-type msg))
|
||||
(body (message-body msg)))
|
||||
(when type
|
||||
(fprintf port "Content-type: ~a\r\n" type))
|
||||
(when body
|
||||
(fprintf port "Content-length: ~a\r\n" (string-length body)))
|
||||
(for-each
|
||||
(lambda (header)
|
||||
(call-with-values (cut car+cdr header)
|
||||
(cut fprintf port "~a: ~a\r\n" <> <>)))
|
||||
(message-headers msg))
|
||||
(display "\r\n" port)
|
||||
(when body
|
||||
(display body port))))
|
||||
|
||||
;;; Request processing infrastructure
|
||||
|
||||
(define max-request-size
|
||||
(make-parameter #xffff))
|
||||
|
||||
(define-values (request-method-handler handled-request-methods)
|
||||
(let ((handlers (make-hash-table #:test string-ci=? #:hash string-ci-hash)))
|
||||
(values
|
||||
(case-lambda
|
||||
((name)
|
||||
(hash-table-ref/default handlers name #f))
|
||||
((name proc)
|
||||
(hash-table-set! handlers name proc)))
|
||||
(cut hash-table-keys handlers))))
|
||||
|
||||
(define request-body-handler
|
||||
(let ((handlers (make-hash-table #:test string-ci=? #:hash string-ci-hash)))
|
||||
(case-lambda
|
||||
((name)
|
||||
(hash-table-ref/default handlers name #f))
|
||||
((name proc)
|
||||
(hash-table-set! handlers name proc)))))
|
||||
|
||||
(define request-parameter-handler
|
||||
(let ((handler
|
||||
(lambda (parameters key msg)
|
||||
(hash-table-update!/default
|
||||
parameters key (cut append! <> (list msg)) '()))))
|
||||
(case-lambda
|
||||
(()
|
||||
handler)
|
||||
((proc)
|
||||
(set! handler proc)))))
|
||||
|
||||
(define (parameter-list-ref parameters key #!optional (convert message-text))
|
||||
(map convert (hash-table-ref/default parameters key '())))
|
||||
|
||||
(define (parameter-ref parameters key #!optional (convert message-text))
|
||||
(and-let* ((vs (hash-table-ref/default parameters key '()))
|
||||
((pair? vs)))
|
||||
(convert (car vs))))
|
||||
|
||||
;;; Response processing infrastructure
|
||||
|
||||
(define-record-type resource-context
|
||||
%make-resource-context #t
|
||||
getenv method path)
|
||||
|
||||
(define current-resource-context
|
||||
(make-parameter #f))
|
||||
|
||||
(define status-table
|
||||
(alist->hash-table
|
||||
'((100 . "Continue")
|
||||
(101 . "Switching Protocols")
|
||||
(200 . "Ok")
|
||||
(201 . "Created")
|
||||
(202 . "Accepted")
|
||||
(203 . "Non-Authoritative Information")
|
||||
(204 . "No Content")
|
||||
(205 . "Reset Content")
|
||||
(206 . "Partial Content")
|
||||
(300 . "Multiple Choices")
|
||||
(301 . "Moved Permanently")
|
||||
(302 . "Found")
|
||||
(303 . "See Other")
|
||||
(304 . "Not Modified")
|
||||
(305 . "Use Proxy")
|
||||
(307 . "Temporary Redirect")
|
||||
(400 . "Bad Request")
|
||||
(401 . "Unauthorized")
|
||||
(402 . "Payment Required")
|
||||
(403 . "Forbidden")
|
||||
(404 . "Not Found")
|
||||
(405 . "Method Not Allowed")
|
||||
(406 . "Not Acceptable")
|
||||
(407 . "Proxy Authentication Required")
|
||||
(408 . "Request Timeout")
|
||||
(409 . "Conflict")
|
||||
(410 . "Gone")
|
||||
(411 . "Length Required")
|
||||
(412 . "Precondition Failed")
|
||||
(413 . "Request Entity Too Large")
|
||||
(414 . "Request-URI Too Long")
|
||||
(415 . "Unsupported Media Type")
|
||||
(416 . "Requested Range Not Satisfiable")
|
||||
(417 . "Expectation Failed")
|
||||
(500 . "Internal Server Error")
|
||||
(501 . "Not Implemented")
|
||||
(502 . "Bad Gateway")
|
||||
(503 . "Service Unavailable")
|
||||
(504 . "Gateway Timeout")
|
||||
(505 . "HTTP Version Not Supported"))
|
||||
#:test = #:hash number-hash))
|
||||
|
||||
(define-record-type (response message)
|
||||
%make-response #t
|
||||
status status-message)
|
||||
|
||||
(define (make-response
|
||||
status body #!key
|
||||
(type (and body "application/octet-stream"))
|
||||
(headers '())
|
||||
(status-message
|
||||
(hash-table-ref/default status-table status "Unknown")))
|
||||
(%make-response
|
||||
type headers body
|
||||
status status-message))
|
||||
|
||||
(define (collect-response
|
||||
status thunk #!key
|
||||
(type "application/octet-stream")
|
||||
(headers '())
|
||||
(status-message
|
||||
(hash-table-ref/default status-table status "Unknown")))
|
||||
(%make-response
|
||||
type headers (with-output-to-string thunk)
|
||||
status status-message))
|
||||
|
||||
(define (make-html-response
|
||||
status html #!key
|
||||
(status-message
|
||||
(hash-table-ref/default status-table status "Unknown"))
|
||||
(headers '()))
|
||||
(%make-response
|
||||
"text/html" headers (call-with-output-string (cut write-html html <>))
|
||||
status status-message))
|
||||
|
||||
(define (make-error-response
|
||||
status message #!key
|
||||
(status-message
|
||||
(hash-table-ref/default status-table status "Unknown"))
|
||||
(headers '()))
|
||||
(make-html-response
|
||||
status
|
||||
(let ((status-line (sprintf "~a ~a" status status-message)))
|
||||
`(html
|
||||
(head
|
||||
(meta ((name "robots") (content "noindex")))
|
||||
(title ,status-line))
|
||||
(body
|
||||
(h1 ,status-line)
|
||||
(p ,message))))
|
||||
#:status-message status-message
|
||||
#:headers headers))
|
||||
|
||||
(define make-redirect-response
|
||||
(case-lambda
|
||||
((status target)
|
||||
(make-error-response
|
||||
status `(a ((href ,target)) ,target)
|
||||
#:headers `(("Location" . ,target))))
|
||||
((target)
|
||||
(make-error-response
|
||||
302 `(a ((href ,target)) ,target)
|
||||
#:headers `(("Location" . ,target))))))
|
||||
|
||||
(define (write-response rsp #!optional (port (current-output-port)))
|
||||
(fprintf
|
||||
port "Status: ~a ~a\r\n"
|
||||
(response-status rsp) (response-status-message rsp))
|
||||
(write-message rsp port))
|
||||
|
||||
(define resource-handler
|
||||
(let ((handlers (make-hash-table)))
|
||||
(case-lambda
|
||||
((path)
|
||||
(let next ((handlers handlers) (args '()) (path path))
|
||||
(if (pair? path)
|
||||
(let-values (((step path) (car+cdr path)))
|
||||
(cond
|
||||
((hash-table-ref/default handlers step #f)
|
||||
=> (cut next <> args path))
|
||||
((hash-table-ref/default handlers #f #f)
|
||||
=> (cut next <> (cons step args) path))
|
||||
(else
|
||||
#f)))
|
||||
(cond
|
||||
((hash-table-ref/default handlers #t #f)
|
||||
=> (lambda (proc)
|
||||
(lambda (parameters)
|
||||
(apply proc (reverse! (cons* parameters args))))))
|
||||
(else
|
||||
#f)))))
|
||||
((path proc)
|
||||
(let next ((handlers handlers) (path path))
|
||||
(if (pair? path)
|
||||
(let-values (((step path) (car+cdr path)))
|
||||
(hash-table-update!
|
||||
handlers step (cut next <> path) make-hash-table))
|
||||
(hash-table-set! handlers #t proc))
|
||||
handlers)
|
||||
(void)))))
|
||||
|
||||
(define-syntax define-resource
|
||||
(syntax-rules ()
|
||||
((define-resource (name step/arg ... parameters)
|
||||
expr ...)
|
||||
(begin
|
||||
(define name
|
||||
(let-syntax ((path
|
||||
(ir-macro-transformer
|
||||
(lambda (stx inject id=?)
|
||||
(let ((steps (cdr stx)))
|
||||
`(list ,@(map
|
||||
(lambda (step)
|
||||
(and (string? step) step))
|
||||
steps))))))
|
||||
(path-lambda
|
||||
(ir-macro-transformer
|
||||
(lambda (stx inject id=?)
|
||||
(let ((steps (cadr stx))
|
||||
(body (cddr stx)))
|
||||
`(lambda ,(filter-map
|
||||
(lambda (step)
|
||||
(and (symbol? step) step))
|
||||
steps)
|
||||
,@body))))))
|
||||
(extend-procedure
|
||||
(path-lambda (step/arg ... parameters)
|
||||
expr ...)
|
||||
(path step/arg ...))))
|
||||
(resource-handler (procedure-data name) name)))))
|
||||
|
||||
(define (write-uri-step step port)
|
||||
(fprintf port "/~a" (uri-encode step)))
|
||||
|
||||
(define (resource-uri res . args)
|
||||
(call-with-output-string
|
||||
(lambda (port)
|
||||
(for-each
|
||||
(cut write-uri-step <> port)
|
||||
(string-split
|
||||
(or ((resource-context-getenv (current-resource-context)) "SCRIPT_NAME")
|
||||
"")
|
||||
"/"))
|
||||
(let next ((steps (procedure-data res)) (args args))
|
||||
(if (pair? steps)
|
||||
(let-values (((step steps) (car+cdr steps)))
|
||||
(if step
|
||||
(begin
|
||||
(write-uri-step step port)
|
||||
(next steps args))
|
||||
(if (pair? args)
|
||||
(let-values (((arg args) (car+cdr args)))
|
||||
(write-uri-step arg port)
|
||||
(next steps args))
|
||||
(error 'resource-uri "too few arguments"))))
|
||||
(unless (null? args)
|
||||
(error 'resource-uri "too many arguments" args)))))))
|
||||
|
||||
;;; Pre-installed default handlers (and directly related stuff)
|
||||
|
||||
(define (handle-query-parameters parameters query)
|
||||
(for-each
|
||||
(lambda (key+value)
|
||||
(let-optionals (map uri-decode (string-split key+value "="))
|
||||
((key #f) (value ""))
|
||||
(when key
|
||||
((request-parameter-handler)
|
||||
parameters key
|
||||
(make-message value #:type "text/plain")))))
|
||||
(string-split query "&;"))
|
||||
#f)
|
||||
|
||||
(request-body-handler "application/x-www-form-urlencoded"
|
||||
(lambda (parameters type size port)
|
||||
(handle-query-parameters parameters (read-string size port))))
|
||||
|
||||
(request-body-handler "multipart/form-data"
|
||||
(letrec ((boundary-rx
|
||||
(irregex '(: bow "boundary=" ($ (+ (~ (" ;\n\r\t")))))))
|
||||
(multipart-boundary
|
||||
(lambda (s)
|
||||
(cond
|
||||
((irregex-search boundary-rx s)
|
||||
=> (cut irregex-match-substring <> 1))
|
||||
(else
|
||||
#f))))
|
||||
(header-rx
|
||||
(irregex '(: ($ (+ (~ #\:))) #\: (* space) ($ (*? any))
|
||||
(or "\r\n" eos))))
|
||||
(special+regular-headers
|
||||
(lambda (s start end special)
|
||||
(partition
|
||||
(lambda (key+value)
|
||||
(member (car key+value) special string-ci=?))
|
||||
(irregex-fold
|
||||
header-rx
|
||||
(lambda (start m seed)
|
||||
(cons (cons (irregex-match-substring m 1)
|
||||
(irregex-match-substring m 2))
|
||||
seed))
|
||||
'() s
|
||||
(lambda (start seed)
|
||||
(reverse! seed))
|
||||
start end))))
|
||||
(name-rx
|
||||
(irregex '(: bow "name=" #\" ($ (*? (~ #\"))) #\")))
|
||||
(disposition-name
|
||||
(lambda (s default)
|
||||
(cond
|
||||
((irregex-search name-rx s)
|
||||
=> (cut irregex-match-substring <> 1))
|
||||
(else
|
||||
default))))
|
||||
(handle-messages
|
||||
(lambda (parameters name data boundary)
|
||||
(let ((boundary-rx
|
||||
(irregex `(: (or bos "\r\n") "--"
|
||||
,boundary
|
||||
(? "--") "\r\n"))))
|
||||
(irregex-fold
|
||||
boundary-rx
|
||||
(lambda (start m skip?)
|
||||
(and-let* (((not skip?))
|
||||
(end
|
||||
(irregex-match-start-index m))
|
||||
(header-end
|
||||
(string-contains data "\r\n\r\n" start end))
|
||||
(body
|
||||
(substring/shared data (+ header-end 4) end)))
|
||||
(let-values (((specials headers)
|
||||
(special+regular-headers
|
||||
data start header-end
|
||||
'("Content-type" "Content-length"))))
|
||||
(let ((type
|
||||
(alist-ref
|
||||
"Content-type" specials string-ci=?
|
||||
"text/plain"))
|
||||
(name
|
||||
(disposition-name
|
||||
(alist-ref
|
||||
"Content-disposition" headers string-ci=?)
|
||||
name)))
|
||||
(when name
|
||||
(cond
|
||||
((multipart-boundary type)
|
||||
=> (cut handle-messages parameters name body <>))
|
||||
(else
|
||||
((request-parameter-handler)
|
||||
parameters name
|
||||
(make-message
|
||||
body #:type type #:headers headers))))))))
|
||||
#f)
|
||||
#t data))
|
||||
#f)))
|
||||
(lambda (parameters type size port)
|
||||
(cond
|
||||
((multipart-boundary type)
|
||||
=> (cut handle-messages
|
||||
parameters #f (read-string size port) <>))
|
||||
(else
|
||||
(make-error-response
|
||||
501 "The server doesn't know how to parse request parameters from the content type sent."))))))
|
||||
|
||||
(request-method-handler "GET"
|
||||
(lambda (parameters method getenv port)
|
||||
(handle-query-parameters parameters (or (getenv "QUERY_STRING") ""))))
|
||||
|
||||
(request-method-handler "POST"
|
||||
(lambda (parameters method getenv port)
|
||||
(or
|
||||
(handle-query-parameters parameters (or (getenv "QUERY_STRING") ""))
|
||||
(let ((type (or
|
||||
(getenv "CONTENT_TYPE")
|
||||
"application/octet-stream"))
|
||||
(size (cond
|
||||
((getenv "CONTENT_LENGTH")
|
||||
=> string->number)
|
||||
(else
|
||||
#f))))
|
||||
(cond
|
||||
((not size)
|
||||
(make-error-response
|
||||
411 "The server refuses processing as no content length was sent with the request."))
|
||||
((cond ((max-request-size) => (cut > size <>)) (else #f))
|
||||
(make-error-response
|
||||
413 "The server refuses processing as the request's content length is too large."))
|
||||
((request-body-handler (substring/shared
|
||||
type 0 (or (string-index type #\;)
|
||||
(string-length type))))
|
||||
=> (cut <> parameters type size port))
|
||||
(else
|
||||
(make-error-response
|
||||
501 "The server doesn't know how to parse request parameters from the content type sent.")))))))
|
||||
|
||||
;;; FIXME: thread magic could replace this:
|
||||
(define (with-limited-continuation thunk)
|
||||
(thunk))
|
||||
|
||||
;;; CGI server "loop"
|
||||
|
||||
(define (cgi-main-loop handle-request)
|
||||
(handle-request
|
||||
get-environment-variable
|
||||
(current-input-port) (cute write-response <> (current-output-port))))
|
||||
|
||||
;;; Central server routine
|
||||
|
||||
(define (handle-request getenv input-port write-response)
|
||||
(write-response
|
||||
(handle-exceptions
|
||||
exn (begin
|
||||
(when (uncaught-exception? exn)
|
||||
(set! exn (uncaught-exception-reason exn)))
|
||||
(fprintf (current-output-port) "Content-type: text/plain\r\nConnection: close\r\n\r\n")
|
||||
(print-error-message
|
||||
exn (current-output-port)
|
||||
(sprintf "[~a] Request Handling Error" (current-seconds)))
|
||||
(print-call-chain)
|
||||
(make-error-response
|
||||
500 "The server encountered an internal error handling the request."))
|
||||
(let ((parameters (make-hash-table))
|
||||
(method (or (getenv "REQUEST_METHOD") "GET"))
|
||||
(path (string-split (uri-decode (or (getenv "PATH_INFO") (getenv "SCRIPT_URL") "")) "/")))
|
||||
(or
|
||||
(cond
|
||||
((request-method-handler method)
|
||||
=> (cut <> parameters method getenv input-port))
|
||||
(else
|
||||
(make-error-response
|
||||
405 "The access method used to request the document is not supported."
|
||||
#:headers
|
||||
(list
|
||||
(cons "Allow" (string-join (handled-request-methods) ", "))))))
|
||||
(cond
|
||||
((resource-handler path)
|
||||
=> (lambda (proc)
|
||||
(with-limited-continuation
|
||||
(lambda ()
|
||||
(current-resource-context
|
||||
(%make-resource-context getenv method path))
|
||||
(proc parameters)))))
|
||||
(else
|
||||
(make-error-response
|
||||
404 "The requested resource was not found by the server.")))
|
||||
(make-response 204 '()))))))
|
||||
|
||||
|
||||
)
|
||||
468
src/webgate-utils.scm
Normal file
468
src/webgate-utils.scm
Normal file
|
|
@ -0,0 +1,468 @@
|
|||
;; -*- mode: Scheme; -*-
|
||||
;;
|
||||
;; modified for chicken 5 and brmelect
|
||||
;; Copyright (c) 2011-2013 by TMA. All rights reserved.
|
||||
|
||||
;; https://chust.org/repos/chicken-webgate/file?name=webgate-utils.scm&ci=tip
|
||||
|
||||
;;
|
||||
;; This file is part of WebGate for CHICKEN.
|
||||
;; Copyright (c) 2011-2013 by Thomas Chust. All rights reserved.
|
||||
;;
|
||||
;; Permission is hereby granted, free of charge, to any person
|
||||
;; obtaining a copy of this software and associated documentation
|
||||
;; files (the Software), to deal in the Software without restriction,
|
||||
;; including without limitation the rights to use, copy, modify,
|
||||
;; merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
;; Software, and to permit persons to whom the Software is furnished
|
||||
;; to do so, subject to the following conditions:
|
||||
;;
|
||||
;; The above copyright notice and this permission notice shall be
|
||||
;; included in all copies or substantial portions of the Software.
|
||||
;;
|
||||
;; THE SOFTWARE IS PROVIDED ASIS, WITHOUT WARRANTY OF ANY KIND,
|
||||
;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
;; SOFTWARE.
|
||||
|
||||
;;; Compression
|
||||
|
||||
(declare (unit webgate-utils))
|
||||
|
||||
(module webgate-utils
|
||||
(#;compress #;decompress
|
||||
write-netstring read-netstring
|
||||
make-at-reader make-at-read-table use-at-read-table
|
||||
uri-encode uri-decode
|
||||
base64-encode base64-decode
|
||||
write-html)
|
||||
(import
|
||||
scheme
|
||||
(chicken base)
|
||||
;(chicken foreign)
|
||||
(chicken io)
|
||||
(chicken format)
|
||||
(chicken fixnum)
|
||||
(chicken syntax)
|
||||
(chicken keyword)
|
||||
(chicken read-syntax)
|
||||
srfi-1 srfi-13 srfi-14 srfi-69
|
||||
;(except data-structures compress) extras
|
||||
(chicken irregex))
|
||||
(import-for-syntax srfi-1)
|
||||
|
||||
#;(foreign-declare
|
||||
"#include <bzlib.h>")
|
||||
|
||||
#;(define (compress idata #!optional [level 9])
|
||||
(let* ((isize (string-length idata))
|
||||
(odata (make-string (inexact->exact (round (+ 600 (* 1.01 isize)))))))
|
||||
(let-location ((osize unsigned-int (string-length odata)))
|
||||
(if ((foreign-lambda* bool ((scheme-pointer odata) ((c-pointer unsigned-int) osize)
|
||||
(scheme-pointer idata) (unsigned-int isize) (int level))
|
||||
"C_return(BZ2_bzBuffToBuffCompress(odata, osize, idata, isize, level, 0, 0) == BZ_OK);")
|
||||
odata (location osize) idata isize level)
|
||||
(substring odata 0 osize)
|
||||
(error 'compress "Data compression error")))))
|
||||
|
||||
#;(define (decompress idata)
|
||||
(let ((isize (string-length idata)))
|
||||
(let retry ((odata (make-string (* 2 isize))))
|
||||
(let-location ((osize unsigned-int (string-length odata)))
|
||||
(case ((foreign-lambda* int ((scheme-pointer odata) ((c-pointer unsigned-int) osize)
|
||||
(scheme-pointer idata) (unsigned-int isize))
|
||||
"switch (BZ2_bzBuffToBuffDecompress(odata, osize, idata, isize, 0, 0)) {\n"
|
||||
"case BZ_OK: C_return(0);\n"
|
||||
"case BZ_OUTBUFF_FULL: C_return(1);\n"
|
||||
"default: C_return(2);\n"
|
||||
"}\n")
|
||||
odata (location osize) idata isize)
|
||||
((0) (substring odata 0 osize))
|
||||
((1) (retry (make-string (* 2 osize))))
|
||||
(else (error 'decompress "Data decompression error")))))))
|
||||
|
||||
;;; Netstrings
|
||||
|
||||
(define (write-netstring s #!optional (port (current-output-port)))
|
||||
(fprintf port "~a:~a," (string-length s) s))
|
||||
|
||||
(define (read-netstring #!optional (port (current-input-port)))
|
||||
(let ((l (string->number (read-token char-numeric? port))))
|
||||
(unless l
|
||||
(error
|
||||
'read-netstring
|
||||
"client side protocol error: malformed netstring (bad length)"))
|
||||
(unless (eq? (read-char port) #\:)
|
||||
(error
|
||||
'read-netstring
|
||||
"client side protocol error: malformed netstring (bad delimiter)"))
|
||||
(let ((s (read-string l port)))
|
||||
(unless (eq? (read-char port) #\,)
|
||||
(error
|
||||
'read-netstring
|
||||
"client side protocol error: malformed netstring (bad terminal)"))
|
||||
s)))
|
||||
|
||||
;;; @-expressions
|
||||
|
||||
(define (make-at-reader+table args)
|
||||
(letrec* ((command-char
|
||||
(get-keyword #:command-char args (constantly #\@)))
|
||||
(trim-whitespace?
|
||||
(get-keyword #:trim-whitespace? args (constantly #t)))
|
||||
(condense-whitespace?
|
||||
(get-keyword #:condense-whitespace? args (constantly #t)))
|
||||
(list-arguments?
|
||||
(get-keyword #:list-arguments? args (constantly #f)))
|
||||
(char-normal?
|
||||
(cute char-set-contains?
|
||||
(char-set-complement
|
||||
(char-set command-char #\{ #\} #\return #\newline))
|
||||
<>))
|
||||
(char-group?
|
||||
(cute char-set-contains?
|
||||
(char-set #\[ #\{)
|
||||
<>))
|
||||
(skip-whitespace
|
||||
(lambda (port)
|
||||
(when (char-whitespace? (peek-char port))
|
||||
(read-char port)
|
||||
(skip-whitespace port))))
|
||||
(read-whitespace
|
||||
(if condense-whitespace?
|
||||
(lambda (port)
|
||||
(skip-whitespace port)
|
||||
" ")
|
||||
(cut read-token char-whitespace? <>)))
|
||||
(read-datum
|
||||
(lambda (port)
|
||||
(parameterize ((current-read-table datum-read-table))
|
||||
(read port))))
|
||||
(read-at-exp
|
||||
(lambda (port)
|
||||
(skip-whitespace port)
|
||||
(let ((char (peek-char port)))
|
||||
(cond
|
||||
((eof-object? char)
|
||||
(read-char port))
|
||||
(else
|
||||
(when (eqv? char command-char)
|
||||
(read-char port))
|
||||
(let* ((head (and (not (char-group? (peek-char port)))
|
||||
(read-datum port)))
|
||||
(args (and (eqv? (peek-char port) #\[)
|
||||
(read-datum port)))
|
||||
(body (and (eqv? (peek-char port) #\{)
|
||||
(read-inside-at-exp 'skip port))))
|
||||
(if (or args body)
|
||||
(append!
|
||||
(cond
|
||||
(head => list)
|
||||
(else '()))
|
||||
(cond
|
||||
((and list-arguments? args) => list)
|
||||
(else (or args '())))
|
||||
(or body '()))
|
||||
head)))))))
|
||||
(read-inside-at-exp
|
||||
(lambda (brace-mode port)
|
||||
(append!
|
||||
(let ((head
|
||||
(case brace-mode
|
||||
((none)
|
||||
'())
|
||||
((skip)
|
||||
(and (eqv? (peek-char port) #\{)
|
||||
(begin (read-char port) '())))
|
||||
(else
|
||||
(and (eqv? (peek-char port) #\{)
|
||||
(list (string (read-char port))))))))
|
||||
(if head
|
||||
(begin
|
||||
(when trim-whitespace? (skip-whitespace port))
|
||||
head)
|
||||
(syntax-error
|
||||
'read-inside-at-exp "expected @-expression body, found"
|
||||
(peek-char port))))
|
||||
(let more ()
|
||||
(let ((char (peek-char port)))
|
||||
(cond
|
||||
((eqv? char #\{)
|
||||
(case brace-mode
|
||||
((none)
|
||||
(cons (string (read-char port)) (more)))
|
||||
(else
|
||||
(append! (read-inside-at-exp 'keep port) (more)))))
|
||||
((eqv? char #\})
|
||||
(case brace-mode
|
||||
((none)
|
||||
(cons (string (read-char port)) (more)))
|
||||
((skip)
|
||||
(read-char port)
|
||||
'())
|
||||
(else
|
||||
(list (string (read-char port))))))
|
||||
((eof-object? char)
|
||||
(case brace-mode
|
||||
((none)
|
||||
(read-char port)
|
||||
'())
|
||||
(else
|
||||
(syntax-error
|
||||
'read-inside-at-exp "@-expression body not closed"))))
|
||||
((eqv? char command-char)
|
||||
(cons (read-at-exp port) (more)))
|
||||
((char-whitespace? char)
|
||||
(let* ((head (read-whitespace port))
|
||||
(tail (more)))
|
||||
(if (or (pair? tail) (not trim-whitespace?))
|
||||
(cons head tail)
|
||||
tail)))
|
||||
(else
|
||||
(cons (read-token char-normal? port) (more)))))))))
|
||||
(read-table
|
||||
(get-keyword #:read-table args current-read-table))
|
||||
(at-read-table
|
||||
(parameterize ((current-read-table (copy-read-table read-table)))
|
||||
(set-read-syntax! command-char read-at-exp)
|
||||
(current-read-table)))
|
||||
(datum-read-table
|
||||
(let ((spec (get-keyword #:datum-read-table args (constantly #t))))
|
||||
(cond
|
||||
((procedure? spec)
|
||||
(spec at-read-table))
|
||||
(spec
|
||||
at-read-table)
|
||||
(else
|
||||
read-table)))))
|
||||
(values
|
||||
(if (get-keyword #:inside? args)
|
||||
(lambda (#!optional (port (current-input-port)))
|
||||
(read-inside-at-exp 'none port))
|
||||
(lambda (#!optional (port (current-input-port)))
|
||||
(read-at-exp port)))
|
||||
at-read-table)))
|
||||
|
||||
(define (make-at-reader . args)
|
||||
(nth-value 0 (make-at-reader+table args)))
|
||||
|
||||
(define (make-at-read-table . args)
|
||||
(nth-value 1 (make-at-reader+table args)))
|
||||
|
||||
(define (use-at-read-table . args)
|
||||
(current-read-table (nth-value 1 (make-at-reader+table args))))
|
||||
|
||||
;;; URI encoding
|
||||
|
||||
(define uri-encode
|
||||
(let ((problematic-rx (irregex '(~ (or alphanumeric "-._~")))))
|
||||
(lambda (s)
|
||||
(irregex-replace/all
|
||||
problematic-rx s
|
||||
(lambda (m)
|
||||
(string-append
|
||||
"%"
|
||||
(string-pad
|
||||
(number->string
|
||||
(char->integer (string-ref (irregex-match-substring m) 0)) 16)
|
||||
2 #\0)))))))
|
||||
|
||||
(define uri-decode
|
||||
(let ((escape-rx (irregex '(or #\+ (: #\% ($ (= 2 hex-digit)))))))
|
||||
(lambda (s)
|
||||
(irregex-replace/all
|
||||
escape-rx s
|
||||
(lambda (m)
|
||||
(case (string-ref s (irregex-match-start-index m))
|
||||
((#\+)
|
||||
" ")
|
||||
((#\%)
|
||||
(string
|
||||
(integer->char
|
||||
(string->number (irregex-match-substring m 1) 16))))))))))
|
||||
|
||||
;;; Base64URI encoding
|
||||
|
||||
(define base64-alphabet
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
|
||||
(define base64-alphabet/uri
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_")
|
||||
|
||||
(define (base64-encode s #!optional uri-safe?)
|
||||
(let* ((alphabet (if uri-safe? base64-alphabet/uri base64-alphabet))
|
||||
(n (string-length s))
|
||||
(e (make-string (inexact->exact (ceiling (* 4/3 n))))))
|
||||
(do ((is 0 (fx+ is 3)) (ie 0 (fx+ ie 4))) ((fx>= is n) e)
|
||||
(let ((i (fxior
|
||||
(fxshl (char->integer (string-ref s is)) 16)
|
||||
(if (fx< (fx+ is 1) n)
|
||||
(fxior
|
||||
(fxshl (char->integer (string-ref s (fx+ is 1))) 8)
|
||||
(if (fx< (fx+ is 2) n)
|
||||
(char->integer (string-ref s (fx+ is 2)))
|
||||
0))
|
||||
0))))
|
||||
(string-set!
|
||||
e ie
|
||||
(string-ref alphabet (fxand (fxshr i 18) #b111111)))
|
||||
(string-set!
|
||||
e (fx+ ie 1)
|
||||
(string-ref alphabet (fxand (fxshr i 12) #b111111)))
|
||||
(when (fx< (fx+ is 1) n)
|
||||
(string-set!
|
||||
e (fx+ ie 2)
|
||||
(string-ref alphabet (fxand (fxshr i 6) #b111111)))
|
||||
(when (fx< (fx+ is 2) n)
|
||||
(string-set!
|
||||
e (fx+ ie 3)
|
||||
(string-ref alphabet (fxand i #b111111)))))))))
|
||||
|
||||
(define base64-decode
|
||||
(let ((alphabet-ref
|
||||
(let* ((n (string-length base64-alphabet))
|
||||
(alphabet (make-hash-table eqv? eqv?-hash (fx+ n 2))))
|
||||
(do ((i 0 (fx+ i 1))) ((fx>= i n))
|
||||
(hash-table-set! alphabet (string-ref base64-alphabet i) i))
|
||||
(do ((i (fx- n 2) (fx+ i 1))) ((fx>= i n))
|
||||
(hash-table-set! alphabet (string-ref base64-alphabet/uri i) i))
|
||||
(lambda (chr)
|
||||
(hash-table-ref
|
||||
alphabet chr
|
||||
(cut syntax-error 'base64-decode "illegal character" chr))))))
|
||||
(lambda (e)
|
||||
(let* ((n (string-length e))
|
||||
(s (make-string (inexact->exact (floor (* 3/4 n))))))
|
||||
(do ((ie 0 (fx+ ie 4)) (is 0 (fx+ is 3))) ((fx>= ie n) s)
|
||||
(let ((i (fxior
|
||||
(fxshl
|
||||
(alphabet-ref (string-ref e ie)) 18)
|
||||
(if (fx< (fx+ ie 1) n)
|
||||
(fxior
|
||||
(fxshl
|
||||
(alphabet-ref (string-ref e (fx+ ie 1))) 12)
|
||||
(if (fx< (fx+ ie 2) n)
|
||||
(fxior
|
||||
(fxshl
|
||||
(alphabet-ref (string-ref e (fx+ ie 2))) 6)
|
||||
(if (fx< (fx+ ie 3) n)
|
||||
(alphabet-ref (string-ref e (fx+ ie 3)))
|
||||
0))
|
||||
0))
|
||||
0))))
|
||||
(string-set!
|
||||
s is (integer->char (fxand (fxshr i 16) #xff)))
|
||||
(when (fx< (fx+ ie 2) n)
|
||||
(string-set!
|
||||
s (fx+ is 1) (integer->char (fxand (fxshr i 8) #xff)))
|
||||
(when (fx< (fx+ ie 3) n)
|
||||
(string-set!
|
||||
s (fx+ is 2) (integer->char (fxand i #xff)))))))))))
|
||||
|
||||
;;; HTML output
|
||||
|
||||
(define write-html
|
||||
(letrec ((tag-rules
|
||||
(alist->hash-table
|
||||
'((area . void)
|
||||
(base . void)
|
||||
(br . void)
|
||||
(col . void)
|
||||
(command . void)
|
||||
(embed . void)
|
||||
(hr . void)
|
||||
(img . void)
|
||||
(input . void)
|
||||
(keygen . void)
|
||||
(link . void)
|
||||
(meta . void)
|
||||
(param . void)
|
||||
(source . void)
|
||||
(track . void)
|
||||
(wbr . void)
|
||||
(script . raw)
|
||||
(style . raw))
|
||||
#:test eq? #:hash eq?-hash))
|
||||
(problematic-rx
|
||||
(irregex '("\"&<>")))
|
||||
(html-escape
|
||||
(lambda (s)
|
||||
(irregex-replace/all
|
||||
problematic-rx s
|
||||
(lambda (m)
|
||||
(case (string-ref (irregex-match-substring m) 0)
|
||||
((#\") """)
|
||||
((#\&) "&")
|
||||
((#\<) "<")
|
||||
((#\>) ">"))))))
|
||||
(write-element
|
||||
(lambda (elt port)
|
||||
(unless (and (pair? elt) (symbol? (car elt)) (list? (cdr elt)))
|
||||
(error
|
||||
'write-html "not a proper element"
|
||||
elt))
|
||||
(let-values (((tag attributes+contents)
|
||||
(car+cdr elt)))
|
||||
(fprintf port "<~a" tag)
|
||||
(let-values (((rule)
|
||||
(hash-table-ref/default tag-rules tag 'normal))
|
||||
((attributes contents)
|
||||
(cond
|
||||
((null? attributes+contents)
|
||||
(values '() '()))
|
||||
((and (list? (car attributes+contents))
|
||||
(every list? (car attributes+contents)))
|
||||
(car+cdr attributes+contents))
|
||||
(else
|
||||
(values '() attributes+contents)))))
|
||||
(for-each (cut write-attribute <> port) attributes)
|
||||
(display #\> port)
|
||||
(case rule
|
||||
((normal)
|
||||
(for-each (cut write-content #t <> port) contents))
|
||||
((raw)
|
||||
(for-each (cut write-content #f <> port) contents))
|
||||
((void)
|
||||
(unless (null? contents)
|
||||
(error
|
||||
'write-html "void elements cannot have contents"
|
||||
elt))))
|
||||
(case rule
|
||||
((normal raw)
|
||||
(fprintf port "</~a>" tag)))))))
|
||||
(write-attribute
|
||||
(lambda (attr port)
|
||||
(unless (and (pair? attr) (symbol? (car attr)) (list? (cdr attr)))
|
||||
(error
|
||||
'write-html "not a proper attribute"
|
||||
attr))
|
||||
(let-values (((key contents) (car+cdr attr)))
|
||||
(fprintf port " ~a=\"" key)
|
||||
(for-each (cut write-content #f <> port) contents)
|
||||
(display #\" port))))
|
||||
(write-content
|
||||
(lambda (allow-elements? v port)
|
||||
(cond
|
||||
((symbol? v)
|
||||
(fprintf port "&~a;" v))
|
||||
((and (integer? v) (positive? v))
|
||||
(fprintf port "&#~a;" v))
|
||||
((string? v)
|
||||
(display (html-escape v) port))
|
||||
(allow-elements?
|
||||
(write-element v port))
|
||||
(else
|
||||
(error
|
||||
'write-html "element not allowed in this context"
|
||||
v))))))
|
||||
(lambda (html #!optional (port (current-output-port)))
|
||||
(display "<!DOCTYPE html>" port)
|
||||
(newline port)
|
||||
(write-element html port)
|
||||
(newline port))))
|
||||
|
||||
)
|
||||
;;This page was generated in about 0.011s by Fossil 2.24 [8be0372c10] 2024-04-23 13:25:26
|
||||
195
src/webgate.scm
Normal file
195
src/webgate.scm
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
;; -*- mode: Scheme; -*-
|
||||
;; https://chust.org/repos/chicken-webgate/file?name=webgate.scm&ci=tip
|
||||
|
||||
;;
|
||||
;; This file is part of WebGate for CHICKEN.
|
||||
;; Copyright (c) 2011-2013 by Thomas Chust. All rights reserved.
|
||||
;;
|
||||
;; Permission is hereby granted, free of charge, to any person
|
||||
;; obtaining a copy of this software and associated documentation
|
||||
;; files (the Software), to deal in the Software without restriction,
|
||||
;; including without limitation the rights to use, copy, modify,
|
||||
;; merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
;; Software, and to permit persons to whom the Software is furnished
|
||||
;; to do so, subject to the following conditions:
|
||||
;;
|
||||
;; The above copyright notice and this permission notice shall be
|
||||
;; included in all copies or substantial portions of the Software.
|
||||
;;
|
||||
;; THE SOFTWARE IS PROVIDED ASIS, WITHOUT WARRANTY OF ANY KIND,
|
||||
;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
;; BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
;; ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
;; SOFTWARE.
|
||||
|
||||
(require-library
|
||||
srfi-1 srfi-4 srfi-13 srfi-14 srfi-18 srfi-69 srfi-99
|
||||
data-structures ports extras lolevel irregex tcp posix
|
||||
suspension tweetnacl)
|
||||
|
||||
(module webgate-utils
|
||||
(compress decompress
|
||||
write-netstring read-netstring
|
||||
make-at-reader make-at-read-table use-at-read-table
|
||||
uri-encode uri-decode
|
||||
base64-encode base64-decode
|
||||
write-html)
|
||||
(import
|
||||
scheme chicken foreign
|
||||
srfi-1 srfi-13 srfi-14 srfi-69
|
||||
(except data-structures compress) extras irregex)
|
||||
(include
|
||||
"webgate-utils.scm"))
|
||||
|
||||
(module webgate-core
|
||||
(message make-message message?
|
||||
message-type message-headers message-body message-text
|
||||
write-message
|
||||
max-request-size
|
||||
request-method-handler
|
||||
request-body-handler
|
||||
request-parameter-handler
|
||||
parameter-list-ref parameter-ref
|
||||
resource-context current-resource-context resource-context?
|
||||
resource-context-getenv resource-context-method resource-context-path
|
||||
response make-response response?
|
||||
collect-response make-html-response make-error-response
|
||||
make-redirect-response
|
||||
response-status response-status-message
|
||||
write-response
|
||||
resource-handler resource-uri
|
||||
(define-resource resource-handler extend-procedure procedure-data)
|
||||
handle-query-parameters
|
||||
handle-request)
|
||||
(import
|
||||
scheme chicken
|
||||
srfi-1 srfi-4 srfi-13 srfi-18 srfi-69 srfi-99
|
||||
data-structures ports extras lolevel irregex
|
||||
suspension webgate-utils)
|
||||
(include
|
||||
"webgate-core.scm"))
|
||||
|
||||
(module webgate-suspend
|
||||
(current-suspension-key
|
||||
suspended
|
||||
send/suspend)
|
||||
(import
|
||||
scheme chicken
|
||||
srfi-1 srfi-4 srfi-13 srfi-18 srfi-69
|
||||
data-structures extras suspension tweetnacl webgate-utils webgate-core
|
||||
(only posix current-user-id current-group-id current-directory))
|
||||
(include
|
||||
"webgate-suspend.scm"))
|
||||
|
||||
(module webgate-cgi
|
||||
(cgi-main-loop)
|
||||
(import
|
||||
scheme chicken
|
||||
(only webgate-core write-response))
|
||||
(include
|
||||
"webgate-cgi.scm"))
|
||||
|
||||
(module webgate-scgi
|
||||
(scgi-main-loop)
|
||||
(import
|
||||
scheme chicken
|
||||
srfi-13 srfi-18 srfi-69
|
||||
data-structures irregex webgate-utils tcp
|
||||
(only webgate-core write-response))
|
||||
(include
|
||||
"webgate-scgi.scm"))
|
||||
|
||||
(cond-expand
|
||||
(enable-webgate-soup
|
||||
(module webgate-soup
|
||||
(soup-main-loop)
|
||||
(import
|
||||
scheme chicken foreign
|
||||
srfi-1 srfi-13
|
||||
data-structures webgate-core)
|
||||
(include
|
||||
"webgate-soup.scm")))
|
||||
(else))
|
||||
|
||||
(module webgate
|
||||
(webgate-main)
|
||||
(import
|
||||
scheme chicken
|
||||
srfi-13 irregex webgate-cgi webgate-scgi tcp
|
||||
(only webgate-core
|
||||
handle-request)
|
||||
(only webgate-suspend
|
||||
current-suspension-key))
|
||||
(cond-expand
|
||||
(enable-webgate-soup
|
||||
(import webgate-soup))
|
||||
(else))
|
||||
(reexport
|
||||
(only webgate-core
|
||||
message make-message message?
|
||||
message-type message-headers message-body message-text
|
||||
parameter-list-ref parameter-ref
|
||||
resource-context current-resource-context resource-context?
|
||||
resource-context-getenv resource-context-method resource-context-path
|
||||
response make-response response?
|
||||
collect-response make-html-response make-error-response
|
||||
make-redirect-response
|
||||
response-status response-status-message
|
||||
define-resource resource-uri)
|
||||
(only webgate-suspend
|
||||
send/suspend))
|
||||
|
||||
(define (webgate-main #!optional (arguments (command-line-arguments)))
|
||||
(apply
|
||||
(lambda (#!key (listen #f) (backlog 4) (suspension-key #f))
|
||||
(cond
|
||||
(suspension-key => current-suspension-key))
|
||||
(if listen
|
||||
(let ((m (irregex-match
|
||||
'(: (? ($ (+ (~ (":!")))) (":!")) (? ($ (+ (~ (":!")))) (":!")) ($ (+ num)))
|
||||
listen)))
|
||||
(if m
|
||||
(let* ((port
|
||||
(string->number (irregex-match-substring m 3)))
|
||||
(host
|
||||
(cond
|
||||
((irregex-match-substring m 2)
|
||||
=> (lambda (host) (if (string=? host "*") #f host)))
|
||||
(else
|
||||
"localhost")))
|
||||
(protocol
|
||||
(cond
|
||||
((irregex-match-substring m 1)
|
||||
=> string->symbol)
|
||||
(else
|
||||
'scgi))))
|
||||
(case protocol
|
||||
((scgi)
|
||||
(let ((ear (tcp-listen port backlog host)))
|
||||
(dynamic-wind
|
||||
void
|
||||
(cut scgi-main-loop handle-request ear)
|
||||
(cut tcp-close ear))))
|
||||
((http)
|
||||
(cond-expand
|
||||
(enable-webgate-soup
|
||||
(soup-main-loop handle-request port host))
|
||||
(else
|
||||
(error 'webgate-main "HTTP support not enabled"))))
|
||||
(else
|
||||
(error 'webgate-main "Unknown protocol" protocol))))
|
||||
(error 'webgate-main "Bad listener specification" listen)))
|
||||
(cgi-main-loop handle-request)))
|
||||
(map
|
||||
(lambda (arg)
|
||||
(if (string-prefix? "-" arg)
|
||||
(string->keyword (substring/shared arg 1))
|
||||
arg))
|
||||
arguments)))
|
||||
|
||||
)
|
||||
|
||||
;;This page was generated in about 0.012s by Fossil 2.24 [8be0372c10] 2024-04-23 13:25:26
|
||||
Loading…
Add table
Add a link
Reference in a new issue