From: Paul Hänsch Date: Sun, 22 May 2022 18:13:13 +0000 (+0200) Subject: Merge commit 'c1c97928352f0ac36034432f5bbba708c4146ff0' X-Git-Url: http://git.plutz.net/?p=serve0;a=commitdiff_plain;h=2dc16837b68beede769ac485c518fd62a9c3e373;hp=-c Merge commit 'c1c97928352f0ac36034432f5bbba708c4146ff0' --- 2dc16837b68beede769ac485c518fd62a9c3e373 diff --combined cgilite/cgilite.sh index 90d6557,b47a3e2..b47a3e2 --- a/cgilite/cgilite.sh +++ b/cgilite/cgilite.sh @@@ -89,7 -89,8 +89,8 @@@ HEX_DECODE() "$pfx"[0-9a-fA-F][0-9a-fA-F]*) in="${in#${pfx}}";; \\*) in="${in#?}"; out="${out}\\\\"; continue;; %*) in="${in#?}"; out="${out}%%"; continue;; - *) out="${out}${in%"${in#?}"}"; in="${in#?}"; continue;; + *) att="${in%%"${pfx}"*}"; att="${att%%%*}"; att="${att%%\\*}" + out="${out}${att}"; in="${in#"${att}"}"; continue;; esac; # Hex escapes for printf (e.g. \x41) are not portable @@@ -143,6 -144,8 +144,8 @@@ if [ -z "$REQUEST_METHOD" ]; the (sleep $cgilite_timeout && kill $$) & cgilite_watchdog=$! while read REQUEST_METHOD REQUEST_URI SERVER_PROTOCOL; do + unset PATH_INFO QUERY_STRING cgilite_headers CONTENT_LENGTH CONTENT_TYPE + [ "${SERVER_PROTOCOL#HTTP/1.[01]${CR}}" ] && break kill $cgilite_watchdog @@@ -151,7 -154,7 +154,7 @@@ [ "${REQUEST_URI}" = "${REQUEST_URI#*\?}" ] \ && QUERY_STRING='' \ || QUERY_STRING="${REQUEST_URI#*\?}" - cgilite_headers=''; while read -r hl; do + while read -r hl; do hl="${hl%${CR}}"; [ "$hl" ] || break case $hl in 'Content-Length: '*) CONTENT_LENGTH="${hl#*: }";; @@@ -161,7 -164,7 +164,7 @@@ done export REMOTE_ADDR SERVER_NAME SERVER_PORT REQUEST_METHOD REQUEST_URI SERVER_PROTOCOL \ - PATH_INFO QUERY_STRING CONTENT_TYPE CONTENT_LENGTH + PATH_INFO QUERY_STRING CONTENT_TYPE CONTENT_LENGTH cgilite_headers # Try to serve multiple requests, provided that script serves a # Content-Length header. diff --combined cgilite/common.css index 71bead6,c5cf6c4..c5cf6c4 --- a/cgilite/common.css +++ b/cgilite/common.css @@@ -17,6 -17,7 +17,7 @@@ body } ul, ol, dl, table, p { margin-bottom: .5em; } + p:only-child { margin-bottom: 0; } a { font-style: italic; @@@ -44,6 -45,12 +45,12 @@@ ul, ol { margin-left: 1.125em; dl dt { font-weight: bolder; } table th { font-weight: bold; } + li p + ul, li p + ol { + margin-top: -.25em; + } + + hr { border-bottom: 1pt solid; } + h1, h2, h3 { font-weight: bold; margin-top: .75em; @@@ -88,6 -95,21 +95,21 @@@ input + label margin-left: .375em; } + input.search + button.search { + width: 2.5em; + color: transparent; + background-color: #CCC; + margin-left: -2pt; + border-left: none; + border-radius: 0 2pt 2pt 0; + white-space: nowrap; + } + input.search + button.search:before { + content: '\1f50d'; + color: #000; + font-weight: bold; + } + @media print { @page { margin: 20mm; } diff --combined cgilite/markdown.awk index 361e600,e63541b..e63541b --- a/cgilite/markdown.awk +++ b/cgilite/markdown.awk @@@ -4,10 -4,6 +4,6 @@@ # EXPERIMENTAL Markdown processor with minimal dependencies. # Meant to support all features of John Grubers basic Markdown # + a number of common extensions, mostly inspired by Pandoc Markdown - # - # ToDo: - # - HTML processing / escaping (according to environment flag) - # - em-dashes and arrows # Supported Features / TODO: # ========================== @@@ -45,7 -41,7 +41,7 @@@ # - ? Heading identifiers (php md, pandoc) # - [x] Automatic heading identifiers (custom) # - [x] Fenced code blocks (php md, pandoc) - # - [-] Fenced code attributes + # - [x] Fenced code attributes # - [ ] Tables # - ? Simple table (pandoc) # - ? Multiline table (pandoc) @@@ -56,7 -52,8 +52,8 @@@ # - [ ] Definition lists (php md, pandoc) # - [-] Numbered example lists (pandoc) # - [-] Metadata blocks (pandoc) - # - [-] Fenced Divs (pandoc) + # - [x] Metadata blocks (custom) + # - [x] Fenced Divs (pandoc) # # Extensions - Inline elements: # ---------------------------- @@@ -126,12 -123,18 +123,18 @@@ function inline( line, LOCAL, len, code 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 = HTML( substr( line, 2, len - 2) ); + return "" href "" inline( substr( line, len + 1) ); + # inline links - } else if ( match(line, /^\[([^]]+)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)/) ) { + } else if ( match(line, /^\[([^]]+)\]\(([^"\)]+)([[:space:]]+"([^"]+)")?\)/) ) { len = RLENGTH; - text = gensub(/^\[([^]]+)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)/, "\\1", "g", line); - href = gensub(/^\[([^]]+)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)/, "\\2", "g", line); - title = gensub(/^\[([^]]+)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)/, "\\4", "g", line); + text = gensub(/^\[([^]]+)\]\(([^"\)]+)([[:space:]]+"([^"]+)")?\)/, "\\1", 1, substr(line, 1, len) ); + href = gensub(/^\[([^]]+)\]\(([^"\)]+)([[:space:]]+"([^"]+)")?\)/, "\\2", 1, substr(line, 1, len) ); + title = gensub(/^\[([^]]+)\]\(([^"\)]+)([[:space:]]+"([^"]+)")?\)/, "\\4", 1, substr(line, 1, len) ); if ( title ) { return "" inline( text ) "" inline( substr( line, len + 1) ); } else { @@@ -141,23 -144,23 +144,23 @@@ # reference style links } else if ( match(line, /^\[([^]]+)\] ?\[([^]]*)\]/ ) ) { len = RLENGTH; - text = gensub(/^\[([^\n]+)\] ?\[([^\n]*)\].*/, "\\1", 1, line); - id = gensub(/^\[([^\n]+)\] ?\[([^\n]*)\].*/, "\\2", 1, line); + text = gensub(/^\[([^\n]+)\] ?\[([^\n]*)\].*/, "\\1", 1, substr(line, 1, len) ); + 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 "" substr(line, 1, len) inline( substr(line, len + 1) ); + return "" HTML(substr(line, 1, len)) inline( substr(line, len + 1) ); } # inline images } else if ( match(line, /^!\[([^]]+)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)/) ) { len = RLENGTH; - text = gensub(/^!\[([^]]+)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)/, "\\1", "g", line); - href = gensub(/^!\[([^]]+)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)/, "\\2", "g", line); - title = gensub(/^!\[([^]]+)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)/, "\\4", "g", line); + text = gensub(/^!\[([^]]+)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)/, "\\1", "g", substr(line, 1, len) ); + href = gensub(/^!\[([^]]+)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)/, "\\2", "g", substr(line, 1, len) ); + title = gensub(/^!\[([^]]+)\]\(([^"\)]+)([ \t]+"([^"]+)")?\)/, "\\4", "g", substr(line, 1, len) ); if ( title ) { return "\""" inline( substr( line, len + 1) ); } else { @@@ -167,15 -170,15 +170,15 @@@ # reference style images } else if ( match(line, /^!\[([^]]+)\] ?\[([^]]*)\]/ ) ) { len = RLENGTH; - text = gensub(/^!\[([^\n]+)\] ?\[([^\n]*)\].*/, "\\1", 1, line); - id = gensub(/^!\[([^\n]+)\] ?\[([^\n]*)\].*/, "\\2", 1, line); + text = gensub(/^!\[([^\n]+)\] ?\[([^\n]*)\].*/, "\\1", 1, substr(line, 1, len) ); + id = gensub(/^!\[([^\n]+)\] ?\[([^\n]*)\].*/, "\\2", 1, substr(line, 1, len) ); if ( ! id ) id = text; if ( rl_href[id] && rl_title[id] ) { - return "\""" inline( substr( line, len + 1) ); + return "\""" inline( substr( line, len + 1) ); } else if ( rl_href[id] ) { - return "\""" inline( substr( line, len + 1) ); + return "\""" inline( substr( line, len + 1) ); } else { - return "" substr(line, 1, len) inline( substr(line, len + 1) ); + return "" HTML(substr(line, 1, len)) inline( substr(line, len + 1) ); } # ~~strikeout~~ (pandoc) @@@ -195,7 -198,7 +198,7 @@@ # ignore embedded underscores (pandoc, php md) } else if ( match(line, "^[[:alnum:]](__|_)") ) { - return substr( line, 1, RLENGTH) inline( substr(line, RLENGTH + 1) ); + return HTML(substr( line, 1, RLENGTH)) inline( substr(line, RLENGTH + 1) ); # __strong__$ } else if ( match(line, "^__(([^_[:space:]]|" ieu ")|([^_[:space:]]|" ieu ")(" nu "|" ieu ")*([^_[:space:]]|" ieu "))__$") ) { @@@ -227,6 -230,11 +230,11 @@@ len = RLENGTH; return "" inline( substr( line, 2, len - 2 ) ) "" inline( substr( line, len + 1 ) ); + # Macros + } else if ( AllowMacros && match( line, /^<<([^>]|>[^>])+>>/) ) { + len = RLENGTH; + return macro( substr( line, 3, len - 4 ) ) inline(substr(line, len + 1)); + # Verbatim inline HTML } else if ( AllowHTML && match( line, /^(|<\?([^\?]|\?[^>])*\?>|]*>|])*\]\]>|<\/[A-Za-z][A-Za-z0-9-]*[[:space:]]*>|<[A-Za-z][A-Za-z0-9-]*([[:space:]]+[A-Za-z_:][A-Za-z0-9_\.:-]*([[:space:]]*=[[:space:]]*([[:space:]"'=<>`]+|"[^"]*"|'[^']*'))?)*[[:space:]]*\/?>)/) ) { len = RLENGTH; @@@ -247,7 -255,7 +255,7 @@@ } } - function _block( block, LOCAL, st, len, hlvl, htxt, guard, code, indent ) { + function _block( block, LOCAL, st, len, hlvl, htxt, guard, code, indent, attrib ) { gsub( /^\n+|\n+$/, "", block ); if ( block == "" ) { @@@ -274,6 -282,12 +282,12 @@@ } else if ( AllowHTML && match( block, /^ ? ? ?(<\/[A-Za-z][A-Za-z0-9-]*[[:space:]]*>|<[A-Za-z][A-Za-z0-9-]*([[:space:]]+[A-Za-z_:][A-Za-z0-9_\.:-]*([[:space:]]*=[[:space:]]*([[:space:]"'=<>`]+|"[^"]*"|'[^']*'))?)*[[:space:]]*\/?>)([[:space:]]*\n)([^\n]|\n[ \t]*[^\n])*(\n[[:space:]]*\n|$)/) ) { len = RLENGTH; st = RSTART; return substr(block, st, len) _block(substr(block, st + len)); + + # 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|$))*/) ) { + len = RLENGTH; st = RSTART; + return _block( substr( block, len + 1) ); # Blockquote (leading >) } else if ( match( block, /^> /) ) { @@@ -301,13 -315,34 +315,34 @@@ return "
" HTML( code ) "
\n" \ _block( substr( block, len + 1 ) ); + # Fenced Divs (pandoc, custom) + } else if ( match( block, /^(:::+)/ ) ) { + guard = substr( block, 1, RLENGTH ); + code = gensub(/^[^\n]+\n/, "", 1, block); + attrib = gensub(/^:::+[ \t]*\{?[ \t]*([^\}\n]*)\}?[ \t]*\n.*$/, "\\1", 1, block); + gsub(/[^a-zA-Z0-9_-]+/, " ", attrib); + gsub(/(^ | $)/, "", attrib); + if ( match(code, "(^|\n)" guard "+(\n|$)" ) ) { + len = RLENGTH; st = RSTART; + return "
" _block( substr(code, 1, st - 1) ) "
\n" \ + _block( substr( code, st + len ) ); + } else { + match( block, /(^|\n)[[:space:]]*(\n|$)/ ) || match( block, /$/ ); + len = RLENGTH; st = RSTART; + return "

