From 75ca4944241b423b42ae7ad811a0e5fbfc4d428d Mon Sep 17 00:00:00 2001 From: Uriel Date: Fri, 30 Jan 2009 15:18:07 +0100 Subject: [PATCH 01/12] Cosmetic improvements to login form and error msg. --- lib/_users/login.tpl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/_users/login.tpl b/lib/_users/login.tpl index d705451..0edad32 100644 --- a/lib/_users/login.tpl +++ b/lib/_users/login.tpl @@ -5,9 +5,9 @@ % } % if not { % if (~ $REQUEST_METHOD POST) -% echo 'Login failed!' -
-
+% echo '
Login failed!
' + +
User password:
From f71c7866f4a93004c3804b1517a036d7599a75a6 Mon Sep 17 00:00:00 2001 From: Uriel Date: Fri, 30 Jan 2009 16:10:14 +0100 Subject: [PATCH 02/12] New version of comments module, must more reliable and polished. --- apps/bridge/app.rc | 118 ++++++++++++++++++++++++++++++++++--------- apps/bridge/foot.tpl | 39 +++++++------- 2 files changed, 113 insertions(+), 44 deletions(-) diff --git a/apps/bridge/app.rc b/apps/bridge/app.rc index da1be48..c46996d 100644 --- a/apps/bridge/app.rc +++ b/apps/bridge/app.rc @@ -1,30 +1,98 @@ comment_file_types=(md html) -fn bridge_init { - if(! ~ $#enable_comments 0 && ! ~ `{ls $local_path.$comment_file_types >[2]/dev/null|wc -l} 0) { - ll_add handlers_body_foot template apps/bridge/foot.tpl - if(get_post_args comment_text) { - d=`{date -n} # FIXME Obvious race - d=$local_path^'_werc/comments/'^$d/ - u=$logged_user - if(~ $#logged_user 0) { - get_post_args comment_user_name comment_user_password - # XXX Should do this too if user not in required group - if(! login_user $comment_user_name $comment_user_password) { - u=$comment_user_name':'$comment_user_password - d=$d^'_pending' - } - if not - u = $logged_user - } - - umask 002 - if(mkdir -m 775 -p $d) { # Rudimentary perm checking - echo $u > $d/user - echo $comment_text > $d/body - } - } +fn conf_enable_comments { + if(~ $1 -n) { + allow_new_user_comments=yes + shift } - + enable_comments=yes + groups_allowed_comments=$* } +fn bridge_init { + if(~ $#enable_comments 1) { + + cdir=$sitedir$req_path'_werc/comments' + if(test -d $cdir) + ll_add handlers_body_foot display_comments $cdir + + if({ check_user $groups_allowed_comments || {~ $#logged_user 0 && ! ~ $#allow_new_user_comments 0} } && ! ~ `{ls $local_path.$comment_file_types >[2]/dev/null|wc -l} 0) { + ll_add handlers_body_foot template apps/bridge/foot.tpl + + if(~ $REQUEST_METHOD POST && mk_new_comment $cdir) + post_redirect $base_url^$post_arg_document_uri + if not + saved_comment_text=$post_arg_comment_text + } + } +} + +fn validate_new_user { + usr=$1; pass=$2; pass2=$3 + _status=() + + if(~ $"usr '' || ! echo $usr |sed 1q|grep -s '^'$allowed_user_chars'+$') + _status='Requested user name is invalid, must match: '^$allowed_user_chars^'+' + if not if(test -d etc/users/$usr) + _status='Sorry, user name '''^$usr^''' already taken, please pick a different one.' + + if(~ $"pass '' || ! ~ $"pass $"pass2) + _status=($_status 'Provided passwords don''t match.') + + status=$_status +} + + +fn mk_new_comment { + _status=() + dir=$1 + if(~ $"post_arg_comment_text '') + _status='Provide a comment!' + if not if(~ $#logged_user 0) { + if(! ~ $#allow_new_user_comments 0) { + if(validate_new_user $"post_arg_comment_user $post_arg_comment_passwd $post_arg_comment_passwd2) { + u=$post_arg_comment_user':'$post_arg_comment_passwd + dir=$cdir^'_pending' + notify_notes='Saved comment and registration info, they will be enabled when approved by an admin.' + ll_add handlers_body_foot notices_handler + } + if not + _status=$status + } + if not + _status='You need to log in to comment.' + } + if not if(check_user $groups_allowed_comments) + u=$logged_user + if not + _status='You are not a memeber of a group allowed to comment.' + + if(~ $#_status 0) { + umask 002 + + dir=$dir'/'`{date -n} # FIXME Obvious race + mkdir -m 775 -p $dir && + echo $u > $dir/user && + echo $post_arg_comment_text > $dir/body + _s=$status + if(! ~ $"_s '') { + dprint 'ERROR XXX: Could not create comment: ' $_s + _status='Could not post comment due internal error, sorry.' + } + } + notify_errors=$_status + status=$_status +} + +fn display_comments { + echo '

Comments

' + + for(c in `{ls $*/}) { + if(test -s $c/body) { + echo '
' + sed 's!.+!By: &
!' < $c/user + cat $c/body | escape_html | sed 's,$,
,' + echo '
' + } + } +} diff --git a/apps/bridge/foot.tpl b/apps/bridge/foot.tpl index 8058c2d..7e54226 100644 --- a/apps/bridge/foot.tpl +++ b/apps/bridge/foot.tpl @@ -1,26 +1,27 @@ -% cdir = $local_path^'_werc/comments' -% if(test -d $cdir) { -

Comments

-% for(c in `{ls $cdir/}) { -
By: -% cat $c/user - -
-% cat $c/body | escape_html | sed 's,$,
,' -
-% } -% } -
+% notices_handler +% # XXX should post to bridge_post or similar
- +
+ -% if(! check_user) { - - -
If you are not registered enter your desired user/password and your account will be created when your comment is approved.
+ +% if(~ $#logged_user 0 && ! ~ $#allow_new_user_comments 0) { + + + + + +
+ Enter your desired user name/password and after your comment has been reviewed by an addmin it will be posted and your account will be enabled. If you are already registered please login before posting. +
% }
- From 39b0cc06f6f28eccfec3ad774f974f2cd3ab2340 Mon Sep 17 00:00:00 2001 From: Uriel Date: Fri, 30 Jan 2009 16:13:06 +0100 Subject: [PATCH 03/12] Make http_redirect resolve non-absolute uris. Only allow safe chars for user names. Reliability fixes when checking $status (don't check $#status!). Check user suceeds always if user in admin group. --- bin/cgilib.rc | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/bin/cgilib.rc b/bin/cgilib.rc index f5c182e..e36d9a1 100644 --- a/bin/cgilib.rc +++ b/bin/cgilib.rc @@ -10,8 +10,14 @@ fn dprintvars { { for(v in $*) { echo -n $v^'#'^$#$v^'=' $$v '; ' }; echo } >[1 fn escape_html { sed 's/&/\&/g; s//\>/g' $* } fn http_redirect { + if(~ $1 http:* https:*) + t=$1 + if not if(~ $1 /*) + t=$"base_url^$1 + if not + t=$"base_url^$"req_path^$1 echo 'Status: '^$2^' -Location: '^$1^' +Location: '^$t^' ' exit @@ -189,7 +195,7 @@ fn get_lib_file { fn template { awk -f bin/template.awk $* | rc $rcargs } # Auth code - +allowed_user_chars='[a-zA-Z0-9_]' # Cookie format: WERC_USER: name:timestamp:hash(name.timestamp.password) # login_user can't be used from a template because it sets a cookie fn login_user { @@ -198,13 +204,13 @@ fn login_user { set_cookie werc_user $"logged_user^':0:'^$"logged_password } -# Check loggin status, if called with group arg we check membership too +# Check login status, if called with group arg we check membership too fn check_user { get_user _status=$status - if(! ~ $#_status 0 ) + if(! ~ $"_status '') _status=(Not logged in: $"_status) - if not if(! ~ $#* 0 && ! grep -s '^'^$logged_user^'$' etc/groups/$*) { + if not if(! ~ $#* 0 && ! grep -s '^'^$logged_user^'$' etc/groups/$* etc/groups/admin) { dprint NOT IN GROUP _status=(User $logged_user not in groups $*) } From 2ee4609eb14e318262715987da423752568d136b Mon Sep 17 00:00:00 2001 From: Uriel Date: Fri, 30 Jan 2009 16:13:39 +0100 Subject: [PATCH 04/12] Styles for new notification boxes. --- pub/style/style.css | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pub/style/style.css b/pub/style/style.css index b7129d5..4155cba 100644 --- a/pub/style/style.css +++ b/pub/style/style.css @@ -334,6 +334,18 @@ blockquote { .doNotDisplay { display: none; } +.notify_errors, +.notify_notes, +.notify_success { padding: .8em; margin-bottom: 1em; border: 2px solid #ddd; } + +.notify_errors { background: #FBE3E4; color: #8a1f11; border-color: #FBC2C4; } +.notify_notes { background: #FFF6BF; color: #514721; border-color: #FFD324; } +.notify_success { background: #E6EFC2; color: #264409; border-color: #C6D880; } +.notify_errors a { color: #8a1f11; } +.notify_notes a { color: #514721; } +.notify_success a { color: #264409; } + + /* # Page/Handler specific # */ h1.dir-list-head, ul.dir-list { text-transform: capitalize; From 590816c6d4df9f3ff79d02d9b7e3568120ee1a8a Mon Sep 17 00:00:00 2001 From: Uriel Date: Fri, 30 Jan 2009 16:18:07 +0100 Subject: [PATCH 05/12] Replace $redirectPermanent with new conf_perm_redirect function. New notices_handler for error/note/success notification boxes. Rename hide_paths to conf_hide_paths. Reorder variable setting, all req-specifc vars are set after initrc and app sourcing, moved request handling code to its own function, this makes us ready to do scgi and other optimizations. --- bin/werc.rc | 165 +++++++++++++++++++++++++++++----------------------- 1 file changed, 91 insertions(+), 74 deletions(-) diff --git a/bin/werc.rc b/bin/werc.rc index 44fb523..c74f24d 100755 --- a/bin/werc.rc +++ b/bin/werc.rc @@ -6,23 +6,30 @@ forbidden_uri_chars='[^a-zA-Z0-9_+\-\/\.]' # Expected input: ls -F style, $sitedir/path/to/files/ # -dirfilter='s/\*$//; s,/+\./+,/,g; s,^\./,,; /\/[._][^\/]/d; /'^$forbidden_uri_chars^'/d; /^\/(robots|sitemap)\.txt$|\/index\.(md|html|txt|tpl)$/d; /_werc\/?$/d; ' +dirfilter='s/\*$//; s,/+\./+,/,g; s,^\./,,; /\/[._][^\/]/d; /'$forbidden_uri_chars'/d; /^\/(robots|sitemap)\.txt$|\/index\.(md|html|txt|tpl)$/d; /_werc\/?$/d; ' dirclean=' s/\.(md|html|txt)$//; ' # To be used from config files -fn hide_paths { +fn conf_perm_redirect { + if(~ $#* 1) + perm_redirect $1 + if not + perm_redir_patterns=($perm_redir_patterns $1 $2) +} + +fn conf_hide_paths { for(i in $*) dirfilter=$dirfilter^'/^'$i'$/d; ' } -# Sidebar +# Standard handlers fn nav_tree { if(! ~ $#sideBarNavTitle 0) echo '

'$"sideBarNavTitle':

' # Ignore stderr, last path element might be a file that doesn't exist (eg., foo for foo.md) # /./ to deal with p9p's ls failure to follow dir symlinks otherwise ls -F $sitedir/./$req_paths_list >[2]/dev/null \ - | sed 's!^'$sitedir'!!; '^$dirfilter^'/\/[^_.\/][^\/]*(\.(md|txt|html)|\/)$/!d; '^$dirclean \ + | sed 's!^'$sitedir'!!; '$dirfilter'/\/[^_.\/][^\/]*(\.(md|txt|html)|\/)$/!d; '$dirclean \ | sort -u | awk -F/ ' function p(x, y, s) { for(i=0; i < x-y; i+=1) print s } { @@ -48,7 +55,6 @@ fn nav_tree { } -# Handlers fn md_handler { $formatter < $1 } fn tpl_handler { template $* } @@ -77,6 +83,12 @@ fn dir_listing_handler { echo '' } +fn notices_handler { + for(type in notify_errors notify_notes notify_success) + for(n in $$type) + echo '
'$"n'
' +} + fn setup_handlers { if(test -f $local_path.md) @@ -133,13 +145,6 @@ sitesdir=sites for(i in siteTitle siteSubTitle pageTitle extraHeaders) $i = '' -# TODO: Per-req variables should move after initrc loading. -site=$SERVER_NAME -base_url=http://$site/ -sitedir=$sitesdir/$site -master_template=`{get_lib_file default_master.tpl} -current_date_time=`{date} - . ./etc/initrc if(test -f etc/initrc.local) @@ -148,71 +153,83 @@ if(test -f etc/initrc.local) for(a in $werc_apps) . ./$a/app.rc -# Parse request URL -# NOTE: $REQUEST_URI is not officially in CGI 1.1, but seems to be de-facto -req_path=`{echo -n $REQUEST_URI | sed 's/\?.*//; s/'^$forbidden_uri_chars^'//g; s/\.\.*/./g; 1q'} -local_path=$sitedir$req_path -ifs='/' { args=`{echo -n $req_path} } +fn werc_exec_request { + site=$SERVER_NAME + base_url=http://$site + sitedir=$sitesdir/$site + master_template=`{get_lib_file default_master.tpl} + current_date_time=`{date} -# Hack: preload post data so we can access it from templates where cgi's stdin is not accesible -if(~ $REQUEST_METHOD POST) { - load_post_args - login_user -} + # Note: $REQUEST_URI is not officially in CGI 1.1, but seems to be de-facto + req_path=`{echo -n $REQUEST_URI | sed 's/\?.*//; s!//+!/!g; s/'^$forbidden_uri_chars^'//g; s/\.\.*/./g; 1q'} + local_path=$sitedir$req_path + ifs='/' { args=`{echo -n $req_path} } -if(! ~ $#args 0) - pageTitle=`{ echo $args|sed -e 's/ / - /g' -e 's/_/ /g' } - -if(~ $req_path */index) - perm_redirect `{echo $req_path | sed 's,/index$,/,'} - -if(~ $local_path */) { - if(test -d $local_path) - local_path=$local_path^'index' - if not # XXX: This redir might step on apps with synthetic dirs. - perm_redirect `{echo $req_path|sed 's,/+$,,'} -} -if not if(test -d $local_path) - perm_redirect $req_path^'/' - -cd $sitedir -req_paths_list='/' # Note: req_paths_list doesn't include 'stnythetic' dirs. -conf_wd='/' # Used in config files to know where we are in the document tree. -if(test -f _werc/config) - . _werc/config -for(i in $args) { - conf_wd=$conf_wd^$i - req_paths_list=($req_paths_list $conf_wd) - if(test -d $i) { - conf_wd=$conf_wd^'/' - cd $i - if(test -f _werc/config) - . _werc/config + # Preload post args for templates where cgi's stdin is not accessible + if(~ $REQUEST_METHOD POST) { + load_post_args + login_user } -} -cd $werc_root -# Redirections and other preprocessing -if(~ $#redirectPermanent 1) { - perm_redirect $"redirectPermanent -} -if not if(~ $#redirectPermanent 2) { - from='http://'^$SERVER_NAME^$req_path - to=`{echo $from|sed 's@'^$redirectPermanent(1)^'@'^$redirectPermanent(2)^'@'} - if(! ~ $to $from) - perm_redirect $to + if(~ $req_path */index) + perm_redirect `{echo $req_path | sed 's,/index$,/,'} + + if(~ $local_path */) { + if(test -d $local_path) + local_path=$local_path^'index' + if not # XXX: This redir might step on apps with synthetic dirs. + perm_redirect `{echo $req_path|sed 's,/+$,,'} + } + if not if(test -d $local_path) + perm_redirect $base_url^$req_path^'/' + + if(! ~ $#args 0) + pageTitle=`{ echo $args|sed -e 's/ / - /g' -e 's/_/ /g' } + + cd $sitedir + req_paths_list='/' # Note: req_paths_list doesn't include 'stnythetic' dirs. + conf_wd='/' # Used in config files to know where we are in the document tree. + if(test -f _werc/config) + . _werc/config + for(i in $args) { + conf_wd=$conf_wd^$i + req_paths_list=($req_paths_list $conf_wd) + if(test -d $i) { + conf_wd=$conf_wd'/' + cd $i + if(test -f _werc/config) + . _werc/config + } + } + cd $werc_root + + f=();t=() + for(i in $perm_redir_patterns) { + if(~ $#f 0) + f=$i + if not { + t=$i + from=$base_url^$req_path + to=`{ echo $from | sed 's!'$f'!'$t'!' } + if(! ~ $to $from) + perm_redirect $to + f=() + } + } + + # Set Page title + if(~ $pageTitle '') + pageTitle=$siteTitle' '$siteSubTitle + if not + pageTitle=$"pageTitle' | '$"siteTitle' '$"siteSubTitle + + setup_handlers + + if(! ~ $#debug 0) + dprint $"SERVER_NAME^$"REQUEST_URI - $"HTTP_USER_AGENT - $"REQUEST_METHOD - $"handler_body_main - $"master_template + + template $headers $master_template | awk_buffer + echo $res_tail } -# Set Page title -if(~ $pageTitle '') - pageTitle=$siteTitle^' '^$siteSubTitle -if not - pageTitle=$"pageTitle^' | '^$"siteTitle^' '^$"siteSubTitle - -setup_handlers - -if(! ~ $#debug 0) - dprint ' '$"SERVER_NAME^$"REQUEST_URI' - '$"HTTP_USER_AGENT' - '$"REQUEST_METHOD' - '$"handler_body_main - $"master_template - -template $headers $master_template | awk_buffer -echo $res_tail +werc_exec_request From 7702c8ed9b9b62f5f0ba6c44c9732937501d332e Mon Sep 17 00:00:00 2001 From: Uriel Date: Fri, 30 Jan 2009 16:19:48 +0100 Subject: [PATCH 06/12] Fix txt_handler that was broken due to idiotic overzealous 'optimization', thanks mycroftiv for finding and fixing this! --- bin/werc.rc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bin/werc.rc b/bin/werc.rc index c74f24d..022b68e 100755 --- a/bin/werc.rc +++ b/bin/werc.rc @@ -70,7 +70,9 @@ fn html_handler { fn txt_handler { # Note: Words are not broken, even if they are way beyond 82 chars long - echo '
' `{ sed 's//\>/g' < $1 | fmt -l 82 -j } '
' + echo '
'
+    sed 's//\>/g' < $1 | fmt -l 82 -j
+    echo '
' } fn dir_listing_handler { From 7069e2af932932feaad8d630d8f1e3690b44fc32 Mon Sep 17 00:00:00 2001 From: Uriel Date: Fri, 30 Jan 2009 16:48:49 +0100 Subject: [PATCH 07/12] add comment about how note is never displayed due to subsequent redirect. --- apps/bridge/app.rc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/bridge/app.rc b/apps/bridge/app.rc index c46996d..00ce96b 100644 --- a/apps/bridge/app.rc +++ b/apps/bridge/app.rc @@ -53,8 +53,8 @@ fn mk_new_comment { if(validate_new_user $"post_arg_comment_user $post_arg_comment_passwd $post_arg_comment_passwd2) { u=$post_arg_comment_user':'$post_arg_comment_passwd dir=$cdir^'_pending' + # XXX: This doesn't work because we then do a redirect. notify_notes='Saved comment and registration info, they will be enabled when approved by an admin.' - ll_add handlers_body_foot notices_handler } if not _status=$status From 64311bb30be9abe9fee6412533c3fde181198cd9 Mon Sep 17 00:00:00 2001 From: Uriel Date: Sun, 1 Feb 2009 20:02:22 +0100 Subject: [PATCH 08/12] Store and display comment posted date/time. --- apps/bridge/app.rc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/bridge/app.rc b/apps/bridge/app.rc index 00ce96b..b196d99 100644 --- a/apps/bridge/app.rc +++ b/apps/bridge/app.rc @@ -73,6 +73,7 @@ fn mk_new_comment { dir=$dir'/'`{date -n} # FIXME Obvious race mkdir -m 775 -p $dir && echo $u > $dir/user && + echo $current_date_time > $dir/posted && echo $post_arg_comment_text > $dir/body _s=$status if(! ~ $"_s '') { @@ -89,8 +90,7 @@ fn display_comments { for(c in `{ls $*/}) { if(test -s $c/body) { - echo '
' - sed 's!.+!By: &
!' < $c/user + ifs=() { echo '
By: '`{cat $c/user}' ('`{cat $c/posted}')
'} cat $c/body | escape_html | sed 's,$,
,' echo '
' } From 64e3d86654dc163055d7e1700476f3dd95615a0a Mon Sep 17 00:00:00 2001 From: Uriel Date: Sun, 1 Feb 2009 20:03:27 +0100 Subject: [PATCH 09/12] Display date in post listing. --- apps/blagh/app.rc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/blagh/app.rc b/apps/blagh/app.rc index 8e3bbd6..035a7db 100644 --- a/apps/blagh/app.rc +++ b/apps/blagh/app.rc @@ -54,9 +54,9 @@ fn blagh_setup_feed_handlers { fn blagh_body { for(p in `{get_post_list $blagh_root^$blagh_dirs}) { - l=`{echo -n $p|sed 's!'$sitedir^$req_path'!!'} - sed '1s!.*![&]('$l')!' < $p/index.md | $formatter - } + l=`{echo -n $p|sed 's!'$sitedir^$req_path'./([0-9]+/[0-9][0-9]/[0-9][0-9])(.*)!\1 ./\1\2!'} + sed '1s!.*![&]('^$l(2)^') ('^$l(1)^')!' < $p/index.md + } | $formatter } fn get_post_list { From 1020b8b669659d73339d1e295758b79f1b0f4cc2 Mon Sep 17 00:00:00 2001 From: Uriel Date: Sun, 1 Feb 2009 20:04:28 +0100 Subject: [PATCH 10/12] Handle extraHeaders being nil. --- lib/headers.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/headers.tpl b/lib/headers.tpl index 5f59682..6cebf15 100644 --- a/lib/headers.tpl +++ b/lib/headers.tpl @@ -27,7 +27,7 @@ Content-Type: text/html % if(! ~ $#h 0) % cat $h - %($extraHeaders%) + %($"extraHeaders%) From eb190a1a0b1c56146a45f6ced65f5053dc251193 Mon Sep 17 00:00:00 2001 From: Uriel Date: Sun, 1 Feb 2009 20:05:00 +0100 Subject: [PATCH 11/12] Use new conf_enable_comments directive in werc website. --- sites/werc.cat-v.org/_werc/config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sites/werc.cat-v.org/_werc/config b/sites/werc.cat-v.org/_werc/config index 9c577b0..7746e1a 100644 --- a/sites/werc.cat-v.org/_werc/config +++ b/sites/werc.cat-v.org/_werc/config @@ -1,5 +1,5 @@ siteTitle='werc' siteSubTitle=' Bringing minimalism and sanity to the web' -enable_comments=yes +conf_enable_comments enabled_apps=($enabled_apps hello dirdir bridge) From e084b89cae1e1422c2c2cc89c9ba33a42ab21e68 Mon Sep 17 00:00:00 2001 From: Uriel Date: Sun, 1 Feb 2009 20:06:51 +0100 Subject: [PATCH 12/12] Reorg code in five source files: werc.rc, cgilib.rc, corehandlers.rc, wercconf.rc and werclib.rc. --- bin/cgilib.rc | 167 +++++++------------------------------------- bin/corehandlers.rc | 115 ++++++++++++++++++++++++++++++ bin/werc.rc | 139 ++---------------------------------- bin/wercconf.rc | 12 ++++ bin/werclib.rc | 117 +++++++++++++++++++++++++++++++ 5 files changed, 276 insertions(+), 274 deletions(-) create mode 100644 bin/corehandlers.rc create mode 100644 bin/wercconf.rc create mode 100644 bin/werclib.rc diff --git a/bin/cgilib.rc b/bin/cgilib.rc index e36d9a1..0ae1230 100644 --- a/bin/cgilib.rc +++ b/bin/cgilib.rc @@ -1,11 +1,7 @@ -############################################## -# Useful CGI functions - -NEW_LINE=' -' +# Useful CGI stuff fn dprint { echo $* >[1=2] } -fn dprintvars { { for(v in $*) { echo -n $v^'#'^$#$v^'=' $$v '; ' }; echo } >[1=2] } +fn dprintv { { for(v in $*) { echo -n $v^'#'^$#$v^'=' $$v '; ' }; echo } >[1=2] } fn escape_html { sed 's/&/\&/g; s//\>/g' $* } @@ -25,13 +21,6 @@ Location: '^$t^' fn perm_redirect { http_redirect $1 '301 Moved Permanantly' } fn post_redirect { http_redirect $1 '303 See Other' } -fn static_file { - echo 'Content-Type: '`{select_mime $1} - echo - cat $1 - exit -} - # Note: should check if content type is application/x-www-form-urlencoded? fn load_post_args { @@ -117,22 +106,6 @@ BEGIN { ' } -fn crop_text { - ellipsis='...' - if(~ $#* 2) - ellipsis=$2 - - awk -v max'='^$"1^' ' -v 'ellipsis='$ellipsis ' - { - nc += 1 + length; - if(nc > max) { - print substr($0, 1, nc - max) ellipsis - exit - } - print - }' -} - # Cookies fn set_cookie { @@ -149,6 +122,14 @@ fn get_cookie { { for(c in $co) echo $c } | sed -n 's/^ ?'$1'=//p' } + +fn static_file { + echo 'Content-Type: '`{select_mime $1} + echo + cat $1 + exit +} + fn select_mime { m='text/plain' if(~ $1 *.css) @@ -169,128 +150,30 @@ fn select_mime { ############################################## # Generic rc programming helpers +# Manage nested lists fn ll_add { _l=$1^_^$#$1 $_l=$*(2-) $1=( $$1 $_l ) } +NEW_LINE=' +' -############################################## -# Werc-specific functions +fn crop_text { + ellipsis='...' + if(~ $#* 2) + ellipsis=$2 -fn get_lib_file { - if(! ~ $#sitedir 0 && test -f $sitedir/_werc/lib/$1) - echo -n $sitedir/_werc/lib/$1 - if not if(! ~ $#masterSite 0 && test -f $sitesdir/$masterSite/_werc/lib/$1) - echo -n $sitesdir/$masterSite/_werc/lib/$1 - if not if(test -f lib/$1) - echo -n lib/$1 - if not if(~ $#* 2) - echo -n $2 - if not - status='Can''t find lib file: '$1 -} - -fn template { awk -f bin/template.awk $* | rc $rcargs } - -# Auth code -allowed_user_chars='[a-zA-Z0-9_]' -# Cookie format: WERC_USER: name:timestamp:hash(name.timestamp.password) -# login_user can't be used from a template because it sets a cookie -fn login_user { - # Note: we set the cookie even if it is already there. - if(get_user $*) - set_cookie werc_user $"logged_user^':0:'^$"logged_password -} - -# Check login status, if called with group arg we check membership too -fn check_user { - get_user - _status=$status - if(! ~ $"_status '') - _status=(Not logged in: $"_status) - if not if(! ~ $#* 0 && ! grep -s '^'^$logged_user^'$' etc/groups/$* etc/groups/admin) { - dprint NOT IN GROUP - _status=(User $logged_user not in groups $*) - } - status=$_status -} - -# If not logged in, try to get user login info from POST or from cookie -fn get_user { - if(~ $#logged_user 0) { - if(~ $#* 2) { - user_name=$1 - user_password=$2 + awk -v max'='^$"1^' ' -v 'ellipsis='$ellipsis ' + { + nc += 1 + length; + if(nc > max) { + print substr($0, 1, nc - max) ellipsis + exit } - if not if(~ $REQUEST_METHOD POST) - get_post_args user_name user_password - - if(~ $#user_name 0) { - ifs=':' { cu=`{get_cookie werc_user|tr -d $NEW_LINE} } - if(! ~ $#cu 0) { - user_name=$cu(1) - user_password=$cu(3) - } - } - auth_user $user_name $user_password - } - if not - status=() -} - -# Check if user_name and user_password represent a valid user account -# If valid, 'log in' by setting logged_user -fn auth_user { - user_name=$1 - user_password=$2 - - pfile='etc/users/'^$"user_name^'/password' - if(~ $#user_name 0 || ~ $#user_password 0) - status=('Auth: missing user name or pass: '^$"user_name^' / '^$"user_password) - if not if(! test -f $pfile) - status=('Auth: cant find '^$pfile) - if not if(! ~ $user_password `{cat $pfile}) - status=('Auth: Pass '$user_password' doesnt match '^`{cat $pfile}) - if not { - logged_user=$user_name - logged_password=$user_password - dprint Auth: success - status=() - } -} - -fn user_controls { - echo User: $"logged_user + print + }' } -# .md '(meta-)data' extract -fn get_md_file_attr { - sed -n '/^\* '$2': /p; /^\* '$2': /q; /^$/q' < $1 -} - -#app_blog_methods = ( _post index.rss ) -#fn app_blog__post { -# echo -#} -# -#app_blog___default { -# if (~ $blog) -# call_app blogpost -#} -# -## -- -#app_blogpost_methods = ( comment _edit ) -# -#fn app_blogpost_comment { -# call_app comments -#} -# -## -- -#app_comments_methods = ( _post _edit ) -# -#fn app_comments___default { -# -#} diff --git a/bin/corehandlers.rc b/bin/corehandlers.rc new file mode 100644 index 0000000..73281a7 --- /dev/null +++ b/bin/corehandlers.rc @@ -0,0 +1,115 @@ +# Werc builtin handlers + +fn nav_tree { + if(! ~ $#sideBarNavTitle 0) + echo '

'$"sideBarNavTitle':

' + # Ignore stderr, last path element might be a file that doesn't exist (eg., foo for foo.md) + # /./ to deal with p9p's ls failure to follow dir symlinks otherwise + ls -F $sitedir/./$req_paths_list >[2]/dev/null \ + | sed 's!^'$sitedir'!!; '$dirfilter'/\/[^_.\/][^\/]*(\.(md|txt|html)|\/)$/!d; '$dirclean \ + | sort -u | awk -F/ ' + function p(x, y, s) { for(i=0; i < x-y; i+=1) print s } + { + d = "" + if(match($0, "/$")) + d = "/" + sub("/$", "") # Strip trailing / for dirs so NF is consistent + + p(NF, lNF, "
    ") + p(lNF, NF, "
") + lNF = NF + + bname = $NF d + path = $0 d + gsub("_", " ", bname) + + if(index(ENVIRON["req_path"] "/", path) == 1) + print "
  • » " bname "" + else + print "
  • › " bname "
  • " + } + END { p(lNF, 0, "") }' +} + + +fn md_handler { $formatter < $1 } + +fn tpl_handler { template $* } + +fn html_handler { + # body states: 0 = no found, 2 = after , 1 = after , -1 = after + awk 'gsub(".*<[Bb][Oo][Dd][Yy][^>]*>", "") > 0 {body=2} + gsub("]*>.*", "") > 0 {print; body=body-1} + body==2 {print} + body==0 {buf=buf "\n" $0} + END {if(body<=0) {print buf}}' < $1 +} + +fn txt_handler { + # Note: Words are not broken, even if they are way beyond 82 chars long + echo '
    '
    +    sed 's//\>/g' < $1 | fmt -l 82 -j
    +    echo '
    ' +} + +fn dir_listing_handler { + d=`{basename -d $1} + if(~ $#d 0) + d='/' + echo $d|sed 's,.*//,,g; s,/$,,; s,/, / ,g; s,.*,

    &

      ,' + # Symlinks suck: '/.' forces ls to list the linked dir if $d is a symlink. + ls -F $dir_listing_ls_opts $sitedir$d/. | sed $dirfilter$dirclean' s,.*/([^/]+/?)$,
    • \1
    • ,' + echo '
    ' +} + +fn notices_handler { + for(type in notify_errors notify_notes notify_success) + for(n in $$type) + echo '
    '$"n'
    ' +} + +fn setup_handlers { + + if(test -f $local_path.md) + handler_body_main=(md_handler $local_path.md) + if not if(test -f $local_path.tpl) + handler_body_main=(tpl_handler $local_path.tpl) + if not if(test -f $local_path.html) + handler_body_main=(html_handler $local_path.html) + # Global tpl (eg sitemap.tpl), should take precedence over txt handler! + if not if(test -f lib^$req_path^.tpl) + handler_body_main=(tpl_handler lib^$req_path^.tpl) + if not if(test -f $local_path.txt) + handler_body_main=(txt_handler $local_path.txt) + + # XXX Should check that $enabled_apps exist in $werc_apps? + # XXX Should split init of apps that provide main handler (eg., blog) and apps that don't (eg., comments)? + if(! ~ $#enabled_apps 0) + for(a in $enabled_apps) + $a^'_init' + + if(! ~ $#handler_body_main 0) + { } # We are done + # Dir listing + if not if(~ $local_path */index) + handler_body_main=(dir_listing_handler $req_path) + # Canonize explicit .html urls, the web server might handle this first! + if not if(~ $local_path *.html && test -f $local_path) + perm_redirect `{ echo $req_path|sed 's/.html$//' } + # Fallback static file handler + if not if(test -f $local_path) + static_file $local_path + if not if(~ $req_path /pub/* && test -f .$req_path) + static_file .$req_path + # File not found + if not { + handler_body_main=(tpl_handler `{get_lib_file 404.tpl}) + echo 'Status: 404 Not Found' + dprint 'NOT FOUND: '$SERVER_NAME^$"REQUEST_URI^' - '^$"HTTP_REFERER^' - '^$"HTTP_USER_AGENT + } +} + +fn run_handlers { for(h in $*) run_handler $$h } +fn run_handler { $*(1) $*(2-) } + + diff --git a/bin/werc.rc b/bin/werc.rc index 022b68e..a274c7c 100755 --- a/bin/werc.rc +++ b/bin/werc.rc @@ -1,5 +1,8 @@ #!/usr/local/plan9/bin/rc . ./cgilib.rc +. ./werclib.rc +. ./wercconf.rc +. ./corehandlers.rc cd .. forbidden_uri_chars='[^a-zA-Z0-9_+\-\/\.]' @@ -9,132 +12,6 @@ forbidden_uri_chars='[^a-zA-Z0-9_+\-\/\.]' dirfilter='s/\*$//; s,/+\./+,/,g; s,^\./,,; /\/[._][^\/]/d; /'$forbidden_uri_chars'/d; /^\/(robots|sitemap)\.txt$|\/index\.(md|html|txt|tpl)$/d; /_werc\/?$/d; ' dirclean=' s/\.(md|html|txt)$//; ' -# To be used from config files -fn conf_perm_redirect { - if(~ $#* 1) - perm_redirect $1 - if not - perm_redir_patterns=($perm_redir_patterns $1 $2) -} - -fn conf_hide_paths { - for(i in $*) - dirfilter=$dirfilter^'/^'$i'$/d; ' -} - -# Standard handlers -fn nav_tree { - if(! ~ $#sideBarNavTitle 0) - echo '

    '$"sideBarNavTitle':

    ' - # Ignore stderr, last path element might be a file that doesn't exist (eg., foo for foo.md) - # /./ to deal with p9p's ls failure to follow dir symlinks otherwise - ls -F $sitedir/./$req_paths_list >[2]/dev/null \ - | sed 's!^'$sitedir'!!; '$dirfilter'/\/[^_.\/][^\/]*(\.(md|txt|html)|\/)$/!d; '$dirclean \ - | sort -u | awk -F/ ' - function p(x, y, s) { for(i=0; i < x-y; i+=1) print s } - { - d = "" - if(match($0, "/$")) - d = "/" - sub("/$", "") # Strip trailing / for dirs so NF is consistent - - p(NF, lNF, "
      ") - p(lNF, NF, "
    ") - lNF = NF - - bname = $NF d - path = $0 d - gsub("_", " ", bname) - - if(index(ENVIRON["req_path"] "/", path) == 1) - print "
  • » " bname "" - else - print "
  • › " bname "
  • " - } - END { p(lNF, 0, "") }' -} - - -fn md_handler { $formatter < $1 } - -fn tpl_handler { template $* } - -fn html_handler { - # body states: 0 = no found, 2 = after , 1 = after , -1 = after - awk 'gsub(".*<[Bb][Oo][Dd][Yy][^>]*>", "") > 0 {body=2} - gsub("]*>.*", "") > 0 {print; body=body-1} - body==2 {print} - body==0 {buf=buf "\n" $0} - END {if(body<=0) {print buf}}' < $1 -} - -fn txt_handler { - # Note: Words are not broken, even if they are way beyond 82 chars long - echo '
    '
    -    sed 's//\>/g' < $1 | fmt -l 82 -j
    -    echo '
    ' -} - -fn dir_listing_handler { - d=`{basename -d $1} - if(~ $#d 0) - d='/' - echo $d|sed 's,.*//,,g; s,/$,,; s,/, / ,g; s,.*,

    &

      ,' - # Symlinks suck: '/.' forces ls to list the linked dir if $d is a symlink. - ls -F $dir_listing_ls_opts $sitedir$d/. | sed $dirfilter$dirclean' s,.*/([^/]+/?)$,
    • \1
    • ,' - echo '
    ' -} - -fn notices_handler { - for(type in notify_errors notify_notes notify_success) - for(n in $$type) - echo '
    '$"n'
    ' -} - -fn setup_handlers { - - if(test -f $local_path.md) - handler_body_main=(md_handler $local_path.md) - if not if(test -f $local_path.tpl) - handler_body_main=(tpl_handler $local_path.tpl) - if not if(test -f $local_path.html) - handler_body_main=(html_handler $local_path.html) - # Global tpl (eg sitemap.tpl), should take precedence over txt handler! - if not if(test -f lib^$req_path^.tpl) - handler_body_main=(tpl_handler lib^$req_path^.tpl) - if not if(test -f $local_path.txt) - handler_body_main=(txt_handler $local_path.txt) - - # XXX Should check that $enabled_apps exist in $werc_apps? - # XXX Should split init of apps that provide main handler (eg., blog) and apps that don't (eg., comments)? - if(! ~ $#enabled_apps 0) - for(a in $enabled_apps) - $a^'_init' - - if(! ~ $#handler_body_main 0) - { } # We are done - # Dir listing - if not if(~ $local_path */index) - handler_body_main=(dir_listing_handler $req_path) - # Canonize explicit .html urls, the web server might handle this first! - if not if(~ $local_path *.html && test -f $local_path) - perm_redirect `{ echo $req_path|sed 's/.html$//' } - # Fallback static file handler - if not if(test -f $local_path) - static_file $local_path - if not if(~ $req_path /pub/* && test -f .$req_path) - static_file .$req_path - # File not found - if not { - handler_body_main=(tpl_handler `{get_lib_file 404.tpl}) - echo 'Status: 404 Not Found' - dprint 'NOT FOUND: '$SERVER_NAME^$"REQUEST_URI^' - '^$"HTTP_REFERER^' - '^$"HTTP_USER_AGENT - } -} - -fn run_handlers { for(h in $*) run_handler $$h } -fn run_handler { $*(1) $*(2-) } - # Careful, the proper p9p path might not be set until initrc.local is sourced path=(. $PLAN9/bin ./bin/ /bin/ /usr/bin) @@ -144,10 +21,8 @@ ll_add handlers_bar_left nav_tree werc_apps=( apps/* ) werc_root=`{pwd} sitesdir=sites -for(i in siteTitle siteSubTitle pageTitle extraHeaders) - $i = '' -. ./etc/initrc + . ./etc/initrc if(test -f etc/initrc.local) . ./etc/initrc.local @@ -220,8 +95,8 @@ fn werc_exec_request { } # Set Page title - if(~ $pageTitle '') - pageTitle=$siteTitle' '$siteSubTitle + if(~ $"pageTitle '') + pageTitle=$"siteTitle' '$"siteSubTitle if not pageTitle=$"pageTitle' | '$"siteTitle' '$"siteSubTitle @@ -230,7 +105,7 @@ fn werc_exec_request { if(! ~ $#debug 0) dprint $"SERVER_NAME^$"REQUEST_URI - $"HTTP_USER_AGENT - $"REQUEST_METHOD - $"handler_body_main - $"master_template - template $headers $master_template | awk_buffer + template $headers $master_template #| awk_buffer echo $res_tail } diff --git a/bin/wercconf.rc b/bin/wercconf.rc new file mode 100644 index 0000000..e98cbff --- /dev/null +++ b/bin/wercconf.rc @@ -0,0 +1,12 @@ +# To be used from config files +fn conf_perm_redirect { + if(~ $#* 1) + perm_redirect $1 + if not + perm_redir_patterns=($perm_redir_patterns $1 $2) +} + +fn conf_hide_paths { + for(i in $*) + dirfilter=$dirfilter^'/^'$i'$/d; ' +} diff --git a/bin/werclib.rc b/bin/werclib.rc new file mode 100644 index 0000000..fdbc1f1 --- /dev/null +++ b/bin/werclib.rc @@ -0,0 +1,117 @@ +fn get_lib_file { + if(! ~ $#sitedir 0 && test -f $sitedir/_werc/lib/$1) + echo -n $sitedir/_werc/lib/$1 + if not if(! ~ $#masterSite 0 && test -f $sitesdir/$masterSite/_werc/lib/$1) + echo -n $sitesdir/$masterSite/_werc/lib/$1 + if not if(test -f lib/$1) + echo -n lib/$1 + if not if(~ $#* 2) + echo -n $2 + if not + status='Can''t find lib file: '$1 +} + +fn template { awk -f bin/template.awk $* | rc $rcargs } + +# Auth code +allowed_user_chars='[a-zA-Z0-9_]' +# Cookie format: WERC_USER: name:timestamp:hash(name.timestamp.password) +# login_user can't be used from a template because it sets a cookie +fn login_user { + # Note: we set the cookie even if it is already there. + if(get_user $*) + set_cookie werc_user $"logged_user^':0:'^$"logged_password +} + +# Check login status, if called with group arg we check membership too +fn check_user { + get_user + _status=$status + if(! ~ $"_status '') + _status=(Not logged in: $"_status) + if not if(! ~ $#* 0 && ! grep -s '^'^$logged_user^'$' etc/groups/$* etc/groups/admin) { + dprint NOT IN GROUP + _status=(User $logged_user not in groups $*) + } + status=$_status +} + +# If not logged in, try to get user login info from POST or from cookie +fn get_user { + if(~ $#logged_user 0) { + if(~ $#* 2) { + user_name=$1 + user_password=$2 + } + if not if(~ $REQUEST_METHOD POST) + get_post_args user_name user_password + + if(~ $#user_name 0) { + ifs=':' { cu=`{get_cookie werc_user|tr -d $NEW_LINE} } + if(! ~ $#cu 0) { + user_name=$cu(1) + user_password=$cu(3) + } + } + auth_user $user_name $user_password + } + if not + status=() +} + +# Check if user_name and user_password represent a valid user account +# If valid, 'log in' by setting logged_user +fn auth_user { + user_name=$1 + user_password=$2 + + pfile='etc/users/'^$"user_name^'/password' + if(~ $#user_name 0 || ~ $#user_password 0) + status=('Auth: missing user name or pass: '^$"user_name^' / '^$"user_password) + if not if(! test -f $pfile) + status=('Auth: cant find '^$pfile) + if not if(! ~ $user_password `{cat $pfile}) + status=('Auth: Pass '$user_password' doesnt match '^`{cat $pfile}) + if not { + logged_user=$user_name + logged_password=$user_password + dprint Auth: success + status=() + } +} + +fn user_controls { + echo User: $"logged_user +} + + +# .md '(meta-)data' extract +fn get_md_file_attr { + sed -n '/^\* '$2': /p; /^\* '$2': /q; /^$/q' < $1 +} + +########################################################################## +########################################################################## +#app_blog_methods = ( _post index.rss ) +#fn app_blog__post { +# echo +#} +# +#app_blog___default { +# if (~ $blog) +# call_app blogpost +#} +# +## -- +#app_blogpost_methods = ( comment _edit ) +# +#fn app_blogpost_comment { +# call_app comments +#} +# +## -- +#app_comments_methods = ( _post _edit ) +# +#fn app_comments___default { +# +#}