X-Git-Url: https://git.plutz.net/?p=cgilite;a=blobdiff_plain;f=users.sh;h=1959e9de4b757084fad3d850b1b7b771517cb3c3;hp=0af232090d7f8fbf9ba9726f628e9ea18ebc1e2c;hb=HEAD;hpb=142f5b0b60216dddb94ef30684c300b633f269ed diff --git a/users.sh b/users.sh index 0af2320..32299ff 100755 --- a/users.sh +++ b/users.sh @@ -1,39 +1,195 @@ #!/bin/sh +# 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 +# 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_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} -USER_REGISTRATION="${USER_REGISTRATION:-true}" -USER_REQUIREEMAIL="${USER_REQUIREEMAIL:-true}" +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}" HTTP_HOST="$(HEADER Host)" -MAILFROM="${MAILDOMAIN:-noreply@${HTTP_HOST%:*}}" +MAILFROM="noreply@${HTTP_HOST%:*}" + +[ "$HTTPS" ] && SCHEMA=https || SCHEMA=http + +# == FILE FORMAT == +# UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE +# (pending|active|deleted) + +# == GLOBALS == +UNSET_USER='unset \ + USER_ID USER_NAME USER_STATUS USER_EMAIL USER_PWSALT USER_PWHASH \ + USER_EXPIRE USER_DEVICES USER_FUTUREUSE +' + +LOCAL_USER='local \ + USER_ID USER_NAME USER_STATUS USER_EMAIL USER_PWSALT USER_PWHASH \ + USER_EXPIRE USER_DEVICES USER_FUTUREUSE +' -user_db="${_DATA}/users.db" -unset USER_ID USER_NAME USER_EMAIL +# == TRANSLATIONS == +# override all functions marked with "TRANSLATION" +# sed -n '/TRANSLATION$/,/^}/p;' "${user_db}.$$" + mv -- "${user_db}.$$" "$user_db" + RELEASE "$user_db" + else + return 1 + fi +} + +new_user(){ + local user="${1:-$(timeid)}" + shift 1 + + if LOCK "$user_db"; then + if grep -q "^${user} " "$user_db"; then + RELEASE "$user_db" + return 1 + fi + printf '%s \\ %s \\ \\ \\ %i \\ \\\n' \ + "$user" "pending" "$(( _DATE + USER_CONFIRMEXPIRE ))" >>"$user_db" + else + return 1 + fi + + if [ $# -eq 0 ]; then + RELEASE "$user_db" + return 0 + elif update_user "$user" "$@"; then + return 0 + else + RELEASE "$user_db" + return 1 + fi +} + +user_idmap(){ + local uid="$1" ret + eval "$LOCAL_USER" + + if [ ! "$USER_IDMAP" ]; then + while read_user; do + USER_IDMAP="${USER_IDMAP}${USER_ID} ${USER_NAME}${BR}" + done <"$user_db" + fi + if [ "$uid" -a "$USER_IDMAP" != "${USER_IDMAP##*${uid} }" ]; then + ret="${USER_IDMAP##*${uid} }"; ret="${ret%%${BR}*}"; + printf '%s\n' "$ret" + return 0 + elif [ "$uid" ]; then + return 1 + else + printf '%s' "$USER_IDMAP" + return 0 + fi +} + +user_idof(){ + local name="$(STRING "$1")" ret + [ "$USER_IDMAP" ] || user_idmap >/dev/null + + if [ "${name%\\}" -a "$USER_IDMAP" != "${USER_IDMAP% ${name}${BR}*}" ]; then + ret="${USER_IDMAP% ${name}${BR}*}"; ret="${ret##*${BR}}" + printf '%s\n' "$ret" + return 0 + else + return 1 + fi } user_checkname(){ - { [ $# -gt 0 ] && printf %s "$*" || cat } \ + { [ $# -gt 0 ] && printf %s "$*" || cat; } \ | sed -nE ' :X; $!{N;bX;} s;[ \t\r\n]+; ;g; @@ -45,7 +201,7 @@ user_checkname(){ } user_checkemail(){ - { [ $# -gt 0 ] && printf %s "$*" || cat } \ + { [ $# -gt 0 ] && printf %s "$*" || cat; } \ | sed -nE ' # W3C recommended email regex # https://html.spec.whatwg.org/multipage/input.html#email-state-(type=email) @@ -55,8 +211,9 @@ user_checkemail(){ user_nameexist(){ local uname="$(STRING "$1")" - local UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE - while read -r UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE; do + 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 [ "$EXPIRE" -gt "$_DATE" -a "$UNAME" = "$uname" ] && return 0 done <"$user_db" return 1 @@ -64,8 +221,9 @@ user_nameexist(){ user_emailexist(){ local email="$(STRING "$1")" - local UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE - while read -r UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE; do + 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 [ "$EXPIRE" -gt "$_DATE" -a "$EMAIL" = "$email" ] && return 0 done <"$user_db" return 1 @@ -74,44 +232,22 @@ user_emailexist(){ user_pwhash(){ local salt="$1" secret="$2" hash hash="$(printf '%s\n%s\n' "$secret" "$salt" |sha256sum)" - printf '%s\n' "${hash% *}" + printf '%s\n' "${hash%% *}" } -user_register(){ - # reserve account, send registration mail - # preliminary uid, expiration, signature - local uid="$(timeid)" - local uname="$(POST uname |user_checkname)" - local email="$(POST email |user_checkemail)" - local pwsalt="$(randomid)" - local pw="$(POST pw |grep -m1 -xE '.{6,}' )" pwconfirm="$(POST pwconfirm)" - - if [ "$USER_REGISTRATION" != true ]; then - REDIRECT "${_BASE}${PATH_INFO}#ERROR_REGISTRATION_DISABLED" - fi - - if [ "$USER_REQUIREEMAIL" = true ]; then - if [ ! "email" ]; then - REDIRECT "${_BASE}${PATH_INFO}#ERROR_EMAIL_INVALID" - elif user_emailexist "$email"; then - REDIRECT "${_BASE}${PATH_INFO}#ERROR_EMAIL_EXISTS" - elif LOCK "$user_db"; then - printf '%s \\ pending %s \\ \\ %i \\ \\\n' \ - "$uid" "$(STRING "$email")" "$(( $_DATE + 86400 ))" \ - >>"$user_db" - RELEASE "$user_db" - sendmail -t -f "$MAILFROM" <<-EOF +user_register_email() { # TRANSLATION + "$SENDMAIL" -t -f "$MAILFROM" <<-EOF From: ${MAILFROM} - To: "${email}" + To: ${email} Subject: Your account registration at ${HTTP_HOST%:*} Someone tried to sign up for a user account using this email address. You can activate your account using this link: - https://${HTTP_HOST%:*}/${_BASE}/?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 @@ -121,12 +257,36 @@ user_register(){ This is an automatic email. Any direct reply will not be received. Your Account Registration Robot. EOF +} + +user_register(){ + # reserve account, send registration mail + # preliminary uid, expiration, signature + local uid="$(timeid)" + local uname="$(POST uname |user_checkname)" + local email="$(POST email |user_checkemail)" + local pwsalt="$(randomid)" + local pw="$(POST pw |grep -m1 -xE '.{6,}' )" pwconfirm="$(POST pwconfirm)" + + if [ "$USER_REGISTRATION" != true -a -s "$user_db" ]; then + REDIRECT "${_BASE}${PATH_INFO}#ERROR_REGISTRATION_DISABLED" + fi + + if [ "$USER_REQUIREEMAIL" = true ]; then + if [ ! "$email" ]; then + 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 + USER_CONFIRMEXPIRE))"; then + debug "Sending Activation Link:" \ + "${SCHEMA}://${HTTP_HOST}${_BASE}${PATH_INFO}?user_confirm=${uid}+$(session_mac "$uid")" + user_register_email REDIRECT "${_BASE}${PATH_INFO}#USER_REGISTER_CONFIRM" else REDIRECT "${_BASE}${PATH_INFO}#ERROR_USER_NOLOCK" fi - elif [ "$USER_REQUIREEMAIL" != true ] then + elif [ "$USER_REQUIREEMAIL" != true ]; then if [ ! "$uname" ]; then REDIRECT "${_BASE}${PATH_INFO}#ERROR_UNAME_INVALID" elif user_nameexist "$uname"; then @@ -135,33 +295,77 @@ user_register(){ REDIRECT "${_BASE}${PATH_INFO}#ERROR_PW_EMPTYTOOSHORT" elif [ "$pw" != "$pwconfirm" ]; then REDIRECT "${_BASE}${PATH_INFO}#ERROR_PW_MISMATCH" - elif LOCK "$user_db"; then - printf '%s %s active %s %s %s %i \\ \\\n' \ - "$uid" "$(STRING "$uname")" "$(STRING "$email")" \ - "$pwsalt" "$(user_pwhash "$pwsalt" "$pw")" \ - "$(( $_DATE + 86400 * 730 ))" \ - >>"$user_db" - RELEASE "$user_db" - + 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 fi } +user_invite_email(){ # TRANSLATION + "$SENDMAIL" -t -f "$MAILFROM" <<-EOF + From: ${MAILFROM} + To: ${email} + Subject: You have been invited to ${HTTP_HOST%:*} + + ${USER_NAME:-Someone} has offered an invitation to this email address. + + ${message} + + You can create your account using this link: + + ${SCHEMA}://${HTTP_HOST}${_BASE}${PATH_INFO}?user_confirm=${uid}+$(session_mac "$uid") + + 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 + simply ignore this message and we will remove your email address from + our database within the next day. + + This is an automatic email. Any direct reply will not be received. + Your Account Registration Robot. + EOF +} + +user_invite(){ + local uid="$(timeid)" + local email="$(POST email |user_checkemail)" + local message="$(POST message)" + + if [ ! "$email" ]; then + 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 + USER_CONFIRMEXPIRE))"; then + debug "Sending Invitation Link:" \ + "${SCHEMA}://${HTTP_HOST}${_BASE}${PATH_INFO}?user_confirm=${uid}+$(session_mac "$uid")" + user_invite_email + REDIRECT "${_BASE}${PATH_INFO}#USER_REGISTER_CONFIRM" + else + REDIRECT "${_BASE}${PATH_INFO}#ERROR_USER_NOLOCK" + fi +} + user_confirm(){ # enable account - local UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE - local uid="$(POST uid |checkid)" + eval "$LOCAL_USER" + local uid="$(POST uid |checkid || printf invalid)" local signature="$(POST signature)" local uname="$(POST uname |user_checkname)" local pwsalt="$(randomid)" local pw="$(POST pw |grep -m1 -xE '.{6,}' )" pwconfirm="$(POST pwconfirm)" + read_user "${uid}" + if [ "$signature" != "$(session_mac "$uid")" ]; then REDIRECT "${_BASE}${PATH_INFO}?${QUERY_STRING}#ERROR_LINK_INVALID" elif [ ! "$uname" ]; then @@ -172,27 +376,15 @@ user_confirm(){ REDIRECT "${_BASE}${PATH_INFO}?${QUERY_STRING}#ERROR_PW_EMPTYTOOSHORT" elif [ "$pw" != "$pwconfirm" ]; then REDIRECT "${_BASE}${PATH_INFO}?${QUERY_STRING}#ERROR_PW_MISMATCH" - elif LOCK "$user_db"; then - read -r UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE <<-EOF - $(grep "^${uid} " "$user_db") - EOF - - if [ "$STATUS" != pending -o "$EXPIRE" -le "$_DATE" ]; then - RELEASE "$user_db" - REDIRECT "${_BASE}${PATH_INFO}?${QUERY_STRING}#ERROR_LINK_INVALID" + elif [ "$USER_STATUS" != pending -o \! "$USER_EXPIRE" -gt "$_DATE" ]; then + REDIRECT "${_BASE}${PATH_INFO}?${QUERY_STRING}#ERROR_LINK_INVALID" + elif update_user "$USER_ID" uname="$uname" status=active password="$pw"; then + SESSION_COOKIE new + SESSION_BIND user_id "$USER_ID" + if [ "$USER_ACCOUNTPAGE" ]; then + REDIRECT "${USER_ACCOUNTPAGE}" else - printf '%s %s active %s %s %s %i %s %s\n' \ - "$UID" "$(STRING "$uname")" "$EMAIL" \ - "$pwsalt" "$(user_pwhash "$pwsalt" "$pw")" \ - "$(( $_DATE + 86400 * 730 ))" "$DEVICES" "$FUTUREUSE" \ - >"${user_db}.$$" - grep -v "^${uid} " "$user_db" >>"${user_db}.$$" - mv "${user_db}.$$" "${user_db}" - RELEASE "$user_db" - - SESSION_COOKIE new - SESSION_BIND user_id "$UID" - REDIRECT "${_BASE}${PATH_INFO}#USER_REGISTER_CONFIRM" + REDIRECT "${_BASE}${PATH_INFO}?user_register=confirm#USER_REGISTER_CONFIRM" fi else REDIRECT "${_BASE}${PATH_INFO}#ERROR_USER_NOLOCK" @@ -203,14 +395,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)" - while read -r UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE; do + [ -f "$user_db" -a -r "$user_db" ] \ + && 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 @@ -222,44 +415,99 @@ user_logout(){ # destroy cookie, destroy session # keep device cookie new_session - SET_COOKIE 0 session="" - SET_COOKIE 0 user_id="" + SESSION_COOKIE new + SET_COOKIE 0 user_id="" Path="/${_BASE#/}" SameSite=Strict HttpOnly REDIRECT "${_BASE}${PATH_INFO}#USER_LOGGED_OUT" } 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 -m1 -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_PW_MISMATCH" + 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 + : } user_disable(){ + : } -user_init +read_user "$(SESSION_VAR user_id)" +[ "$USER_STATUS" -a "$USER_STATUS" != active ] && eval $UNSET_USER [ "$REQUEST_METHOD" = POST ] && case "$(POST action)" in user_register) user_register ;; user_confirm) user_confirm ;; + user_invite) user_invite ;; user_login) user_login ;; user_logout) user_logout ;; - user_update) - :;; + user_update) user_update ;; user_recover) :;; user_disable) :;; esac -w_user_register(){ - if [ "$USER_REGISTRATION" != true ]; then +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_disabled(){ # TRANSLATION + cat <<-EOF [div #user_register .disabled User Registration is disabled. ] EOF - elif [ "$USER_REQUIREEMAIL" = true ]; then - cat <<-EOF +} +w_user_register_sendmail(){ # TRANSLATION + cat <<-EOF [form #user_register .registeremail method=POST [p We will send an activation mail to your email address. You can continue the signup process when you click on the @@ -268,70 +516,146 @@ w_user_register(){ [submit "action" "user_register" Sign Up] ] EOF - elif [ "$USER_REQUIREEMAIL" != true ]; then - cat <<-EOF +} +w_user_register_direct(){ # TRANSLATION + 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 type=pw placeholder="Choose Passphrase" pattern=".{4,}"] - [input type=pwconfirm placeholder="Confirm Passphrase" pattern=".{4,}"] + [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] ] EOF - fi } -w_user_confirm(){ - local UID UNAME STATUS EMAIL PWSALT PWHASH EXPIRE DEVICES FUTUREUSE - local user_confirm="$(GET user_confirm)" - local uid="${user_confirm% *}" signature="${user_confirm#* }" +w_user_register(){ + if [ "$(GET user_confirm)" ]; then + w_user_confirm + elif [ "$USER_REGISTRATION" != true -a -s "$user_db" ]; then + w_user_register_disabled + elif [ "$USER_REQUIREEMAIL" = true ]; then + w_user_register_sendmail + elif [ "$USER_REQUIREEMAIL" != true ]; then + w_user_register_direct + fi +} - if [ "$signature" = "$(session_mac "$uid")" ]; then - 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 - cat <<-EOF +w_user_confirm_proceed(){ # TRANSLATION + cat <<-EOF [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] - [input type=pw placeholder="Choose Passphrase" pattern=".{4,}"] - [input type=pwconfirm placeholder="Confirm Passphrase" pattern=".{4,}"] + $([ "$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] ] EOF - else - cat <<-EOF +} +w_user_confirm_expired(){ # TRANSLATION + cat <<-EOF [div #user_confirm .expired [p This activation link is not valid anymore.] ] EOF - fi - else - cat <<-EOF +} +w_user_confirm_invalid(){ # TRANSLATION + cat <<-EOF [div #user_confirm .invalid [p This activation link is invalid. Make sure you copied the whole activation link from your email and be careful not to include any line breaks.] ] EOF +} + +w_user_confirm(){ + 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 + $(grep "^${uid} " "$user_db") + EOF + if [ "$STATUS" = pending -a "$EXPIRE" -gt "$_DATE" ]; then + w_user_confirm_proceed + else + w_user_confirm_expired + fi + else + w_user_confirm_invalid fi } -w_user_login(){ - if [ ! "$USER_ID" ]; then - cat <<-EOF +w_user_invite_email(){ # TRANSLATION + cat <<-EOF + [form #user_invite method=POST + [input placeholder="Email Recipient" name=email autocomplete=off] + [textarea name="message" placeholder="Message to recipient" . ] + [submit "action" "user_invite" Send Invitation] + ] + EOF +} +w_user_invite_link(){ # TRANSLATION + 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 +} +w_user_invite_deny(){ # TRANSLATION + cat <<-EOF + [div #user_invite .notallowed + Only registered users may send an invitation to another user. + ] + EOF +} + +w_user_invite(){ + local uid invlink + + if [ "$(GET user_confirm)" ]; then + w_user_confirm + elif [ "$USER_ID" -a "$USER_REQUIREEMAIL" = true ]; then + w_user_invite_email + 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" + w_user_invite_link + else + w_user_invite_deny + fi +} + +w_user_login_logon(){ # TRANSLATION + cat <<-EOF [form #user_login .login method=POST - [input name=uname placeholder="Username or Email" autocomplete=off] + [input name=uname placeholder="Username or Email"] [input type=password name=pw placeholder="Passphrase"] [submit "action" "user_login" Login] ] EOF - elif [ "$USER_ID" ]; then - cat <<-EOF +} +w_user_login_logoff(){ # TRANSLATION + cat <<-EOF [form #user_login .logout method=POST - [p You are currently logged in as "${USER_NAME}"] + [p Logged in as [span . $(HTML ${USER_NAME})]] [submit "action" "user_logout" Logout] ] EOF +} + +w_user_login(){ + if [ ! "$USER_ID" ]; then + w_user_login_logon + elif [ "$USER_ID" ]; then + w_user_login_logoff fi }