" inline( substr(block, 1, st - 1) ) "

\n" \ + _block( substr(block, st + len) ); + } + # Fenced Code Block (pandoc) } else if ( match( block, /^(~~~+|```+)/ ) ) { guard = substr( block, 1, RLENGTH ); code = gensub(/^[^\n]+\n/, "", 1, block); + attrib = gensub(/^:::+[ \t]*\{?[ \t]*([^\}\n]*)\}?[ \t]*\n.*$/, "\\1", 1, block); + gsub(/[^a-zA-Z0-9_-]+/, " ", attrib); + gsub(/(^ | $)/, "", attrib); if ( match(code, "(^|\n)" guard "+(\n|$)" ) ) { len = RLENGTH; st = RSTART; - return "
" HTML( substr(code, 1, st - 1) ) "
\n" \ + return "
" HTML( substr(code, 1, st - 1) ) "
\n" \ _block( substr( code, st + len ) ); } else { match( block, /(^|\n)[[:space:]]*(\n|$)/ ) || match( block, /$/ ); @@@ -364,6 -399,12 +399,12 @@@ return "" inline( htxt ) "\n\n" \ _block( substr( block, len + 1) ); + # Split paragraphs + } else if ( match( block, /(^|\n)[[:space:]]*(\n|$)/) ) { + len = RLENGTH; st = RSTART; + return _block( substr(block, 1, st - 1) ) "\n" \ + _block( substr(block, st + len) ); + # Horizontal rule } else if ( match( block, /(^|\n) ? ? ?((\* *){3,}|(- *){3,}|(_ *){3,})($|\n)/) ) { len = RLENGTH; st = RSTART; @@@ -371,10 -412,7 +412,7 @@@ # Plain paragraph } else { - match( block, /(^|\n)[[:space:]]*(\n|$)/ ) || match( block, /$/ ); - len = RLENGTH; st = RSTART; - return "

