From 51f6906bca7e34cdedca01e024f6cedb1c9e3ecd Mon Sep 17 00:00:00 2001 From: =?utf8?q?Paul=20H=C3=A4nsch?= Date: Mon, 19 Feb 2024 02:13:54 +0100 Subject: [PATCH 01/16] updated copyright notices --- file.sh | 2 +- users.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/file.sh b/file.sh index 8d810b5..c66b17d 100755 --- a/file.sh +++ b/file.sh @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright 2016 - 2023 Paul Hänsch +# Copyright 2016 - 2024 Paul Hänsch # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above diff --git a/users.sh b/users.sh index 4d7965a..18fde07 100755 --- a/users.sh +++ b/users.sh @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright 2021 - 2023 Paul Hänsch +# Copyright 2021 - 2024 Paul Hänsch # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above -- 2.39.2 From 8f729b0b376f33077fe9e209ce136c1adbb706fa Mon Sep 17 00:00:00 2001 From: =?utf8?q?Paul=20H=C3=A4nsch?= Date: Thu, 22 Feb 2024 17:52:13 +0100 Subject: [PATCH 02/16] db23 - simple in-memory key-value DB api --- db23.sh | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100755 db23.sh diff --git a/db23.sh b/db23.sh new file mode 100755 index 0000000..a3a25bb --- /dev/null +++ b/db23.sh @@ -0,0 +1,114 @@ +#!/bin/sh + +# Copyright 2023, 2024 Paul Hänsch +# +# 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. + +[ -n "$include_db23" ] && return 0 +include_db23="$0" + +. "$_EXEC/cgilite/storage.sh" + +DB2() { + local call data file key val seq + data="${BR}${1}${BR}" call="$2" + shift 2 + + case $call in + new|discard) + printf '' + ;; + open|load) file="$1" + cat "$file" || return 1 + ;; + check|contains) key="$(STRING "$1")" val='' + val="${data##*"${BR}${key}" }" val="${val%%"${BR}"*}" + [ "$val" = '' ] && return 1 + ;; + count) key="$(STRING "$1")" val='' seq=0 + val="${data##*"${BR}${key}" }" val="${val%%"${BR}"*}" + [ "$val" = '' ] || val="${val} " + while [ "$val" != '' ]; do + seq=$((seq + 1)) val="${val#* }" + done + printf "%i\n" "$seq" + [ $seq = 0 ] && return 1 + ;; + get) key="$(STRING "$1")" seq="${2:-1}" + val="${data##*"${BR}${key}" }" val="${val%%"${BR}"*}" + [ "$val" = '' ] && return 1 || val="${val} " + while [ $seq -gt 1 ]; do + seq=$((seq - 1)) val="${val#* }" + done + [ "$val" = '' ] && return 1 + UNSTRING "${val%% *}" + ;; + iterate|raw) key="$(STRING "$1")" + val="${data##*"${BR}${key}" }" val="${val%%"${BR}"*}" + [ "$val" = '' ] && return 1 + printf '%s\n' $val + ;; + delete|remove) key="$(STRING "$1")" + val="${data#*"${BR}${key}" *"${BR}"}" + key="${data%"${BR}${key}" *"${BR}"*}" + [ "${key}${BR}${val}" = "${data}" ] && return 1 + printf '%s' "${key#"${BR}"}${BR}${val%"${BR}"}" + ;; + set|store) key="$(STRING "$1")" val="" + shift 1 + val="$(for v in "$@"; do STRING "$v"; printf \\t; done)" + if [ "${data#*"${BR}${key}" *}" != "$data" ]; then + data="${data%"${BR}${key}" *"${BR}"*}${BR}${key} ${val% }${BR}${data#*"${BR}${key}" *"${BR}"}" + data="${data#"${BR}"}" data="${data%"${BR}"}" + else + data="${data#"${BR}"}${key} ${val% }${BR}" + data="${data#"${BR}"}" + fi + printf %s\\n "${data}" + ;; + append) key="$(STRING "$1")" val="" + val="${data##*"${BR}${key}" }" val="${val%%"${BR}"*}" + if [ "$val" = '' ]; then + printf %s\\n "${data}" + return 1 + else + shift 1 + val="${val}$(for v in "$@"; do printf \\t; STRING "$v"; done)" + data="${data%"${BR}${key}" *"${BR}"*}${BR}${key} ${val% }${BR}${data#*"${BR}${key}" *"${BR}"}" + data="${data#"${BR}"}" data="${data%"${BR}"}" + printf %s\\n "${data}" + fi + ;; + flush|save|write) file="$1" + data="${data#"${BR}"}" data="${data%"${BR}"}" + printf '%s\n' "$data" >"$file" || return 1 + ;; + esac + return 0 +} + +DB3() { + # wrapper function that allows easyer use of DB2 + # by always keeping file data in $db3_data + + case "$1" in + new|discard|open|load|delete|remove|set|store|append) + db3_data="$(DB2 "$db3_data" "$@")" + return "$?" + ;; + get|count|check|contains|iterate|raw|flush|save|write) + DB2 "$db3_data" "$@" + return "$?" + ;; + esac +} -- 2.39.2 From 6ab69002e5b686ee4dacdc5a64d620d7361fb070 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Paul=20H=C3=A4nsch?= Date: Sat, 24 Feb 2024 11:24:38 +0100 Subject: [PATCH 03/16] more reliable include path --- db23.sh | 2 +- users.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/db23.sh b/db23.sh index a3a25bb..e8a0d64 100755 --- a/db23.sh +++ b/db23.sh @@ -17,7 +17,7 @@ [ -n "$include_db23" ] && return 0 include_db23="$0" -. "$_EXEC/cgilite/storage.sh" +. "${_EXEC:-.}/cgilite/storage.sh" DB2() { local call data file key val seq diff --git a/users.sh b/users.sh index 18fde07..f60f8a6 100755 --- a/users.sh +++ b/users.sh @@ -17,8 +17,8 @@ [ -n "$include_users" ] && return 0 include_users="$0" -. "${_EXEC}/cgilite/session.sh" -. "${_EXEC}/cgilite/storage.sh" +. "${_EXEC:-.}/cgilite/session.sh" +. "${_EXEC:-.}/cgilite/storage.sh" SENDMAIL=${SENDMAIL-sendmail} -- 2.39.2 From b8a42461bbd21d42576c58ae807f25ac354edfb8 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Paul=20H=C3=A4nsch?= Date: Sat, 24 Feb 2024 12:54:22 +0100 Subject: [PATCH 04/16] read json data into recursive DB2 structure --- json.sh | 236 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100755 json.sh diff --git a/json.sh b/json.sh new file mode 100755 index 0000000..99e1e74 --- /dev/null +++ b/json.sh @@ -0,0 +1,236 @@ +#!/bin/sh + +# [ -n "$include_json" ] && return 0 +# include_json="$0" + +. "${_EXEC:-.}/cgilite/db23.sh" + +json_except() { + printf '%s\n' "$@" >&2; + printf 'Exc: %s\n' "$json_document" >&2 +} + +json_space() { + while true; do case "$json_document" in + [" ${BR}${CR} "]*) json_document="${json_document#?}";; + *) break ;; + esac; done +} + +json_string() { + local string json_document="$json_document" end=0 + + json_space + case $json_document in + \"*) json_document="${json_document#?}" + ;; + *) json_except "Expected string specifyer starting with (\")" + return 1 + ;; + esac + while [ "$json_document" ]; do case $json_document in + \\?*) + string="${string}${json_document%"${json_document#??}"}" + json_document="${json_document#??}" + ;; + \"*) + json_document="${json_document#?}" + end=1 + break + ;; + *) + string="${string}${json_document%"${json_document#?}"}" + json_document="${json_document#?}" + ;; + esac; done + + if [ $end -eq 0 ]; then + json_except "Document ended mid-string" + return 1 + fi + + printf "%s %s\n" "$(STRING "$string")" "$json_document" +} + +json_key() { + local key json_document="$json_document" + + json_space + case $json_document in + \"*) + key="$(json_string)" || return 1 + json_document="${key#* }" + key="${key%% *}" + ;; + *) json_except "Expected key specifyer starting with '\"'" + return 1 + ;; + esac + json_space + case $json_document in + :*) json_document="${json_document#?}" + ;; + *) json_except "Expected value separator \":\"" + return 1 + ;; + esac + + printf '%s %s\n' "$key" "$json_document" +} + +json_number() { + local number json_document="$json_document" + + json_space + number="${json_document%%[" ${BR}${CR} ,}]"]*}" + json_document="${json_document#"$number"}" + if ! number="$(printf %f "$number")"; then + json_except "Invalid number format" + return 1 + fi + + printf '%s %s\n' "num:${number%.000000}" "$json_document" +} + +json_value() { + local value json_document="$json_document" + + json_space + case $json_document in + \"*) + value="$(json_string)" || return 1 + json_document="${value#* }" + value="str:${value%% *}" + ;; + [+-.0-9]*) + value="$(json_number)" || return 1 + json_document="${value#* }" + value="${value%% *}" + ;; + "{"*) + value="$(json_object)" || return 1 + json_document="${value#* }" + value="obj:${value%% *}" + ;; + "["*) + value="$(json_array)" || return 1 + json_document="${value#* }" + value="arr:${value%% *}" + ;; + null*) + json_document="${json_document#null}" + value="null" + ;; + true*) + json_document="${json_document#true}" + value="true" + ;; + false*) + json_document="${json_document#false}" + value="false" + ;; + esac + + printf "%s %s\n" "$value" "$json_document" +} + +json_array() { + local struct="$(DB2 new)" value json_document="$json_document" + + json_space + case $json_document in + "["*) json_document="${json_document#?}" + ;; + *) json_except "Expected array starting with \"[\"" + return 1 + ;; + esac + + json_space + case $json_document in + "]"*) + printf "%s %s\n" "" "${json_document#?}" + return 0 + ;; + esac + + while :; do + json_space + + value="$(json_value)" || return 1 + json_document="${value#* }" + value="$(UNSTRING "${value%% *}")" + + struct="$(DB2 "$struct" append "@" "$value")" \ + || struct="$(DB2 "$struct" set "@" "$value")" + + json_space + case $json_document in + ,*) json_document="${json_document#?}" + ;; + "]"*) json_document="${json_document#?}" + break + ;; + *) json_except "Unexpected character mid-array" + return 1 + ;; + esac + done + + printf "%s %s\n" "$(STRING "$struct")" "$json_document" +} + +json_object() { + local struct="$(DB2 new)" key value json_document="$json_document" + + json_space + case $json_document in + "{"*) json_document="${json_document#?}" + ;; + *) json_except "Expected object starting with \"{\"" + return 1 + ;; + esac + + json_space + case $json_document in + "}"*) + printf "%s %s\n" "" "${json_document#?}" + return 0 + ;; + esac + + while :; do + json_space + + key="$(json_key)" || return 1 + json_document="${key#* }" + key="$(UNSTRING "${key%% *}")" + + value="$(json_value)" || return 1 + json_document="${value#* }" + value="$(UNSTRING "${value%% *}")" + + struct="$(DB2 "$struct" set "$key" "$value")" + + json_space + case $json_document in + ,*) json_document="${json_document#?}" + ;; + "}"*) json_document="${json_document#?}" + break + ;; + *) json_except "Unexpected character mid-object" + return 1 + ;; + esac + done + + printf "%s %s\n" "$(STRING "$struct")" "$json_document" +} + +json_load() { + local json_document="$1" + + json_value +} -- 2.39.2 From 0a8a8519620984650a4354db3f3447891cc74aac Mon Sep 17 00:00:00 2001 From: =?utf8?q?Paul=20H=C3=A4nsch?= Date: Sun, 25 Feb 2024 16:49:44 +0100 Subject: [PATCH 05/16] get json values via jpath --- json.sh | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/json.sh b/json.sh index 99e1e74..43026be 100755 --- a/json.sh +++ b/json.sh @@ -5,6 +5,8 @@ . "${_EXEC:-.}/cgilite/db23.sh" +debug(){ [ $# -gt 0 ] && printf '%s\n' "$@" >&2 || tee -a /dev/stderr; } + json_except() { printf '%s\n' "$@" >&2; printf 'Exc: %s\n' "$json_document" >&2 @@ -94,6 +96,7 @@ json_number() { json_value() { local value json_document="$json_document" + json_type="" json_space case $json_document in @@ -101,33 +104,40 @@ json_value() { value="$(json_string)" || return 1 json_document="${value#* }" value="str:${value%% *}" + json_type=string ;; [+-.0-9]*) value="$(json_number)" || return 1 json_document="${value#* }" value="${value%% *}" + json_type=number ;; "{"*) value="$(json_object)" || return 1 json_document="${value#* }" value="obj:${value%% *}" + json_type=object ;; "["*) value="$(json_array)" || return 1 json_document="${value#* }" value="arr:${value%% *}" + json_type=array ;; null*) json_document="${json_document#null}" value="null" + json_type=null ;; true*) json_document="${json_document#true}" value="true" + json_type=boolean ;; false*) json_document="${json_document#false}" value="false" + json_type=boolean ;; esac @@ -234,3 +244,56 @@ json_load() { json_value } + +json_get() { + local json="$1" jpath="${2#.}" id idx + json_type='' + + case $json in + str:*) json_type="string";; + arr:*) json_type="array";; + obj:*) json_type="object";; + num:*) json_type="number";; + true|false) + json_type="boolean";; + null) json_type="null";; + esac + json="${json#???:}" + + case $jpath in + "") + printf %s\\n "$json" + return 0 + ;; + "["[0-9]*"]"*) + idx="${jpath%%"]"*}" idx="${idx#"["}" + jpath="${jpath#"["*"]"}" + ;; + "['"*"']"*) + id="${jpath%%"']"*}" id="${id#"['"}" + jpath="${jpath#"['"*"']"}" + ;; + *) id="${jpath%%[".["]*}" + jpath="${jpath#"$id"}" + ;; + esac + + if [ "$id" -a "$json_type" = object ]; then + # if ! json="$(DB2 "$(UNSTRING "$json")" get "$id")"; then + if ! json="$(DB2 "$json" get "$id")"; then + debug "No key: \"$id\"" + return 1 + fi + elif [ "$idx" -a "$json_type" = array ]; then + if ! json="$(DB2 "$(UNSTRING "$json")" get @ "$(( idx + 1 ))")"; then + # if ! json="$(DB2 "$json" get @ "$(( idx + 1 ))")"; then + debug "No array index: \"$idx\"" + return 1 + fi + else + debug "Value type missmatch" + return 1 + fi + json_get "$json" "$jpath" + return $? +} -- 2.39.2 From e1c0b4bed53401e2dfb5bfd3c561587db7e0e212 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Paul=20H=C3=A4nsch?= Date: Sun, 25 Feb 2024 19:28:04 +0100 Subject: [PATCH 06/16] more consistent db2/json structure and jpath selector --- json.sh | 145 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 74 insertions(+), 71 deletions(-) diff --git a/json.sh b/json.sh index 43026be..ba1539a 100755 --- a/json.sh +++ b/json.sh @@ -1,11 +1,11 @@ #!/bin/sh -# [ -n "$include_json" ] && return 0 -# include_json="$0" +[ -n "$include_json" ] && return 0 +include_json="$0" . "${_EXEC:-.}/cgilite/db23.sh" -debug(){ [ $# -gt 0 ] && printf '%s\n' "$@" >&2 || tee -a /dev/stderr; } +# debug(){ [ $# -gt 0 ] && printf '%s\n' "$@" >&2 || tee -a /dev/stderr; } json_except() { printf '%s\n' "$@" >&2; @@ -91,57 +91,7 @@ json_number() { return 1 fi - printf '%s %s\n' "num:${number%.000000}" "$json_document" -} - -json_value() { - local value json_document="$json_document" - json_type="" - - json_space - case $json_document in - \"*) - value="$(json_string)" || return 1 - json_document="${value#* }" - value="str:${value%% *}" - json_type=string - ;; - [+-.0-9]*) - value="$(json_number)" || return 1 - json_document="${value#* }" - value="${value%% *}" - json_type=number - ;; - "{"*) - value="$(json_object)" || return 1 - json_document="${value#* }" - value="obj:${value%% *}" - json_type=object - ;; - "["*) - value="$(json_array)" || return 1 - json_document="${value#* }" - value="arr:${value%% *}" - json_type=array - ;; - null*) - json_document="${json_document#null}" - value="null" - json_type=null - ;; - true*) - json_document="${json_document#true}" - value="true" - json_type=boolean - ;; - false*) - json_document="${json_document#false}" - value="false" - json_type=boolean - ;; - esac - - printf "%s %s\n" "$value" "$json_document" + printf '%s %s\n' "${number%.000000}" "$json_document" } json_array() { @@ -239,14 +189,64 @@ json_object() { printf "%s %s\n" "$(STRING "$struct")" "$json_document" } +json_value() { + local value json_document="$json_document" + json_type="" + + json_space + case $json_document in + \"*) + value="$(json_string)" || return 1 + json_document="${value#* }" + value="str:${value%% *}" + json_type=string + ;; + [+-.0-9]*) + value="$(json_number)" || return 1 + json_document="${value#* }" + value="num:${value%% *}" + json_type=number + ;; + "{"*) + value="$(json_object)" || return 1 + json_document="${value#* }" + value="obj:${value%% *}" + json_type=object + ;; + "["*) + value="$(json_array)" || return 1 + json_document="${value#* }" + value="arr:${value%% *}" + json_type=array + ;; + null*) + json_document="${json_document#null}" + value="null" + json_type=null + ;; + true*) + json_document="${json_document#true}" + value="true" + json_type=boolean + ;; + false*) + json_document="${json_document#false}" + value="false" + json_type=boolean + ;; + esac + + printf "%s %s\n" "$value" "$json_document" +} + json_load() { - local json_document="$1" + local json_document="$1" json - json_value + json_value |UNSTRING } json_get() { - local json="$1" jpath="${2#.}" id idx + local json="$1" jpath="${2#.}" key idx json_type='' case $json in @@ -258,11 +258,10 @@ json_get() { json_type="boolean";; null) json_type="null";; esac - json="${json#???:}" case $jpath in "") - printf %s\\n "$json" + printf %s\\n "${json#???:}" return 0 ;; "["[0-9]*"]"*) @@ -270,28 +269,32 @@ json_get() { jpath="${jpath#"["*"]"}" ;; "['"*"']"*) - id="${jpath%%"']"*}" id="${id#"['"}" + key="${jpath%%"']"*}" key="${key#"['"}" jpath="${jpath#"['"*"']"}" ;; - *) id="${jpath%%[".["]*}" - jpath="${jpath#"$id"}" + "$"*) + jpath="${jpath#?}" + ;; + *) key="${jpath%%[".["]*}" + jpath="${jpath#"$key"}" ;; esac - if [ "$id" -a "$json_type" = object ]; then - # if ! json="$(DB2 "$(UNSTRING "$json")" get "$id")"; then - if ! json="$(DB2 "$json" get "$id")"; then - debug "No key: \"$id\"" + if [ "$key" -a "$json_type" = object ]; then + if ! json="$(DB2 "${json#obj:}" get "$key")"; then + debug "Key not found: \"$key\"" return 1 fi elif [ "$idx" -a "$json_type" = array ]; then - if ! json="$(DB2 "$(UNSTRING "$json")" get @ "$(( idx + 1 ))")"; then - # if ! json="$(DB2 "$json" get @ "$(( idx + 1 ))")"; then - debug "No array index: \"$idx\"" + if ! json="$(DB2 "${json#arr:}" get @ "$(( idx + 1 ))")"; then + debug "Array index not found: \"$idx\"" return 1 fi - else - debug "Value type missmatch" + elif [ "$key" ]; then + debug "Cannot select key (\"$key\") from value of type \"$json_type\"" + return 1 + elif [ "$idx" ]; then + debug "Cannot select index ($idx) from value of type \"$json_type\"" return 1 fi json_get "$json" "$jpath" -- 2.39.2 From e7fcaf2f6bd62868cce4dab41056ee2597c47d58 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Paul=20H=C3=A4nsch?= Date: Mon, 26 Feb 2024 01:47:53 +0100 Subject: [PATCH 07/16] json export function --- json.sh | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/json.sh b/json.sh index ba1539a..f43f8ec 100755 --- a/json.sh +++ b/json.sh @@ -300,3 +300,61 @@ json_get() { json_get "$json" "$jpath" return $? } + +json_dump_string() { + local in="$1" out='' + while [ "$in" ]; do case $in in + \\*) out="${out}\\\\"; in="${in#\\}" ;; + "$BR"*) out="${out}\\n"; in="${in#${BR}}" ;; + "$CR"*) out="${out}\\r"; in="${in#${CR}}" ;; + " "*) out="${out}\\t"; in="${in# }" ;; + \"*) out="${out}\\\""; in="${in#\"}" ;; + *) out="${out}${in%%[\\${CR}${BR} \"]*}"; in="${in#"${in%%[\\${BR}${CR} \"]*}"}" ;; + esac; done + printf '"%s"' "${out}" +} + +json_dump_array() { + local json="$1" value out='' + + for value in $(DB2 "$json" iterate @); do + out="${out},$(json_dump "$(UNSTRING "$value")")" + done + printf '[%s]' "${out#,}" +} + +json_dump_object() { + local json="$1" key value out='' + + while read -r key value; do + out="${out},$(json_dump_string "$(UNSTRING "$key")"):$(json_dump "$(UNSTRING "$value")")" + done <<-EOF + ${json} + EOF + printf '{%s}' "${out#,}" +} + +json_dump() { + local json="$1" + + case $json in + str:*) + json_dump_string "${json#str:}" + ;; + arr:*) + json_dump_array "${json#arr:}" + ;; + obj:*) + json_dump_object "${json#obj:}" + ;; + num:*) + printf "${json#num:}" + ;; + true|false|null) + printf %s\\n "$json" + ;; + *) + json_dump_string "${json}" + ;; + esac +} -- 2.39.2 From 0591b0842c75fd078d19e488f5e4a8d047052551 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Paul=20H=C3=A4nsch?= Date: Mon, 26 Feb 2024 17:21:53 +0100 Subject: [PATCH 08/16] Bugfix: faulty email address check --- users.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/users.sh b/users.sh index f60f8a6..be68a8a 100755 --- a/users.sh +++ b/users.sh @@ -274,7 +274,7 @@ user_register(){ fi if [ "$USER_REQUIREEMAIL" = true ]; then - if [ ! "email" ]; then + if [ ! "$email" ]; then REDIRECT "${_BASE}${PATH_INFO}#ERROR_EMAIL_INVALID" elif user_emailexist "$email"; then REDIRECT "${_BASE}${PATH_INFO}#ERROR_EMAIL_EXISTS" @@ -342,7 +342,7 @@ user_invite(){ local email="$(POST email |user_checkemail)" local message="$(POST message)" - if [ ! "email" ]; then + if [ ! "$email" ]; then REDIRECT "${_BASE}${PATH_INFO}#ERROR_EMAIL_INVALID" elif user_emailexist "$email"; then REDIRECT "${_BASE}${PATH_INFO}#ERROR_EMAIL_EXISTS" -- 2.39.2 From 426dac52b5648fea4854fb141452d113b5c2251a Mon Sep 17 00:00:00 2001 From: =?utf8?q?Paul=20H=C3=A4nsch?= Date: Tue, 27 Feb 2024 22:50:00 +0100 Subject: [PATCH 09/16] fix auto detection of sender address --- users.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/users.sh b/users.sh index be68a8a..c924789 100755 --- a/users.sh +++ b/users.sh @@ -29,9 +29,8 @@ USER_ACCOUNTPAGE="${USER_ACCOUNTPAGE}" USER_ACCOUNTEXPIRE="${USER_ACCOUNTEXPIRE:-$((86400 * 730))}" USER_CONFIRMEXPIRE="${USER_CONFIRMEXPIRE:-86400}" -MAILFROM="${MAILDOMAIN-noreply@${HTTP_HOST%:*}}" - HTTP_HOST="$(HEADER Host)" +MAILFROM="noreply@${HTTP_HOST%:*}" [ "$HTTPS" ] && SCHEMA=https || SCHEMA=http -- 2.39.2 From d7b1281dbec20a236a14bd0fa8771da1ce353d00 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Paul=20H=C3=A4nsch?= Date: Mon, 4 Mar 2024 18:29:25 +0100 Subject: [PATCH 10/16] bugfix: incorrect call of `DB2 "" new` --- json.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/json.sh b/json.sh index f43f8ec..12afdc4 100755 --- a/json.sh +++ b/json.sh @@ -95,7 +95,7 @@ json_number() { } json_array() { - local struct="$(DB2 new)" value json_document="$json_document" + local struct="$(DB2 "" new)" value json_document="$json_document" json_space case $json_document in @@ -141,7 +141,7 @@ json_array() { } json_object() { - local struct="$(DB2 new)" key value json_document="$json_document" + local struct="$(DB2 "" new)" key value json_document="$json_document" json_space case $json_document in -- 2.39.2 From 58aab92616a0f59ce402868c1dcfe97dc0d3b87f Mon Sep 17 00:00:00 2001 From: =?utf8?q?Paul=20H=C3=A4nsch?= Date: Sat, 16 Mar 2024 21:26:25 +0100 Subject: [PATCH 11/16] awk port of some cgilite functions --- cgilite.awk | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 cgilite.awk diff --git a/cgilite.awk b/cgilite.awk new file mode 100644 index 0000000..0a1e1ca --- /dev/null +++ b/cgilite.awk @@ -0,0 +1,158 @@ +#!/bin/env awk -f + +function PATH( str, seg, out ) { + while ( str ) { + seg = str; + sub( /\/.*$/, "", seg); + sub( /^[^\/]*\//, "", str); + + if ( seg == ".." ) sub(/\/[^\/]*\/?$/, "", out); + else if ( seg ~ /^\.?$/) sub(/\/?$/, "/", out); + else sub(/\/?$/, "/" seg, out); + + if (seg == str) break; + } + if (!(str && out)) sub(/\/?$/,"/" out); + return out; +} + +function HEX_DECODE( pfx, inp, out, n, k ) { + k = length(pfx); + gsub(/[].*+?^${}()|\\[]/,"\\\\&",pfx); + while ( inp ) if ( n = match(inp, pfx "[0-9a-fA-F][0-9a-fA-F]") ) { + out = out substr(inp, 1, n - 1); + inp = substr(inp, n + k); + if (inp ~ /^[0-9]/) n = 16 * substr(inp, 1, 1); + else if (inp ~ /^[aA]/) n = 160; + else if (inp ~ /^[bB]/) n = 176; + else if (inp ~ /^[cC]/) n = 192; + else if (inp ~ /^[dD]/) n = 208; + else if (inp ~ /^[eE]/) n = 224; + else if (inp ~ /^[fF]/) n = 240; + if (inp ~ /^.[0-9]/) n += substr(inp, 2, 1); + else if (inp ~ /^.[aA]/) n += 10; + else if (inp ~ /^.[bB]/) n += 11; + else if (inp ~ /^.[cC]/) n += 12; + else if (inp ~ /^.[dD]/) n += 13; + else if (inp ~ /^.[eE]/) n += 14; + else if (inp ~ /^.[fF]/) n += 15; + out = out sprintf("%c", n); + inp = substr(inp, 3); + } else { + out = out inp; + break; + } + return out; +} + +function HTML( text ) { + gsub( /&/, "\\&", text ); + gsub( //, "\\>", text ); + gsub( /"/, "\\"", text ); + gsub( /'/, "\\'", text ); + gsub( /\[/, "\\[", text ); + gsub( /\]/, "\\]", text ); + gsub( /\r/, "\\ ", text ); + gsub( /\n/, "\\ ", text ); + gsub( /\\/, "\\\", text ); + return text; +} + +function URL( text ) { + gsub( /&/, "%26", text ); + gsub( /"/, "%22", text ); + gsub( /'/, "%27", text ); + gsub( /`/, "%60", text ); + gsub( /\?/, "%3F", text ); + gsub( /#/, "%23", text ); + gsub( /\[/, "%5B", text ); + gsub( /\]/, "%5D", text ); + gsub( / /, "%20", text ); + gsub( /\t/, "%09", text ); + gsub( /\r/, "%0D", text ); + gsub( /\n/, "%0A", text ); + gsub( /%/, "%25", text ); + gsub( /\\/, "%5C", text ); + return text; +} + +function _cgilite_urldecode( str, arr, spl, form, k, n, key) { + if (! spl) spl="&" + split(str, form, spl); + for ( k in form ) { + key = form[k]; sub(/=.*$/, "", key); + sub(/^[^=]*=/, "", form[k]); + if ( key in arr ) { + n = 1; while ( (key, n) in arr ) n++; + arr[key,n] = HEX_DECODE( "%", form[k]); + } else { + arr[key] = HEX_DECODE( "%", form[k]); + } + } +} + +function _cgilite_request( key, val) { + # Read request from client connection + + # Read Headers + getline; REQUEST_METHOD = $1; REQUEST_URI = $2; SERVER_PROTOCOL = $3; + while ( getline ) { + if ($0 ~ /^\r?$/) break; + else if ($0 ~ /^[a-zA-Z][0-9a-zA-Z-_]+: .*/) { + key = toupper($0); + sub(/:.*$/, "", key); + gsub(/-/, "_", key); + _HEADER[key] = $0; + sub(/^[^:]:[\t ]*/, "", _HEADER[key]); + sub(/[\t ]*\r?$/, "", _HEADER[key]); + } + } + CONTENT_LENGTH = _HEADER["CONTENT_LENGTH"]; + CONTENT_TYPE = _HEADER["CONTENT_TYPE"]; + + PATH_INFO = REQUEST_URI; gsub(/\?.*$/, "", PATH_INFO) + PATH_INFO = PATH( HEX_DECODE( "%", PATH_INFO ) ); + QUERY_STRING = REQUEST_URI; + if ( !gsub(/^[^?]+\?/, "", QUERY_STRING) ) QUERY_STRING = ""; + + # Set up _GET[]-Array + _cgilite_urldecode(QUERY_STRING, _GET); + + if ( _HEADER["CONTENT_TYPE"] == "application/x-www-form-urlencoded" \ + && _HEADER["CONTENT_LENGTH"] ) { + # Set up _POST[]-Array + + val = ""; key = "head -c " _HEADER["CONTENT_LENGTH"]; + while (key |getline) val = val $0; close(key); + _cgilite_urldecode(val, _POST); + } + + if ( _HEADER["COOKIE"] ) { + # Set up _COOKIE[]-Array + _cgilite_urldecode(_HEADER["COOKIE"], _COOKIE, "; ?"); + } + + if ( _HEADER["REFERER"] ) { + key = HEADER["REFERER"]; + if (! sub(/^[^\?]+?/, "", key)) key = "" + _cgilite_urldecode(key, _REF); + } + +} + +function _cgilite_headers() { + # Import request data from webserver environment variables +} + +BEGIN { + REQUEST_METHOD=""; REQUEST_URI=""; SERVER_PROTOCOL=""; + PATH_INFO=""; QUERY_STRING=""; CONTENT_LENGTH=""; CONTENT_TYPE=""; + split("", _GET); split("", _POST); split("", _REF); + split("", _HEADER); split("", _COOKIE); + + if ( ENVIRON["REQUEST_METHOD"] ) + _cgilite_headers(); + else + _cgilite_request(); +} -- 2.39.2 From 6c558265e3db741077c1356d46fe6e7e904dc814 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Paul=20H=C3=A4nsch?= Date: Wed, 20 Mar 2024 11:57:19 +0100 Subject: [PATCH 12/16] allow dashes in metadata names --- markdown.awk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/markdown.awk b/markdown.awk index 7e29c57..c08d856 100755 --- a/markdown.awk +++ b/markdown.awk @@ -391,7 +391,7 @@ function _block( block, LOCAL, st, len, text, title, attrib, href, guard, code, # Metadata (custom, block starting with %something) # Metadata is ignored but can be interpreted externally - } else if ( match(block, /^%[a-zA-Z]+([[:space:]][^\n]*)?(\n|$)(%[a-zA-Z]+([[:space:]][^\n]*)?(\n|$)|%([[:space:]][^\n]*)?(\n|$)|[ \t]+[^\n[:space:]][^\n]*(\n|$))*/) ) { + } else if ( match(block, /^%[a-zA-Z-]+([[:space:]][^\n]*)?(\n|$)(%[a-zA-Z-]+([[:space:]][^\n]*)?(\n|$)|%([[:space:]][^\n]*)?(\n|$)|[ \t]+[^\n[:space:]][^\n]*(\n|$))*/) ) { len = RLENGTH; st = RSTART; return _block( substr( block, len + 1) ); -- 2.39.2 From a48f2023821b9ca8840a111f381d53301c8e9b4b Mon Sep 17 00:00:00 2001 From: =?utf8?q?Paul=20H=C3=A4nsch?= Date: Wed, 20 Mar 2024 11:57:40 +0100 Subject: [PATCH 13/16] bugfix: accidental regex range --- cgilite.awk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cgilite.awk b/cgilite.awk index 0a1e1ca..f16ed6a 100644 --- a/cgilite.awk +++ b/cgilite.awk @@ -99,7 +99,7 @@ function _cgilite_request( key, val) { getline; REQUEST_METHOD = $1; REQUEST_URI = $2; SERVER_PROTOCOL = $3; while ( getline ) { if ($0 ~ /^\r?$/) break; - else if ($0 ~ /^[a-zA-Z][0-9a-zA-Z-_]+: .*/) { + else if ($0 ~ /^[a-zA-Z][0-9a-zA-Z_-]+: .*/) { key = toupper($0); sub(/:.*$/, "", key); gsub(/-/, "_", key); -- 2.39.2 From 9c46b3c13b81e117b77bd58ce782645d1f137161 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Paul=20H=C3=A4nsch?= Date: Wed, 27 Mar 2024 15:56:00 +0100 Subject: [PATCH 14/16] Bugfix: use HTML() escapes in most links instead of double escaping via URL() --- markdown.awk | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/markdown.awk b/markdown.awk index c08d856..34879d2 100755 --- a/markdown.awk +++ b/markdown.awk @@ -158,18 +158,18 @@ function inline( line, LOCAL, len, text, code, href, guard ) { href = gensub(/^\[\[([^]|]+)(\|([^]]+))?\]\]/, "\\1", 1, substr(line, 1, len) ); text = gensub(/^\[\[([^]|]+)(\|([^]]+))?\]\]/, "\\3", 1, substr(line, 1, len) ); if ( ! text ) text = href; - return "" HTML(text) "" inline( substr( line, len + 1) ); + return "" HTML(text) "" inline( substr( line, len + 1) ); # quick links ("automatic links" in md doc) } else if ( match( line, /^<[a-zA-Z]+:\/\/([-\.[:alnum:]]+)(:[0-9]*)?(\/[^>]*)?>/ ) ) { len = RLENGTH; - href = URL( substr( line, 2, len - 2) ); + href = HTML( substr( line, 2, len - 2) ); return "" href "" inline( substr( line, len + 1) ); # quick link email } else if ( match( line, /^<[a-zA-Z0-9.!#$%&'\''*+\/=?^_`{|}~-]+@[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*>/ ) ) { len = RLENGTH; - href = URL( substr( line, 2, len - 2) ); + href = HTML( substr( line, 2, len - 2) ); return "" href "" inline( substr( line, len + 1) ); # Verbatim inline HTML @@ -192,7 +192,7 @@ function inline( line, LOCAL, len, text, code, href, guard ) { gsub(/\\/, "", href); gsub(/\\/, "", title); gsub(/[\n\t]+/, " ", title); - return "" \ + return "" \ inline( text ) "" inline( substr( line, len + 1) ); # reference style links @@ -202,9 +202,9 @@ function inline( line, LOCAL, len, text, code, href, guard ) { id = gensub(/^\[([^\n]+)\] ?\[([^\n]*)\].*/, "\\2", 1, substr(line, 1, len) ); if ( ! id ) id = text; if ( rl_href[id] && rl_title[id] ) { - return "" inline(text) "" inline( substr( line, len + 1) ); + return "" inline(text) "" inline( substr( line, len + 1) ); } else if ( rl_href[id] ) { - return "" inline(text) "" inline( substr( line, len + 1) ); + return "" inline(text) "" inline( substr( line, len + 1) ); } else { return "" HTML(substr(line, 1, len)) inline( substr(line, len + 1) ); } @@ -234,7 +234,7 @@ function inline( line, LOCAL, len, text, code, href, guard ) { gsub(/^[\t ]+$/, "", text); gsub(/\\/, "", href); gsub(/\\/, "", title); gsub(/[\n\t]+/, " ", title); - return "\""" inline( substr( line, len + 1) ); @@ -245,10 +245,10 @@ function inline( line, LOCAL, len, text, code, href, guard ) { id = gensub(/^!\[([^\n]*)\] ?\[([^\n]*)\].*/, "\\2", 1, substr(line, 1, len) ); if ( ! id ) id = text; if ( rl_href[id] && rl_title[id] ) { - return "\""" \ + return "\""" \ inline( substr( line, len + 1) ); } else if ( rl_href[id] ) { - return "\""" \ + return "\""" \ inline( substr( line, len + 1) ); } else { return "" HTML(substr(line, 1, len)) inline( substr(line, len + 1) ); @@ -663,8 +663,8 @@ function _block( block, LOCAL, st, len, text, title, attrib, href, guard, code, gsub(/^[\t ]+$/, "", text); gsub(/\\/, "", href); - return "
" \ - "\""" \ + "\""" \ (title?"
" inline(title) "
":"") \ "
\n\n" \ @@ -677,14 +677,14 @@ function _block( block, LOCAL, st, len, text, title, attrib, href, guard, code, id = gensub(/^!\[([^\n]*)\] ?\[([^\n]*)\](\n.*)?$/, "\\2", 1, block); if ( ! id ) id = text; if ( rl_href[id] && rl_title[id] ) { - return "
" \ - "\""" \ + return "
" \ + "\""" \ "
" inline(rl_title[id]) "
" \ "
\n\n" \ _block( substr( block, len + 1) ); } else if ( rl_href[id] ) { - return "
" \ - "\""" \ + return "
" \ + "\""" \ "
\n\n" \ _block( substr( block, len + 1) ); } else { -- 2.39.2 From 397847dd01a44b7a5a26b6c6d16a61a42079a542 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Paul=20H=C3=A4nsch?= Date: Tue, 9 Apr 2024 16:46:33 +0200 Subject: [PATCH 15/16] offer invitation links if email is not required --- users.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/users.sh b/users.sh index c924789..32299ff 100755 --- a/users.sh +++ b/users.sh @@ -621,7 +621,7 @@ w_user_invite(){ if [ "$(GET user_confirm)" ]; then w_user_confirm - elif [ "$USER_ID" -a "$SENDMAIL" ]; then + elif [ "$USER_ID" -a "$USER_REQUIREEMAIL" = true ]; then w_user_invite_email elif [ "$USER_ID" ]; then uid="$(timeid)" -- 2.39.2 From 1a733d66393fa87f936f19ab64b31ecdb828994f Mon Sep 17 00:00:00 2001 From: =?utf8?q?Paul=20H=C3=A4nsch?= Date: Wed, 17 Apr 2024 16:33:29 +0200 Subject: [PATCH 16/16] debug function --- cgilite.awk | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cgilite.awk b/cgilite.awk index f16ed6a..ebf4411 100644 --- a/cgilite.awk +++ b/cgilite.awk @@ -1,5 +1,7 @@ #!/bin/env awk -f +function debug(t) { printf "%s\n", t >>"/dev/stderr"; } + function PATH( str, seg, out ) { while ( str ) { seg = str; @@ -151,8 +153,9 @@ BEGIN { split("", _GET); split("", _POST); split("", _REF); split("", _HEADER); split("", _COOKIE); - if ( ENVIRON["REQUEST_METHOD"] ) + if ( ENVIRON["REQUEST_METHOD"] ) { _cgilite_headers(); - else + } else { _cgilite_request(); + } } -- 2.39.2