From: Paul Hänsch Date: Sun, 25 Jul 2021 02:46:25 +0000 (+0200) Subject: Merge commit '280b6cc5ddcedd4f744806f965e0165fb0a8f0b2' X-Git-Url: https://git.plutz.net/?a=commitdiff_plain;h=ca8ca62aff28386cd74ef9e5f52d88b8f11fc477;hp=280b6cc5ddcedd4f744806f965e0165fb0a8f0b2;p=webtxt Merge commit '280b6cc5ddcedd4f744806f965e0165fb0a8f0b2' --- diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..24781a9 --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +.PHONY: _subtrees + +_subtrees: _cgilite + +cgilite: + git subtree add --squash -P $@ https://git.plutz.net/git/$@ master + +_cgilite: cgilite + git subtree pull --squash -P $< https://git.plutz.net/git/$< master diff --git a/cgilite.sh b/cgilite/cgilite.sh similarity index 100% rename from cgilite.sh rename to cgilite/cgilite.sh diff --git a/common.css b/cgilite/common.css similarity index 100% rename from common.css rename to cgilite/common.css diff --git a/file.sh b/cgilite/file.sh similarity index 100% rename from file.sh rename to cgilite/file.sh diff --git a/html-sh.sed b/cgilite/html-sh.sed similarity index 100% rename from html-sh.sed rename to cgilite/html-sh.sed diff --git a/logging.sh b/cgilite/logging.sh similarity index 100% rename from logging.sh rename to cgilite/logging.sh diff --git a/markdown.awk b/cgilite/markdown.awk similarity index 100% rename from markdown.awk rename to cgilite/markdown.awk diff --git a/session.sh b/cgilite/session.sh similarity index 100% rename from session.sh rename to cgilite/session.sh diff --git a/storage.sh b/cgilite/storage.sh similarity index 100% rename from storage.sh rename to cgilite/storage.sh diff --git a/index.cgi b/index.cgi new file mode 100755 index 0000000..f2e065e --- /dev/null +++ b/index.cgi @@ -0,0 +1,132 @@ +#!/bin/sh + +_EXEC="${_EXEC:-${0%/*}/}" +_DATA="${_DATA:-.}" + +. "$_EXEC"/cgilite/cgilite.sh +. "$_EXEC"/cgilite/session.sh +. "$_EXEC"/cgilite/file.sh +. "$_EXEC"/session_lock.sh + +git init "$_DATA" >/dev/null & + +export MD_HTML="false" +markdown() { + local awkbin=`which awk` + if [ "$awkbin" ]; then + "$awkbin" -f "$_EXEC"/cgilite/markdown.awk + else + busybox awk -f "$_EXEC"/cgilite/markdown.awk + fi +} + +yield_page(){ + title="${1:-WebTXT}" page="$2" + printf '%s\r\n' 'Content-Type: text/html; charset=utf-8' \ + "Content-Security-Policy: script-src 'none'" \ + '' + { printf '[html + [head + [meta name="viewport" content="width=device-width"] + [link rel="stylesheet" type="text/css" href="common.css"] + [link rel="stylesheet" type="text/css" href="webnote.css"] + [title %s] + ] [body class="%s" + ' "$title" "$page" + cat + printf '] ]' + } |"$_EXEC/cgilite/html-sh.sed" -u +} + +pagename() { + local name page="${1##*/}" + name="$(sed -nE "/[a-zA-Z0-9].[^\r ]/{s;\r$;;; p; q;};" "$_DATA/$page")" + [ "$name" ] && printf %s "$name" \ + || printf %s "$page" +} + +recent="$(COOKIE pages)" + +case ${PATH_INFO##*/} in + favicon.ico) printf '%s\r\n' 'Content-Length: 0' '';; + common.css) FILE "$_EXEC/cgilite/common.css";; + webnote.css) FILE "$_EXEC/webnote.css";; + '') yield_page <<-EOF + [form .new action=new [button type=submit New Note] + $(if [ "$recent" ]; then + printf '[h2 Recent Pages][ul .recent' + for page in $recent; do + [ -f "$_DATA/$(printf %s "$page" |checkid)" ] \ + && printf '[li [a href="./%s" . %s]]' "$page" "$(pagename "$page" |HTML)" + done + printf ']' + fi) + ] + EOF + return 0 + ;; + new) + newid="$(timeid)" + touch "$_DATA/$newid" + git -C "$_DATA" add "$newid" >/dev/null + git -C "$_DATA" commit -m "new note \"${newid}\"" "$newid" >/dev/null + REDIRECT "./$newid" + ;; +esac + +doc="$_DATA/$(printf %s ${PATH_INFO##*/} |checkid)" + +if [ ! -f "$doc" ]; then + REDIRECT "${PATH_INFO%/*}/" + return 0 +fi + +[ $REQUEST_METHOD = POST ] && case $(POST action) in + edit) + if temp=$(SLOCK "$doc"); then + yield_page "$(pagename "$doc" |HTML)" <<-EOF + [form method=POST + [input type=hidden name=session_key value="$SESSION_KEY"] + [button type=submit name=action value=cancel Cancel] + [button type=submit name=action value=update Update] + [textarea name=document . $(HTML <"$doc")] + ] + EOF + else + yield_page "$(pagename "$doc" |HTML) - Error" <<-EOF + [p .error .locked Someone else is already editing this Dokument. Wait a few minutes and try again. [a href="$PATH_INFO" Nothing else I can do.]] + EOF + fi + return 0 + ;; + cancel) + RELEASE_SLOCK "$doc" + REDIRECT "$PATH_INFO" + ;; + update) + if temp=$(CHECK_SLOCK "$doc"); then + RELEASE_SLOCK "$doc" + POST document >"${doc}" + git -C "$_DATA" commit -m "update note ${doc##*/} \"$(pagename "$doc")\"" "${doc##*/}" >/dev/null + REDIRECT "$PATH_INFO" + else + yield_page "$(pagename "$doc" |HTML) - Error" <<-EOF + [p .error .stolen Your edit took too long and someone else is now editing this file. [a href="$PATH_INFO" Dang, I must be quicker next time!]] + [p Copy your Content for reference:] + [div .text . $(POST document |HTML)] + EOF + fi + return 0 + ;; +esac + +printf %s "$recent" |grep -qwF "${doc##*/}" \ +|| SET_COOKIE +$((90 * 86400)) pages="${recent}${recent+ }${doc##*/}" Path="${PATH_INFO%/*}/" + +yield_page "$(pagename "$doc" |HTML)" <<-EOF + [form method=POST + [input type=hidden name=session_key value="$SESSION_KEY"] + [button type=submit name=action value=edit Edit] + ] + [div .text . $(markdown <"$doc")] + EOF diff --git a/session_lock.sh b/session_lock.sh new file mode 100644 index 0000000..de1641a --- /dev/null +++ b/session_lock.sh @@ -0,0 +1,60 @@ +#!/bin/sh + +[ "$include_session_lock" ] && return 0 +include_session_lock="$0" + +SLOCK(){ + local file="$1"; + local timeout="${2-900}" + local lockdir="$_DATA/lock/${file#$_DATA}"; lockdir="${lockdir%/}" + local ovlock="${lockdir%/*}/delete.${lockdir##*/}" + local tempfile="$lockdir/${SESSION_ID}" + local lockexpire=$(( $(date +%s) - timeout )) + + mkdir -p "${lockdir%/*}" + + if [ -e "$lockdir" ] \ + && [ "$(stat -c %Y "$lockdir")" -lt "$lockexpire" ] \ + && mkdir "$ovlock"; then + [ "$(stat -c %Y "$lockdir")" -lt "$lockexpire" ] \ + && rm -r "$lockdir" + rmdir "$ovlock" + fi + + printf '%s\n' "$tempfile" + if mkdir "$lockdir" 2>&-; then + cp "$file" "$tempfile" + return 0 + else + return 1 + fi +} + +CHECK_SLOCK(){ + local file="$1"; + local lockdir="$_DATA/lock/${file#$_DATA}"; lockdir="${lockdir%/}" + local tempfile="$lockdir/${SESSION_ID}" + + printf '%s\n' "$tempfile" + if [ -f "$tempfile" ]; then + touch "$lockdir" + return 0 + else + return 1 + fi +} + +RELEASE_SLOCK(){ + local file="$1"; + local lockdir="$_DATA/lock/${file#$_DATA}"; lockdir="${lockdir%/}" + local ovlock="${lockdir%/*}/delete.${lockdir##*/}" + local tempfile="$lockdir/${SESSION_ID}" + + if [ -f "$tempfile" ] && mkdir "$ovlock"; then + [ -f "$tempfile" ] && rm -r "$lockdir" + rmdir "$ovlock" + return 0 + else + return 1 + fi +} diff --git a/webnote.css b/webnote.css new file mode 100644 index 0000000..484cbc7 --- /dev/null +++ b/webnote.css @@ -0,0 +1,35 @@ +body { + margin: .5em 1em; +} +button { + margin-bottom: 1em; +} +@media print { button { display: none; }} +textarea { + display: block; + font-family: monospace; + width: 100%; + height: 90vh; + height: calc(100vh - 4.5em); +} + +div.text { + max-width: 100%; + overflow: auto; +} + +form.new { + text-align: center; + margin-top: 1em; +} + +.new button { + display: block; + margin-left: auto; + margin-right: auto; +} + +ul.recent { + display: inline-block; + text-align: left; +}