" inline( substr(block, 1, st - 1) ) "

\n" \ - _block( substr(block, st + len) ); + return "

" inline(block) "

\n"; } } diff --combined cgilite/session.sh index 8fb6236,1f4699e..1f4699e --- a/cgilite/session.sh +++ b/cgilite/session.sh @@@ -136,5 -136,3 +136,3 @@@ SESSION_COOKIE() } update_session || new_session - - [ "$1" = nocookie ] || SESSION_COOKIE diff --combined cgilite/users.sh index 873edf0,6a6833e..6a6833e --- a/cgilite/users.sh +++ b/cgilite/users.sh @@@ -6,11 -6,20 +6,20 @@@ include_users="$0 . "${_EXEC}/cgilite/session.sh" . "${_EXEC}/cgilite/storage.sh" - USER_REGISTRATION="${USER_REGISTRATION:-true}" - USER_REQUIREEMAIL="${USER_REQUIREEMAIL:-true}" + SENDMAIL=${SENDMAIL-sendmail} + + USER_REGISTRATION="${USER_REGISTRATION-true}" + USER_REQUIREEMAIL="${USER_REQUIREEMAIL-true}" + 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="${MAILDOMAIN:-noreply@${HTTP_HOST%:*}}" + + [ "$HTTPS" ] && SCHEMA=https || SCHEMA=http # == FILE FORMAT == # UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE @@@ -62,7 -71,7 +71,7 @@@ read_user() update_user() { # internal function for user update local uid="$1" uname status email pwsalt pwhash expire devices futureuse - local UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE + local UID_ UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE local arg for arg in "$@"; do case $arg in @@@ -75,15 -84,15 +84,15 @@@ esac; done if LOCK "$user_db"; then - while read -r UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES \ + while read -r UID_ UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES \ FUTUREUSE; do - if [ "$UID" = "$uid" ]; then + if [ "$UID_" = "$uid" ]; then printf '%s %s %s %s %s %s %i %s %s\n' \ "$uid" "$(STRING "${uname-$(UNSTRING "$UNAME")}")" \ "${status:-${status-${STATUS}}${status+\\}}" \ "${email:-${email-${EMAIL}}${email+\\}}" \ "${pwsalt:-${PWSALT}}" "${pwhash:-${PWHASH}}" \ - "${expire:-$((_DATE + 86400 * 730))}" \ + "${expire:-$((_DATE + USER_ACCOUNTEXPIRE))}" \ "$(STRING "${devices-$(UNSTRING "$DEVICES")}")" \ "${FUTUREUSE:-\\}" elif [ "$STATUS" = pending -a ! "$EXPIRE" -ge "$_DATE" ]; then @@@ -91,7 -100,7 +100,7 @@@ : else printf '%s %s %s %s %s %s %i %s %s\n' \ - "$UID" "$UNAME" "$STATUS" "$EMAIL" "$PWSALT" "$PWHASH" \ + "$UID_" "$UNAME" "$STATUS" "$EMAIL" "$PWSALT" "$PWHASH" \ "$EXPIRE" "$DEVICES" "$FUTUREUSE" fi done <"$user_db" >"${user_db}.$$" @@@ -112,7 -121,7 +121,7 @@@ new_user() return 1 fi printf '%s \\ %s \\ \\ \\ %i \\ \\\n' \ - "$user" "pending" "$(( $_DATE + 86400 ))" >>"$user_db" + "$user" "pending" "$(( _DATE + USER_CONFIRMEXPIRE ))" >>"$user_db" else return 1 fi @@@ -185,9 -194,9 +194,9 @@@ user_checkemail() user_nameexist(){ local uname="$(STRING "$1")" - local UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE + local UID_ UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE [ -f "$user_db" -a -r "$user_db" ] \ - && while read -r UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE; do + && while read -r UID_ UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE; do [ "$EXPIRE" -gt "$_DATE" -a "$UNAME" = "$uname" ] && return 0 done <"$user_db" return 1 @@@ -195,9 -204,9 +204,9 @@@ user_emailexist(){ local email="$(STRING "$1")" - local UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE + local UID_ UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE [ -f "$user_db" -a -r "$user_db" ] \ - && while read -r UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE; do + && while read -r UID_ UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE; do [ "$EXPIRE" -gt "$_DATE" -a "$EMAIL" = "$email" ] && return 0 done <"$user_db" return 1 @@@ -227,10 -236,10 +236,10 @@@ user_register() REDIRECT "${_BASE}${PATH_INFO}#ERROR_EMAIL_INVALID" elif user_emailexist "$email"; then REDIRECT "${_BASE}${PATH_INFO}#ERROR_EMAIL_EXISTS" - elif new_user "$uid" status=pending email="$email" expire="$((_DATE + 86400))"; then + elif new_user "$uid" status=pending email="$email" expire="$((_DATE + USER_CONFIRMEXPIRE))"; then debug "Sending Activation Link:" \ - "https://${HTTP_HOST}${_BASE}${PATH_INFO}?user_confirm=${uid}+$(session_mac "$uid")" - sendmail -t -f "$MAILFROM" <<-EOF + "${SCHEMA}://${HTTP_HOST}${_BASE}${PATH_INFO}?user_confirm=${uid}+$(session_mac "$uid")" + "$SENDMAIL" -t -f "$MAILFROM" <<-EOF From: ${MAILFROM} To: ${email} Subject: Your account registration at ${HTTP_HOST%:*} @@@ -239,9 -248,9 +248,9 @@@ You can activate your account using this link: - https://${HTTP_HOST}${_BASE}${PATH_INFO}?user_confirm=${uid}+$(session_mac "$uid") + ${SCHEMA}://${HTTP_HOST}${_BASE}${PATH_INFO}?user_confirm=${uid}+$(session_mac "$uid") - This registration link will expire after 24 hours. + This registration link will expire after $((USER_CONFIRMEXPIRE / 3600)) hours. If you did not request an account at ${HTTP_HOST%:*}, then someone else probably entered your email address by accident. In this case you shoud @@@ -265,11 -274,15 +274,15 @@@ REDIRECT "${_BASE}${PATH_INFO}#ERROR_PW_EMPTYTOOSHORT" elif [ "$pw" != "$pwconfirm" ]; then REDIRECT "${_BASE}${PATH_INFO}#ERROR_PW_MISMATCH" - elif new_user "$uid" uname="$uname" status=active email="$email" password="$pw" expire="$((_DATE + 86400 * 730))"; then + elif new_user "$uid" uname="$uname" status=active email="$email" password="$pw" expire="$((_DATE + USER_ACCOUNTEXPIRE))"; then SESSION_COOKIE new SESSION_BIND user_id "$uid" - REDIRECT "${_BASE}${PATH_INFO}#USER_REGISTER_CONFIRM" + if [ "$USER_ACCOUNTPAGE" ]; then + REDIRECT "${USER_ACCOUNTPAGE}" + else + REDIRECT "${_BASE}${PATH_INFO}#USER_REGISTER_CONFIRM" + fi else REDIRECT "${_BASE}${PATH_INFO}#ERROR_USER_NOLOCK" fi @@@ -285,10 -298,10 +298,10 @@@ user_invite() REDIRECT "${_BASE}${PATH_INFO}#ERROR_EMAIL_INVALID" elif user_emailexist "$email"; then REDIRECT "${_BASE}${PATH_INFO}#ERROR_EMAIL_EXISTS" - elif new_user "$uid" status=pending email="$email" expire="$((_DATE + 86400))"; then + elif new_user "$uid" status=pending email="$email" expire="$((_DATE + USER_CONFIRMEXPIRE))"; then debug "Sending Invitation Link:" \ - "https://${HTTP_HOST}${BASE}${PATH_INFO}?user_confirm=${uid}+$(session_mac "$uid")" - sendmail -t -f "$MAILFROM" <<-EOF + "${SCHEMA}://${HTTP_HOST}${_BASE}${PATH_INFO}?user_confirm=${uid}+$(session_mac "$uid")" + "$SENDMAIL" -t -f "$MAILFROM" <<-EOF From: ${MAILFROM} To: ${email} Subject: You have been invited to ${HTTP_HOST%:*} @@@ -299,9 -312,9 +312,9 @@@ You can create your account using this link: - https://${HTTP_HOST}${_BASE}${PATH_INFO}?user_confirm=${uid}+$(session_mac "$uid") + ${SCHEMA}://${HTTP_HOST}${_BASE}${PATH_INFO}?user_confirm=${uid}+$(session_mac "$uid") - This registration link will expire after 24 hours. + This registration link will expire after $((USER_CONFIRMEXPIRE / 3600)) hours. If you do not know what this is about, then someone else probably entered your email address by accident. In this case you shoud @@@ -343,7 -356,11 +356,11 @@@ user_confirm() elif update_user "$USER_ID" uname="$uname" status=active password="$pw"; then SESSION_COOKIE new SESSION_BIND user_id "$USER_ID" - REDIRECT "${_BASE}${PATH_INFO}?user_register=confirm#USER_REGISTER_CONFIRM" + if [ "$USER_ACCOUNTPAGE" ]; then + REDIRECT "${USER_ACCOUNTPAGE}" + else + REDIRECT "${_BASE}${PATH_INFO}?user_register=confirm#USER_REGISTER_CONFIRM" + fi else REDIRECT "${_BASE}${PATH_INFO}#ERROR_USER_NOLOCK" fi @@@ -353,15 -370,15 +370,15 @@@ user_login() # set cookie # keep logged in - device cookie? # initialize new session! - local UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE + local UID_ UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE local uname="$(POST uname |STRING)" pw="$(POST pw)" [ -f "$user_db" -a -r "$user_db" ] \ - && while read -r UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE; do + && while read -r UID_ UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE; do if [ "$UNAME" = "$uname" -o "$EMAIL" = "$uname" ]; then if [ "$STATUS" = active -a "$EXPIRE" -gt "$_DATE" -a "$PWHASH" = "$(user_pwhash "$PWSALT" "$pw")" ]; then SESSION_COOKIE new - SESSION_BIND user_id "$UID" + SESSION_BIND user_id "$UID_" REDIRECT "${_BASE}${PATH_INFO}#USER_LOGGED_IN" fi fi @@@ -379,9 -396,35 +396,35 @@@ user_logout() } user_update(){ - # passphrase, email - : + # todo: username update, email update / email confirm + local UID_ UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE + # local uname="$(POST uname |STRING)" + local uid oldpw pw pwconfirm + + uid="$(POST uid)" + oldpw="$(POST oldpw)" + pw="$(POST pw |grep -xE '.{6}')" + pwconfirm="$(POST pwconfirm)" + + + read -r UID_ UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE <<-EOF + $(grep "^${uid} " "$user_db") + EOF + + if [ "$UID_" = "$USER_ID" -a "$PWHASH" = "$(user_pwhash "$PWSALT" "$oldpw")" ]; then + if [ "$pw" -a "$pw" = "$pwconfirm" ]; then + update_user "${uid}" password="$pw" + REDIRECT "${_BASE}${PATH_INFO}#UPDATE_SUCCESS" + else + REDIRECT "${_BASE}${PATH_INFO}#ERROR_PWMISMATCH" + fi + elif [ "$UID_" = "$USER_ID" ]; then + REDIRECT "${_BASE}${PATH_INFO}#ERROR_INVALID_AUTH_PASSWORD" + else + REDIRECT "${_BASE}${PATH_INFO}#ERROR_NOTLOGGEDIN" + fi } + user_recover(){ # send recover link : @@@ -399,14 -442,38 +442,38 @@@ read_user "$(SESSION_VAR user_id) user_invite) user_invite ;; user_login) user_login ;; user_logout) user_logout ;; - user_update) - :;; + user_update) user_update ;; user_recover) :;; user_disable) :;; esac + export USER_ID USER_NAME USER_STATUS USER_EMAIL USER_PWSALT USER_PWHASH \ + USER_EXPIRE USER_DEVICES USER_FUTUREUSE + + + w_user_update(){ + if [ ! "$USER_ID" ]; then + cat <<-EOF + [div #user_update .nouser + This page can only be used by registered users + ] + EOF + else + cat <<-EOF + [form #user_update method=POST + [hidden "uid" "$USER_ID"] + [p .username Logged in as $USER_NAME] + [input type=password name=oldpw placeholder="Current Passphrase"] + [input type=password name=pw placeholder="New Passphrase" pattern=".{6,}"] + [input type=password name=pwconfirm placeholder="Confirm New Passphrase" pattern=".{6,}"] + [submit "action" "user_update" Update Passphrase] + ] + EOF + fi + } + w_user_register(){ if [ "$(GET user_confirm)" ]; then w_user_confirm @@@ -429,7 -496,7 +496,7 @@@ elif [ "$USER_REQUIREEMAIL" != true ]; then cat <<-EOF [form #user_register .registername method=POST - [input name=uname placeholder="Choose Username" tooltip="Your username may contain any character but the @ sign. It must be at least 3 characters long, and it must start with a letter." pattern="^\[a-zA-Z\]\[a-zA-Z0-9 -~\]{2,127}$" autocomplete=off] + [input name=uname placeholder="Choose Username" tooltip="Your username may contain any character but the @ sign. It must be at least 3 characters long, and it must start with a letter." pattern="^\[\\\\p{L}\]\[\\\\p{L}0-9 -~\]{2,127}$" autocomplete=off] [input type=password name=pw placeholder="Choose Passphrase" pattern=".{6,}"] [input type=password name=pwconfirm placeholder="Confirm Passphrase" pattern=".{6,}"] [submit "action" "user_register" Sign Up] @@@ -439,12 -506,12 +506,12 @@@ } w_user_confirm(){ - local UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE + local UID_ UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE local user_confirm="$(GET user_confirm)" local uid="${user_confirm% *}" signature="${user_confirm#* }" if [ "$signature" = "$(session_mac "$uid")" ]; then - read -r UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE <<-EOF + read -r UID_ UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE <<-EOF $(grep "^${uid} " "$user_db") EOF if [ "$STATUS" = pending -a "$EXPIRE" -gt "$_DATE" ]; then @@@ -452,8 -519,10 +519,10 @@@ [form #user_confirm method=POST [input type=hidden name=uid value="${uid}"] [input type=hidden name=signature value="${signature}"] - [input disabled=disabled value="$(HTML "$EMAIL")"] - [input name=uname placeholder="Choose Username" tooltip="Your username may contain any character but the @ sign. It must be at least 3 characters long, and it must start with a letter." pattern="^\[a-zA-Z\]\[a-zA-Z0-9 -~\]{2,127}$" autocomplete=off] + $([ "$EMAIL" != '\' ] && printf \ + '[input disabled=disabled value="%s" placeholder="Email"]' "$(UNSTRING "$EMAIL" |HTML)" + ) + [input name=uname placeholder="Choose Username" tooltip="Your username may contain any character but the @ sign. It must be at least 3 characters long, and it must start with a letter." pattern="^\[\\\\p{L}\]\[\\\\p{L}0-9 -~\]{2,127}$" autocomplete=off] [input type=password name=pw placeholder="Choose Passphrase" pattern=".{6,}"] [input type=password name=pwconfirm placeholder="Confirm Passphrase" pattern=".{6,}"] [submit "action" "user_confirm" Finish Registration] @@@ -476,9 -545,11 +545,11 @@@ } w_user_invite(){ + local uid invlink + if [ "$(GET user_confirm)" ]; then w_user_confirm - elif [ "$USER_ID" ]; then + elif [ "$USER_ID" -a "$SENDMAIL" ]; then cat <<-EOF [form #user_invite method=POST [input placeholder="Email Recipient" name=email autocomplete=off] @@@ -486,6 -557,19 +557,19 @@@ [submit "action" "user_invite" Send Invitation] ] EOF + elif [ "$USER_ID" ]; then + uid="$(timeid)" + new_user "$uid" status=pending expire="$((_DATE + USER_CONFIRMEXPIRE))" + invlink="${SCHEMA}://${HTTP_HOST}${_BASE}${PATH_INFO}?user_confirm=${uid}+$(session_mac "$uid")" + debug "New Invitation Link: $invlink" + cat <<-EOF + [div #user_invite .link + [p An anonymous user account has been set up. Send the following link to the intended user, so they may claim their account. The link will remain valid for $((USER_CONFIRMEXPIRE / 3600)) hours.] + [a href="$(HTML "$invlink")" . $(HTML "$invlink")] + + [p [a href="#" . Set up another account]] + ] + EOF else cat <<-EOF [div #user_invite .notallowed