From 1b78a6c60a0c44a164dcf12f1e7683008f2cf402 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Paul=20H=C3=A4nsch?= Date: Sat, 23 Nov 2019 07:39:48 +0100 Subject: [PATCH] introduce nickname registration --- channel.sh | 81 +++++++++++++++++++--------------------------- index.cgi | 74 +++++++++++++++++++++++------------------- usernick.sh | 69 +++++++++++++++++++++++++++++++++++++++ webchat.css | 93 +++++++++++++++++++++++++++++++++-------------------- 4 files changed, 201 insertions(+), 116 deletions(-) create mode 100755 usernick.sh diff --git a/channel.sh b/channel.sh index eab42fc..1f86e52 100755 --- a/channel.sh +++ b/channel.sh @@ -1,11 +1,5 @@ #!/bin/sh -if [ "$(COOKIE nick)" ]; then - nickname="?$(COOKIE nick)" -else - nickname='?Guest' -fi - if [ -f "$chatfile" ]; then read -r channelkey x <"$chatfile" channelkey="$( printf '%s-%s' "$channelkey" "$SESSION_ID" |sha256sum)" @@ -24,47 +18,38 @@ case $(POST action) in fi REDIRECT "$(URL "/$LOCATION")" ;; - nick) - SET_COOKIE +1209600 "nick=$(POST nickname |URL)" - REDIRECT "$(URL "/$LOCATION")" - ;; - *) if [ ! -f "$chatfile" ]; then - yield_page create <<-EOF - [form #nonexist method=POST action="$(URL "/$LOCATION")" - There is no channel named $(HTML "$LOCATION") - [submit "action" "create" Create] - ] +esac + +if [ ! -f "$chatfile" ]; then + yield_page create <<-EOF + [form #nonexist method="POST" + There is no channel named $(HTML "$LOCATION") + [submit "action" "create" Create] + ] EOF - else - { printf ' - [form #channel method=POST action="%s" - [submit "action" "submit" style="display: none;"] - [input type=hidden name=channelkey value="%s"] - %s [input name="message" autofocus=true][submit "action" "submit" Send!] - ] - ' "$(URL "/$LOCATION")" "$channelkey" "$(settings_menu)" - SHESCAPE='s;[]&<>#."[];\\&;g;' +else + printf '%s: %s\r\n' Refresh 1 + { printf ' + [form #channel method="POST" + [submit "action" "submit" style="display: none;"] + [input type=hidden name=channelkey value="%s"] + [a .settings href="?settings#nick" Settings][input autocomplete="off" name="message" autofocus=true][submit "action" "submit" Send!] + ] + ' "$channelkey" + SHESCAPE='s;[]&<>#."[];\\&;g;' - printf '[div #chat' - # tail -n30 -f "$chatfile" | { - # read x - # while read -r date nick message; do - # printf '[p .message [span .date %s] [span .nick [span .indicator %s]%s:] [span .message %s]]\n' \ - # "${date#*_}" "${nick%${nick#?}}" "$(UNSTRING "${nick#?}" |HTML)" "$(UNSTRING "$message" |HTML)" - # done - # } - tail -n50 -f "$chatfile" \ - | sed -nuE ' - /^[^ ]+ [^ ]+ [^ ]+$/{ - h; s;^([^ ]+) ([^ ]+) ([^ ]+)$;\1;; s;.*_;;; s;.+;[p .message [span .date &];p; - g; s;^([^ ]+) ([^ ]+) ([^ ]+)$;a\2;; bESC; :A s;.;;; s;(.)(.+);[span .nick [span .indicator \1]\2];p; - g; s;^([^ ]+) ([^ ]+) ([^ ]+)$;b\3;; bESC; :B s;.;;; s;.+;[span .message &]];p; - } - b; :ESC - '"$UNSTRING"' '"$SHESCAPE"' - /^a/bA; /^b/bB; - ' - } |yield_page channel - fi - ;; -esac + while sleep 10; do printf '\n'; done & + printf '[div #chat' + tail --pid $$ -n50 -f "$chatfile" \ + | sed -nuE ' + /^[^ ]+ [^ ]+ [^ ]+$/{ + h; s;^([^ ]+) ([^ ]+) ([^ ]+)$;\1;; s;.*_;;; s;.+;[p .message [span .date &];p; + g; s;^([^ ]+) ([^ ]+) ([^ ]+)$;a\2;; bESC; :A s;.;;; s;(.)(.+);[span .nick [span .indicator \1]\2];p; + g; s;^([^ ]+) ([^ ]+) ([^ ]+)$;b\3;; bESC; :B s;.;;; s;.+;[span .message &]];p; + } + b; :ESC + '"$UNSTRING"' '"$SHESCAPE"' + /^a/bA; /^b/bB; + ' + } |yield_page channel +fi diff --git a/index.cgi b/index.cgi index 2daabd8..16bd904 100755 --- a/index.cgi +++ b/index.cgi @@ -21,6 +21,7 @@ yield_page(){ [title Webchat] ] [body class="%s" ' "$page" + [ "$QUERY_STRING" = settings ] && settings_menu cat printf '] ]' } |"$_EXEC/cgilite/html-sh.sed" -u @@ -28,48 +29,55 @@ yield_page(){ settings_menu(){ printf ' - [input #check_settings type="checkbox"][label for=check_settings Settings] - [div #settings - [h1 Settings][label for=check_settings Close] - [input #set_nick type=radio name="setting" value="nick" selected][label for=set_nick Nickname] - [div [input name="nickname" value="%s"][submit "action" "nick" Set Cookie]] - ] + [form #settings method="POST" action="?" + [h1 Settings][a .settings href="?" Close]' + printf ' + [a .section href="#nick" Nickname] + [div #nick [input name="nickname" value="%s"][submit "action" "nick" Set Cookie]] ' "$(HTML "${nickname#\?}")" + printf ' + [a .section href="#register" Register Nickname] + [div #register + [p Registration will set a permanent Cookie in your Browser. + Registration requires neither a password, nor an email address.] + [input name="regnick" value="%s"][submit "action" "register" Register] + ]' "$(HTML "${nickname#\?}")" + printf ']' } +. "$_EXEC/usernick.sh" + case ${LOCATION} in - \&?*) chatfile="$_DATA/${LOCATION}" - . $_EXEC/channel.sh - exit 0 - ;; - @?*) if [ -d "$_DATA/${LOCATION}" ]; then - chatfile="$_DATA/${LOCATION}/?${SESSION_ID}" - . $_EXEC/channel.sh - else - REDIRECT / - fi - exit 0 - ;; - ~?*) if [ -d "$_DATA/@${LOCATION#~}" ]; then - pubinfo="$_DATA/@${LOCATION#~}/pubinfo" - page=pubinfo - else - REDIRECT / - fi - ;; webchat.css) - . "$_EXEC/file.sh" + . "$_EXEC/cgilite/file.sh" FILE "$_EXEC/webchat.css" exit 0 ;; - '') page=front;; - *) REDIRECT /;; -esac - -case "$page $(POST action)" in - pubinfo\ *);; - front\ *) yield_page front <<-EOF + \&?*) + chatfile="$_DATA/${LOCATION}" + . "$_EXEC/channel.sh" + exit 0 + ;; + @?*) + if [ -d "$_DATA/${LOCATION}" ]; then + chatfile="$_DATA/${LOCATION}/?${SESSION_ID}" + . "$_EXEC/channel.sh" + else + REDIRECT / + fi + exit 0 + ;; + ~?*) + if [ -d "$_DATA/@${LOCATION#~}" ]; then + pubinfo="$_DATA/@${LOCATION#~}/pubinfo" + else + REDIRECT / + fi + ;; + '') yield_page front <<-EOF Front EOF ;; + *) REDIRECT / + ;; esac diff --git a/usernick.sh b/usernick.sh new file mode 100755 index 0000000..8d62390 --- /dev/null +++ b/usernick.sh @@ -0,0 +1,69 @@ +#!/bin/sh + +UNAME_VALID=' + # Remove trailing CR, which may have been added by browser + s;\r$;;; + # Collapse white spaces + s;[\r\t\n ]+; ;; + # Remove starting and trailing white spaces + s;^ ;;; s; $;;; + # Usernames starting with & # ? @ + will be invalid + /^[&#?@+]/d; + # Usernames containing a / will be invalid + /\//d; + # Usernames must be between 3 and 24 characters + /...+/!d; /.{25}/d; + # Usernames may not span multiple lines + q;' +username(){ + { [ $# -eq 0 ] && cat || printf %s "$*"; } \ + | sed -E ':X; $!{N;bX;}'"$UNAME_VALID" +} + +nickname="$(COOKIE nick |username)" +if [ ! "$nickname" ]; then + nickname='?Guest' +elif [ ! -d "$_DATA/@$nickname" ]; then + nickname="?$nickname" +else + userclient="$(COOKIE user_client)" + secuid="$(cat "$_DATA/@$nickname/secuid")" + clientid="${userclient%%-*}" + clientid="${clientid}-$(printf '%s%s' "${clientid}" "${secuid}" |sha256sum)" + clientid="${clientid%% *}" + if [ "$clientid" = "$userclient" ]; then + nickname=" $nickname" + SET_COOKIE +"$((86400 * 365))" "user_client=${clientid}" HttpOnly + SET_COOKIE +"$((86400 * 365))" "nick=$(URL "${nickname}")" + else + nickname='?Guest' + fi +fi + +case $(POST action) in + nick) + nick="$(POST nickname |username)" + if [ ! -d "$_DATA/@$nick" ]; then + SET_COOKIE +1209600 "nick=$(POST nickname |URL)" + REDIRECT "$(URL "/$LOCATION")" + else + # ToDo: Return Error Message + REDIRECT "$(URL "/$LOCATION")?settings#nick" + fi + ;; + register) + regnick="$(POST regnick |username)" + userdir="$_DATA/@${regnick}" + if [ "$regnick" ] && mkdir "$userdir"; then + secuid="$(randomid)"; clientid="$(randomid)" + printf %s\\n "$secuid" >"${userdir}/secuid" + clientid="${clientid}-$(printf '%s%s' "${clientid}" "${secuid}" |sha256sum |cut -d\ -f1)" + SET_COOKIE +"$((86400 * 365))" "user_client=${clientid}" HttpOnly + SET_COOKIE +"$((86400 * 365))" "nick=$(URL "${regnick}")" + REDIRECT "$(URL "/$LOCATION")" + else + # ToDo: Return Error Message + REDIRECT "$(URL "/$LOCATION")?settings#register" + fi + ;; +esac diff --git a/webchat.css b/webchat.css index 4f36157..ff70761 100644 --- a/webchat.css +++ b/webchat.css @@ -1,41 +1,32 @@ * { box-sizing: border-box; - background-color: #FFF; - color: #000; font: normal normal normal medium/1.25 Sans-Serif; font: normal normal normal normal medium/1.25 Sans-Serif; text-decoration: none; margin: 0; padding: 0; border: none; + color: inherit; } -#chat { - position: fixed; - bottom: 2.5em; - left: 0; right: 0; - border: 1px solid #08b; - padding: 1ex; - margin: .5ex; - z-index: -1; -} -form#channel { - position: fixed; - bottom: 0; - left: 0; right: 0; +body { + background-color: #FFF; + color: #000; } -#check_settings { display: none; } -#check_settings + label { - display: inline-block; +b, strong { font-weight: bold; } +i, em { font-style: italic; } + +input[type=text], input:not([type]) { + border: 1px solid #08b; + padding: .125ex .5ex; } -#check_settings + label:before { - content: '\2699'; - padding: .5ex; - margin-left: .5ex; - margin-right: 2em; +button { + border: outset #DDD; + padding: .125ex 1ex; + background-color: #EEE; } -#check_settings + label + #settings { display: none; } -#check_settings:checked + label + #settings { + +#settings { display: block; position: fixed; min-width: 20%; max-width: 90%; @@ -55,7 +46,7 @@ form#channel { border-bottom: 1px solid; border-radius: 1ex 1ex 0 0; } -#settings label[for=check_settings] { +#settings a.settings { position: absolute; top: 0; right: 1px; background-color: #F88; @@ -64,39 +55,71 @@ form#channel { width: 3ex; overflow: hidden; } -#settings label[for=check_settings]:before { +#settings a.settings:before { content: "x"; padding: 0 1ex; } -#settings input[type=radio] { display: none; } -#settings input[type=radio] + label + * { display: none; } -#settings input[type=radio]:checked + label + * { display: block; } -#settings input[type=radio] + label { +#settings a.section { display: block; font-weight: bold; text-decoration: underline; - margin: -1px 1px; padding: 0 1ex; + margin: -1px 1px 0 0; padding: .5ex 1ex; border-top: 1px solid; background-color: #EEE; } -#settings input[type=radio] + label + * { +#settings a.section + * { + display: block; + padding: .5ex 1ex 0 1ex; + max-height: .5ex; + overflow: hidden; + transition: max-height .5s; +} +#settings a.section + *:target { + max-height: 20ex; padding: 1ex 1ex .5ex 1ex; } +#settings input {margin-right: 1ex;} +form#channel { + position: fixed; + bottom: 1ex; + left: .5ex; right: .5ex; +} +form#channel a.settings { + display: inline-block; +} +form#channel a.settings:before { + content: '\2699'; + padding: 0 .75ex; + margin-left: .5ex; + margin-right: 2em; +} form#channel input[name=message] { display: inline-block; position: absolute; - left: 4.5ex; - width: calc(100% - 5ex - 1px); + right: 0; + width: calc(100% - 4.5ex); } form#channel button[value=submit] { display: none; } #chat .message .date { color: #888; font-size: .75em; } + +#chat { + position: fixed; + bottom: 2em; + left: 0; right: 0; + border: 1px solid #08b; + padding: 1ex; + margin: .5ex; + z-index: -1; +} + #chat .message .nick { font-weight: bold; } + #chat .message .nick .indicator { color: #888; } -- 2.39.2