swerc

anselm's simpler werc fork
git clone git://git.suckless.org/swerc
Log | Files | Refs | README

commit 4d495116f0456fabe7aa7988bd53819ea93faa69
parent 4f25d2da49b44924271c90ceb622de93f8f42701
Author: uriel@soma <unknown>
Date:   Sun,  1 Feb 2009 19:23:55 +0000

Merge.
Diffstat:
Mapps/blagh/app.rc | 6+++---
Mapps/bridge/app.rc | 110++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Mapps/bridge/foot.tpl | 39++++++++++++++++++++-------------------
Mbin/cgilib.rc | 175+++++++++++++++----------------------------------------------------------------
Abin/corehandlers.rc | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mbin/werc.rc | 260++++++++++++++++++++++++-------------------------------------------------------
Abin/wercconf.rc | 12++++++++++++
Abin/werclib.rc | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mlib/_users/login.tpl | 6+++---
Mlib/headers.tpl | 2+-
Mpub/style/style.css | 12++++++++++++
Msites/werc.cat-v.org/_werc/config | 2+-
12 files changed, 482 insertions(+), 374 deletions(-)

diff --git 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 { diff --git a/apps/bridge/app.rc b/apps/bridge/app.rc @@ -1,30 +1,98 @@ comment_file_types=(md html) + +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 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 - } + 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=() - umask 002 - if(mkdir -m 775 -p $d) { # Rudimentary perm checking - echo $u > $d/user - echo $comment_text > $d/body + 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' + # 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.' } + 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 $current_date_time > $dir/posted && + 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 '<hr /><h2>Comments</h2>' + + for(c in `{ls $*/}) { + if(test -s $c/body) { + ifs=() { echo '<div class="comment"><h5>By: <i>'`{cat $c/user}'</i></b> ('`{cat $c/posted}')</h5>'} + cat $c/body | escape_html | sed 's,$,<br />,' + echo '<hr /></div>' + } + } +} diff --git a/apps/bridge/foot.tpl b/apps/bridge/foot.tpl @@ -1,26 +1,27 @@ -% cdir = $local_path^'_werc/comments' -% if(test -d $cdir) { - <hr /><h2>Comments</h2> -% for(c in `{ls $cdir/}) { - <div><b>By: <i> -% cat $c/user - </i></b> - <br /> -% cat $c/body | escape_html | sed 's,$,<br />,' - <hr /></div> -% } -% } - <hr /> +% notices_handler +% # XXX should post to bridge_post or similar <form action="" method="post"> - <textarea name="comment_text" id="comment_text" cols="80" rows="16"></textarea> + <textarea name="comment_text" id="comment_text" cols="80" rows="16">%($"saved_comment_text%)</textarea> <br /> + <input type="hidden" name="document_uri" value="%($req_path%)" /> <input type="submit" name="post_comment" value="Post a comment" /> -% if(! check_user) { - <label>User: <input type="text" name="comment_user_name" value="" /></label> - <label>Password: <input type="password" name="comment_user_password" value="" /></label> - <div style="font-size: 70%">If you are not registered enter your desired user/password and your account will be created when your comment is approved.</div> + +% if(~ $#logged_user 0 && ! ~ $#allow_new_user_comments 0) { + <label>New user name: + <input type="text" name="comment_user" value="%($"post_arg_comment_user%)" /> + </label> + + <label>Password: + <input type="password" name="comment_passwd" value="" /> + </label> + + <label>Repeat password: + <input type="password" name="comment_passwd2" value="" /> + </label> + <div style="font-size: 70%"> + 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 <a href="/_users/login">login</a> before posting. + </div> % } </form> - diff --git a/bin/cgilib.rc b/bin/cgilib.rc @@ -1,17 +1,19 @@ -############################################## -# 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/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/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 @@ -19,13 +21,6 @@ Location: '^$1^' 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 { @@ -111,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 { @@ -143,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) @@ -163,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 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 - -# 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 loggin status, if called with group arg we check membership too -fn check_user { - get_user - _status=$status - if(! ~ $#_status 0 ) - _status=(Not logged in: $"_status) - if not if(! ~ $#* 0 && ! grep -s '^'^$logged_user^'$' etc/groups/$*) { - 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 +fn crop_text { + ellipsis='...' + if(~ $#* 2) + ellipsis=$2 - 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) - } + awk -v max'='^$"1^' ' -v 'ellipsis='$ellipsis ' + { + nc += 1 + length; + if(nc > max) { + print substr($0, 1, nc - max) ellipsis + exit } - 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 @@ -0,0 +1,115 @@ +# Werc builtin handlers + +fn nav_tree { + if(! ~ $#sideBarNavTitle 0) + echo '<p class="sideBarTitle">'$"sideBarNavTitle':</p>' + # 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, "<ul class=\"side-bar\">") + p(lNF, NF, "</ul>") + lNF = NF + + bname = $NF d + path = $0 d + gsub("_", " ", bname) + + if(index(ENVIRON["req_path"] "/", path) == 1) + print "<li><a href=\"" path "\" class=\"thisPage\">&raquo;<i> " bname "</i></a>" + else + print "<li><a href=\"" path "\">&rsaquo; " bname "</a></li>" + } + END { p(lNF, 0, "</ul>") }' +} + + +fn md_handler { $formatter < $1 } + +fn tpl_handler { template $* } + +fn html_handler { + # body states: 0 = no <body> found, 2 = after <body>, 1 = after <body></body>, -1 = after </body> + awk 'gsub(".*<[Bb][Oo][Dd][Yy][^>]*>", "") > 0 {body=2} + gsub("</ *[Bb][Oo][Dd][Yy][^>]*>.*", "") > 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 '<pre>' + sed 's/</\&lt;/g; s/>/\&gt;/g' < $1 | fmt -l 82 -j + echo '</pre>' +} + +fn dir_listing_handler { + d=`{basename -d $1} + if(~ $#d 0) + d='/' + echo $d|sed 's,.*//,,g; s,/$,,; s,/, / ,g; s,.*,<h1 class="dir-list-head">&</h1> <ul class="dir-list">,' + # 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,.*/([^/]+/?)$,<li><a href="\1">\1</a></li>,' + echo '</ul>' +} + +fn notices_handler { + for(type in notify_errors notify_notes notify_success) + for(n in $$type) + echo '<div class="'$type'"><b>'$"n'</b></div>' +} + +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 @@ -1,126 +1,17 @@ #!/usr/local/plan9/bin/rc . ./cgilib.rc +. ./werclib.rc +. ./wercconf.rc +. ./corehandlers.rc cd .. forbidden_uri_chars='[^a-zA-Z0-9_+\-\/\.]' # Expected input: ls -F style, $sitedir/path/to/files/ # <ls -F+x><symlink hack><Useless?><hiden 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 { - for(i in $*) - dirfilter=$dirfilter^'/^'$i'$/d; ' -} - -# Sidebar -fn nav_tree { - if(! ~ $#sideBarNavTitle 0) - echo '<p class="sideBarTitle">'$"sideBarNavTitle':</p>' - # 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, "<ul class=\"side-bar\">") - p(lNF, NF, "</ul>") - lNF = NF - - bname = $NF d - path = $0 d - gsub("_", " ", bname) - - if(index(ENVIRON["req_path"] "/", path) == 1) - print "<li><a href=\"" path "\" class=\"thisPage\">&raquo;<i> " bname "</i></a>" - else - print "<li><a href=\"" path "\">&rsaquo; " bname "</a></li>" - } - END { p(lNF, 0, "</ul>") }' -} - - -# Handlers -fn md_handler { $formatter < $1 } - -fn tpl_handler { template $* } - -fn html_handler { - # body states: 0 = no <body> found, 2 = after <body>, 1 = after <body></body>, -1 = after </body> - awk 'gsub(".*<[Bb][Oo][Dd][Yy][^>]*>", "") > 0 {body=2} - gsub("</ *[Bb][Oo][Dd][Yy][^>]*>.*", "") > 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 '<pre>' `{ sed 's/</\&lt;/g; s/>/\&gt;/g' < $1 | fmt -l 82 -j } '</pre>' -} - -fn dir_listing_handler { - d=`{basename -d $1} - if(~ $#d 0) - d='/' - echo $d|sed 's,.*//,,g; s,/$,,; s,/, / ,g; s,.*,<h1 class="dir-list-head">&</h1> <ul class="dir-list">,' - # 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,.*/([^/]+/?)$,<li><a href="\1">\1</a></li>,' - echo '</ul>' -} - -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) @@ -130,17 +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 = '' - -# 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 + . ./etc/initrc if(test -f etc/initrc.local) . ./etc/initrc.local @@ -148,71 +30,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} } - -# 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 -} - -if(! ~ $#args 0) - pageTitle=`{ echo $args|sed -e 's/ / - /g' -e 's/_/ /g' } +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} + + # 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} } + + # Preload post args for templates where cgi's stdin is not accessible + if(~ $REQUEST_METHOD POST) { + load_post_args + login_user + } -if(~ $req_path */index) - perm_redirect `{echo $req_path | sed 's,/index$,/,'} + 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 + 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=() + } } -} -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 -} + # Set Page title + if(~ $"pageTitle '') + pageTitle=$"siteTitle' '$"siteSubTitle + if not + pageTitle=$"pageTitle' | '$"siteTitle' '$"siteSubTitle -# Set Page title -if(~ $pageTitle '') - pageTitle=$siteTitle^' '^$siteSubTitle -if not - pageTitle=$"pageTitle^' | '^$"siteTitle^' '^$"siteSubTitle + setup_handlers -setup_handlers + if(! ~ $#debug 0) + dprint $"SERVER_NAME^$"REQUEST_URI - $"HTTP_USER_AGENT - $"REQUEST_METHOD - $"handler_body_main - $"master_template -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 +} -template $headers $master_template | awk_buffer -echo $res_tail +werc_exec_request diff --git a/bin/wercconf.rc 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 @@ -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 { +# +#} diff --git a/lib/_users/login.tpl b/lib/_users/login.tpl @@ -5,9 +5,9 @@ % } % if not { % if (~ $REQUEST_METHOD POST) -% echo 'Login failed!' -<form method="POST"> - <label>User name: <input type="text" name="user_name" /></label><br /> +% echo '<div class="notify_errors">Login failed!</div>' +<form method="POST" style="text-align: right; float: left;"> + <label>User name: <input type="text" name="user_name" value="%($"post_arg_user_name%)"/></label><br /> <lavel>User password: <input type="password" name="user_password" /></label><br /> <input name="s" type="submit" value="Login" /> </form> diff --git a/lib/headers.tpl b/lib/headers.tpl @@ -27,7 +27,7 @@ Content-Type: text/html % if(! ~ $#h 0) % cat $h - %($extraHeaders%) + %($"extraHeaders%) </head> <body> diff --git 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; diff --git 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 )