# 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:
# ==========================
# - [x] Automatic heading identifiers (custom)
# - [x] Fenced code blocks (php md, pandoc)
# - [x] Fenced code attributes
-# - [ ] Tables
+# - [/] Tables
# - ? Simple table (pandoc)
# - ? Multiline table (pandoc)
-# - ? Grid table (pandoc)
-# - ? Pipe table (php md pandoc)
+# - [x] Grid table (pandoc)
+# - [x] Pipe table (php md pandoc)
# - [x] Line blocks (pandoc)
-# - [x] Task lists (pandoc)
+# - [x] Task lists (pandoc, custom)
# - [ ] Definition lists (php md, pandoc)
# - [-] Numbered example lists (pandoc)
# - [-] Metadata blocks (pandoc)
+# - [x] Metadata blocks (custom)
# - [x] Fenced Divs (pandoc)
#
# Extensions - Inline elements:
href = HTML( substr( line, 2, len - 2) );
return "<a href=\"" href "\">" href "</a>" 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 "<a href=\"mailto:" href "\">" href "</a>" 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 "<a href=\"" HTML(href) "\" title=\"" HTML(title) "\">" inline( text ) "</a>" inline( substr( line, len + 1) );
} else {
# 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 "<a href=\"" HTML(rl_href[id]) "\" title=\"" HTML(rl_title[id]) "\">" inline(text) "</a>" 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 "<img src=\"" HTML(href) "\" alt=\"" HTML(text) "\" title=\"" HTML(title) "\" />" inline( substr( line, len + 1) );
} else {
# 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 "<img src=\"" HTML(rl_href[id]) "\" alt=\"" HTML(text) "\" title=\"" HTML(rl_title[id]) "\" />" inline( substr( line, len + 1) );
} 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, /^> /) ) {
return "<blockquote>\n" _block( gensub( /(^|\n)> /, "\n", "g", substr(block, 1, st - 1) ) ) "</blockquote>\n\n" \
_block( substr(block, st + len) );
+ # Pipe Tables (pandoc / php md / gfm )
+ } else if ( match(block, "^((\\|)?([^\n]+\\|)+[^\n]+(\\|)?)\n" \
+ "((\\|)?:?(-+:?[\\|+])+:?-+:?(\\|)?)\n" \
+ "((\\|)?([^\n]+\\|)+[^\n]+(\\|)?(\n|$))+" ) ) {
+ len = RLENGTH; st = RSTART;
+ #initialize empty arrays
+ split("", talign); split("", tarray);
+ cols = 0; cnt=0; ttext = "";
+
+ # table header and alignment
+ split( gensub( /(^\||\|$)/, "", "g", \
+ gensub( /(^|[^\\])\\\|/, "\\1\\|", "g", \
+ substr(block, 1, match(block, /(\n|$)/)) \
+ )), tarray, /\|/);
+ block = substr(block, match(block, /(\n|$)/) + 1 );
+ cols = split( \
+ gensub( /(^\||\|$)/, "", "g", \
+ substr(block, 1, match(block, /(\n|$)/)) \
+ ), talign, /[+\|]/);
+ block = substr(block, match(block, /(\n|$)/) + 1 );
+
+ for( cnt = 1; cnt < cols; cnt++ ) {
+ if (match(talign[cnt], /:-+:/)) talign[cnt]="center";
+ else if (match(talign[cnt], /-+:/)) talign[cnt]="right";
+ else if (match(talign[cnt], /:-+/)) talign[cnt]="left";
+ else talign[cnt]="";
+ }
+
+ ttext = "<thead>\n<tr>"
+ for (cnt = 1; cnt < cols; cnt++)
+ ttext = ttext "<th align=\"" talign[cnt] "\">" inline(tarray[cnt]) "</th>"
+ ttext = ttext "</tr>\n</thead><tbody>\n"
+
+ while ( match(block, "^((\\|)?([^\n]+\\|)+[^\n]+(\\|)?(\n|$))+" ) ){
+ split( gensub( /(^\||\|$)/, "", "g", \
+ gensub( /(^|[^\\])\\\|/, "\\1\\|", "g", \
+ substr(block, 1, match(block, /(\n|$)/)) \
+ )), tarray, /\|/);
+ block = substr(block, match(block, /(\n|$)/) + 1 );
+
+ ttext = ttext "<tr>"
+ for (cnt = 1; cnt < cols; cnt++)
+ ttext = ttext "<td align=\"" talign[cnt] "\">" inline(tarray[cnt]) "</td>"
+ ttext = ttext "</tr>\n"
+ }
+ return "<table>" ttext "</tbody></table>\n" _block(block);
+
+ # Grid Tables (pandoc)
+ } else if ( match(block, "^\\+(-+\\+)+\n" \
+ "(\\|([^\n]+\\|)+\n)+" \
+ "\\+(:?=+:?\\+)+\n" \
+ "((\\|([^\n]+\\|)+\n)+" \
+ "\\+(-+\\+)+(\n|$))+" \
+ ) ) {
+ len = RLENGTH; st = RSTART;
+ #initialize empty arrays
+ split("", talign); split("", tarray); split("", tread);
+ cols = 0; cnt=0; ttext = "";
+
+ # table header and alignment
+ block = substr(block, match(block, /(\n|$)/) + 1 );
+ while ( match(block, "^\\|([^\n]+\\|)+\n") ) {
+ cols = split( gensub( /(^\||\|$)/, "", "g", \
+ gensub( /(^|[^\\])\\\|/, "\\1\\|", "g", \
+ substr(block, 1, match(block, /(\n|$)/)) \
+ )), tread, /\|/);
+ block = substr(block, match(block, /(\n|$)/) + 1 );
+ for (cnt = 1; cnt < cols; cnt++)
+ tarray[cnt] = tarray[cnt] "\n" tread[cnt];
+ }
+
+ cols = split( \
+ gensub( /(^\+|\+$)/, "", "g", \
+ substr(block, 1, match(block, /(\n|$)/)) \
+ ), talign, /\+/);
+ block = substr(block, match(block, /(\n|$)/) + 1 );
+
+ for (cnt = 1; cnt < cols; cnt++) {
+ if (match(talign[cnt], /:=+:/)) talign[cnt]="center";
+ else if (match(talign[cnt], /=+:/)) talign[cnt]="right";
+ else if (match(talign[cnt], /:=+/ )) talign[cnt]="left";
+ else talign[cnt]="";
+ }
+
+ ttext = "<thead>\n<tr>"
+ for (cnt = 1; cnt < cols; cnt++)
+ ttext = ttext "<th align=\"" talign[cnt] "\">" _block(tarray[cnt]) "</th>"
+ ttext = ttext "</tr>\n</thead><tbody>\n"
+
+ while ( match(block, /^((\|([^\n]+\|)+\n)+\+(-+\+)+(\n|$))+/ ) ){
+ split("", tarray);
+ while ( match(block, /^\|([^\n]+\|)+\n/) ) {
+ split( gensub( /(^\||\|$)/, "", "g", \
+ gensub( /(^|[^\\])\\\|/, "\\1\\|", "g", \
+ substr(block, 1, match(block, /(\n|$)/)) \
+ )), tread, /\|/);
+ block = substr(block, match(block, /(\n|$)/) + 1 );
+ for (cnt = 1; cnt < cols; cnt++)
+ tarray[cnt] = tarray[cnt] "\n" tread[cnt];
+ }
+ block = substr(block, match(block, /(\n|$)/) + 1 );
+
+ ttext = ttext "<tr>"
+ for (cnt = 1; cnt < cols; cnt++)
+ ttext = ttext "<td align=\"" talign[cnt] "\">" _block(tarray[cnt]) "</td>"
+ ttext = ttext "</tr>\n"
+ }
+ return "<table>" ttext "</tbody></table>\n" _block(block);
+
# Line Blocks (pandoc)
} else if ( match(block, /^\| [^\n]*(\n|$)(\| [^\n]*(\n|$)|[ \t]+[^\n[:space:]][^\n]*(\n|$))*/) ) {
len = RLENGTH; st = RSTART;
_block( substr( block, len + 1) );
# Indented Code Block
- } else if ( match(block, /^( |\t)[^\n]+(\n|$)(( |\t)[^\n]+(\n|$)|[ \t]*(\n|$))*/) ) {
+ } else if ( match(block, /^( |\t)( *\t*[^ \t\n]+ *\t*)+(\n|$)(( |\t)[^\n]+(\n|$)|[ \t]*(\n|$))*/) ) {
len = RLENGTH; st = RSTART;
code = substr(block, 1, len);
gsub(/(^|\n)( |\t)/, "\n", code);
} 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);
+ attrib = gensub(/^(~~~+|```+)[ \t]*\{?[ \t]*([^\}\n]*)\}?[ \t]*\n.*$/, "\\2", 1, block);
gsub(/[^a-zA-Z0-9_-]+/, " ", attrib);
gsub(/(^ | $)/, "", attrib);
if ( match(code, "(^|\n)" guard "+(\n|$)" ) ) {
} else if ( match( block, /^[^\n]+\n===+(\n|$)/ ) ) {
len = RLENGTH;
HL[1]++; HL[2] = 0; HL[3] = 0; HL[4] = 0; HL[5] = 0; HL[6] = 0;
- return "<h1 id=\"" HL[1] " - " HTML(gensub( /\n.*$/, "", "g", block )) "\">" inline( gensub( /\n.*$/, "", "g", block ) ) "</h1>\n\n" \
+ return "<h1 id=\"" HL[1] " - " HTML(gensub( /\n.*$/, "", "g", block )) "\">" \
+ inline( gensub( /\n.*$/, "", "g", block ) ) \
+ "<a class=\"anchor\" href=\"#" HL[1] " - " \
+ HTML(gensub( /\n.*$/, "", "g", block )) "\"></a></h1>\n\n" \
_block( substr( block, len + 1 ) );
# Second Order Heading
} else if ( match( block, /^[^\n]+\n---+(\n|$)/ ) ) {
len = RLENGTH;
HL[2]++; HL[3] = 0; HL[4] = 0; HL[5] = 0; HL[6] = 0;
- return "<h2 id=\"" HL[1] "." HL[2] " - " HTML(gensub( /\n.*$/, "", "g", block )) "\">" inline( gensub( /\n.*$/, "", "g", block ) ) "</h2>\n\n" \
+ return "<h2 id=\"" HL[1] "." HL[2] " - " HTML(gensub( /\n.*$/, "", "g", block )) "\">" \
+ inline( gensub( /\n.*$/, "", "g", block ) ) \
+ "<a class=\"anchor\" href=\"#" HL[1] "." HL[2] " - " \
+ HTML(gensub( /\n.*$/, "", "g", block )) "\"></a></h2>\n\n" \
_block( substr( block, len + 1) );
# Nth Order Heading
htxt = gensub(/^#{1,6}[ \t]*(([^ \t\n]+|[ \t]+[^ \t\n#]|[ \t]+#+[^\n#])+)([ \t]*#*)(\n.*)?$/, "\\1", 1, block);
HL[hlvl]++; for ( n = hlvl + 1; n < 7; n++) { HL[n] = 0;}
hid = HL[1]; for ( n = 2; n <= hlvl; n++) { hid = hid "." HL[n] ; }
- return "<h" hlvl " id=\"" hid " - " HTML(htxt) "\">" inline( htxt ) "</h" hlvl ">\n\n" \
+ return "<h" hlvl " id=\"" hid " - " HTML(htxt) "\">" inline( htxt ) \
+ "<a class=\"anchor\" href=\"#" hid "\"></a></h" hlvl ">\n\n" \
_block( substr( block, len + 1) );
# Split paragraphs
}
sub( /\n$/, "", p );
- # Task List (pandoc)
- if ( p ~ /^\[ \].*/ ) { p = "<input type=checkbox disabled />" substr(p, 4); }
- else if ( p ~ /^\[[xX]\].*/ ) { p = "<input type=checkbox disabled checked />" substr(p, 4); }
- else if ( p ~ /^<p>\[ \].*/ ) { p = "<p><input type=checkbox disabled />" substr(p, 7); }
- else if ( p ~ /^<p>\[[xX]\].*/ ) { p = "<p><input type=checkbox disabled checked />" substr(p, 7); }
- return "<li>" p "</li>\n" _list( block, last );
+ # Task List (pandoc, custom)
+ if ( p ~ /^\[ \].*/ ) { return "<li class=\"task pending\"><input type=checkbox disabled />" \
+ substr(p, 4) "</li>\n" _list( block, last );
+ } else if ( p ~ /^\[-\].*/ ) { return "<li class=\"task negative\"><input type=checkbox disabled />" \
+ substr(p, 4) "</li>\n" _list( block, last );
+ } else if ( p ~ /^\[\?\].*/ ) { return "<li class=\"task unsure\"><input type=checkbox disabled />" \
+ substr(p, 4) "</li>\n" _list( block, last );
+ } else if ( p ~ /^\[\/\].*/ ) { return "<li class=\"task partial\"><input type=checkbox disabled />" \
+ substr(p, 4) "</li>\n" _list( block, last );
+ } else if ( p ~ /^\[[xX]\].*/ ) { return "<li class=\"task done\"><input type=checkbox disabled checked />" \
+ substr(p, 4) "</li>\n" _list( block, last );
+ } else if ( p ~ /^<p>\[ \].*/ ) { return "<li class=\"task pending\"><p><input type=checkbox disabled />" \
+ substr(p, 7) "</li>\n" _list( block, last );
+ } else if ( p ~ /^<p>\[-\].*/ ) { return "<li class=\"task negative\"><p><input type=checkbox disabled />" \
+ substr(p, 7) "</li>\n" _list( block, last );
+ } else if ( p ~ /^<p>\[\?\].*/ ) { return "<li class=\"task unsure\"><p><input type=checkbox disabled />" \
+ substr(p, 7) "</li>\n" _list( block, last );
+ } else if ( p ~ /^<p>\[\/\].*/ ) { return "<li class=\"task partial\"><p><input type=checkbox disabled />" \
+ substr(p, 7) "</li>\n" _list( block, last );
+ } else if ( p ~ /^<p>\[[xX]\].*/ ) { return "<li class=\"task done\"><p><input type=checkbox disabled checked />" \
+ substr(p, 7) "</li>\n" _list( block, last );
+ } else { return "<li>" p "</li>\n" _list( block, last ); }
}
BEGIN {
. "${_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
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
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
:
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}.$$"
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
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
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
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%:*}
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
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
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%:*}
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
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
# 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
}
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
:
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
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]
}
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
[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]
}
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]
[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