#!/bin/bash # -*- coding:utf-8 -*- # file KLEIDER/web/src/pinw/pinw_setup # 2020-05-23 Herbert Schiemann # Pinwand-Darstellung einrichten # 2020-11-19 check_command # 2020-11-23 bugfix check_infile statt check_infiles # Zähler, Variable, Aktionen declare_vars () { # Ein Leerzeichen als Wert bedeutet, dass Positionsargumente verarbeitet werden _argv=""; # Suchpfad für rc-Dateien, : - getrennte Liste von Verzeichnispfaden # Falls leer, wird die Option --rc nicht speziell behandelt g_configpath= ; # Zähler g_counters=" \ verbose \ overwrite \ websrv \ keeptmp "; # Variable g_variables=" \ base \ webbase \ srcbase \ docroot \ secrets \ tempdir " ; # Aktionen g_actions=" \ xslt \ xhtml \ cgi \ htaccess \ htpasswd \ freexsl \ db \ dbtables \ dbuser \ upload " ; has_actions=0 ; } # declare_vars # Default-Werte setzen set_defaults () { local b=$(realpath $0); b=${b%/web/src/*}; no_db=1; no_dbtables=1; no_dbuser=1; no_upload=1; [[ -n $verbose ]] || verbose=1; [[ -n $overwrite ]] || overwrite=0; [[ -n $websrv ]] || websrv=0; [[ -n $base ]] || base=$b ; [[ -n $webbase ]] || webbase=$base/web ; [[ -n $srcbase ]] || srcbase=$webbase/src ; [[ -n $docroot ]] || docroot=$webbase/docroot ; [[ -n $secrets ]] || secrets=$webbase/secrets ; [[ -n $tempdir ]] || tempdir=$webbase/temp ; tempdir=$tempdir/$(date +%Y%m%d%H%M%S%N) ; } # set_defaults # Variable und Zähler initialisieren init_vars () { local v; declare_vars ; for v in $g_counters $g_variables $g_actions; do eval "$v=" ; done; } # init_vars # Ist der Dateipfad sicher, d.h # - Hat nur der Besitzer mehr als nur Leserecht für1 die Datei? # - Hat nur der Besitzer Schreibrecht für das Verzeichnis? # - Ist der Besitzer der Datei auch der Besitzer des Verzeichnisses? # - Ist die Datei nicht im Wurzelverzeichnis? is_secure () { local chk ; (( verbose )) && echo "prüfe Sicherheit $1" ; [[ -f "$1" && -r "$1" ]] || return 1 ; chk=$(stat --format=%A "$1") ; [[ $chk =~ ^.{4}[r-]{6}$ ]] || return 1 ; chk=${1%/*}; [[ -n "$chk" ]] || return 1; [[ "$chk" != "$1" ]] || chk=$(pwd); [[ -d "$chk" ]] || return 1 ; [[ $(stat --format=%u "$chk") == $(stat --format=%u "$1") ]] || return 1; chk=$(stat --format=%A "$chk") ; if [[ $chk =~ ^.{4}[rx-]{6}$ ]]; then (( verbose )) && echo "Datei $1 scheint sicher"; return 0; fi; return 1; } # Argumente verarbeiten read_args () { local wd ; local lastwd ; local var ; local ok ; has_actions=0 ; for wd in "$@"; do if [[ "$lastwd" = "--" ]]; then _argv="$_argv $wd"; elif [[ -n "$lastwd" ]]; then if [[ "$wd" =~ ^[\ a-zA-Z0-9./_#-]+$ ]]; then if [[ "$lastwd" == "rc" && -n "$g_configpath" ]]; then if ! read_configuration $wd; then (( verbose )) && echo "Kann Konfiguration $wd nicht lesen" ; exit 10 ; fi ; else ok=0 ; for var in $g_variables; do if [[ "$var" == "$lastwd" ]]; then (( ++ok )) ; eval "$var=\"$wd\"" ; break ; fi ; done ; if (( ! ok )); then (( verbose )) && echo "Unbekannte Option --$lastwd $wd" ; exit 11 ; fi ; fi ; else (( verbose )) && echo "Ungültiger Optionswert --$lastwd $wd" ; exit 12; fi; lastwd= ; else case "$wd" in --version ) show_version ; exit 0 ; ;; --help ) show_version ; show_help ; exit 0 ; ;; -- ) if [[ -n "$_argv" ]]; then lastwd=--; continue; else (( verbose )) && echo "Ungültige Option $wd" ; exit 13 ; fi ; ;; --* ) if [[ "$wd" =~ ^--[a-z][a-z0-9_]*$ ]]; then lastwd=${wd#--} ; ok=0 ; for var in $g_counters ; do if [[ "$lastwd" == $var ]]; then eval "(( ++$lastwd ))" ; elif [[ "$lastwd" == "no_$var" ]]; then eval "${lastwd#no_}=0" ; else continue; fi; (( ++ok )) ; break ; done; if (( !ok )); then for var in $g_actions; do if [[ "$lastwd" == "$var" ]]; then eval "(( ++$var ))" ; (( ++ok )); has_actions=1; break; elif [[ "$lastwd" == "no_$var" ]]; then eval "(( ++no_$var ))" ; (( ++ok )); break; fi; done; fi; (( ok )) && lastwd=; else (( verbose )) && echo "Ungültige Option $wd" ; exit 14 ; fi ; ;; * ) if [[ -n $_argv ]]; then _argv="$_argv $wd"; else (( verbose )) && echo "Ungültige Option $wd" ; exit 15 ; fi; ;; esac ; fi ; done ; if [[ -n $lastwd && "$lastwd" != "--" ]]; then (( verbose )) && echo "Unverarbeitete Option --$lastwd"; exit 16 ; fi ; } # read_args # Aktionen ausführen run_actions () { local act ; for act in $g_actions; do eval "(( ! has_actions && ! no_$act || $act )) && process_$act"; done; } # run_actions # Werte der Variablen anzeigen show_variables () { local v ; for v in $g_counters $g_variables $g_actions $1; do eval "echo \"$v = \$$v\"" ; done; } # show_variables # Zeigt eine kurze Hilfe an show_help () { local cmd=${0#*/} ; set_defaults ; cat << .HELP ; $cmd --version $cmd --help $cmd [Option]* --[no_]verbose Ablauf nach stdout ausgeben ($verbose)? --[no_]overwrite Existierende Dateien überschreiben ($overwrite)? --[no_]websrv htaccess-Dateien für den Webserver ($websrv)? --base BASE Datenträger-Verzeichnis ($base) --webbase WEBBASE Web-Basisverzeichnis ($webbase) --srcbase SRCBASE übergeordnetes Quell-Verzeichnis ($srcbase) --docroot DOCROOT Wurzel-Verzeichnis der Website ($docroot) --secrets SECRETS Pfad der Geheimnis-Datei ($secrets) --tempdir TEMPDIR Verzeichnis für temporäre Dateien ($tempdir) --[no_]keeptmp Temporäre Dateien behalten ($keeptmp)? --xslt Datei pinw.xslt einrichten --xhtml XHTML-Dateien einrichten --cgi CGI-Skripte --htaccess htaccess-Dateien anlegen --htpasswd htpasswd-Datei anlegen --freexsl freistehende XSLT-Dateien --db Lokale Datenbank anlegen --dbtables Lokalen Datenbank-Tabellen anlegen --dbuser Lokalen Datenbank-Nutzer anlegen --upload erstellte Dateien hochladen .HELP } # show_help # Zeigt die Version an show_version () { cat << .VERSION ; pinw_setup 20200528 Website kleider.herbaer.de: Pinwand-Darstellung einrichten 2020-09-04, Herbert Schiemann, h.schiemann@herbaer.de GPL Version 2 oder neuer .VERSION } # show_version # ist ein Befehl verfügbar? # check_command xsltproc sed check_command () { local f ; for f in "$@"; do if [[ -z "$(which $f)" ]]; then (( verbose )) && echo "Befehl $f ist nicht verfügbar."; return 1; fi; done; } # check_command # Kann die Ausgabedatei erstellt werden? check_outfile () { local fp=$1; local dir; local verb; (( verbose )) && verb=--verbose ; if [[ ! -e $fp ]]; then dir=${fp%/*}; if [[ -n $dir && ! -e $dir ]]; then mkdir -p $verb $dir ; if [[ ! -d $dir ]]; then (( verbose )) && echo "$dir ist kein Verzeichnis"; return 1; fi; fi; fi; if [[ -d $fp ]]; then (( verbose )) && echo "$fp ist ein Verzeichnis"; return 1; elif [[ -d $fp. ]]; then (( verbose )) && echo "$fp. ist ein Verzeichnis"; return 1; elif (( overwrite )); then if [[ -e $fp ]]; then (( verbose )) && echo "lösche $fp"; rm $fp; fi; if [[ -e $fp. ]]; then (( verbose )) && echo "lösche $fp."; rm $fp.; fi; else if [[ -e $fp ]]; then (( verbose )) && echo "$fp existiert"; return 1; fi; if [[ -e $fp. ]]; then (( verbose )) && echo "$fp. existiert"; return 1; fi; fi; (( verbose )) && echo "$fp"; return 0; } # check_outfile # Sind Dateien lesbar und nicht leer? # check_infile file1 file2 ... check_infile () { local f ; for f in "$@"; do if [[ ! -f "$f" ]]; then (( verbose )) && echo "Datei $f existiert nicht"; return 1; fi; if [[ ! -s "$f" ]]; then (( verbose )) && echo "Datei $f ist leer"; return 1; fi; if [[ ! -r "$f" ]]; then (( verbose )) && echo "Datei $f kann nicht gelesen werden"; return 1; fi; done; return 0; } # check_infile # Sind die Dateien ausführbar? # check_executeable first/path/to/script path/to/second_srcipt ; check_executeable () { local f ; for f in "$@"; do if [[ ! -f "$f" ]]; then (( verbose )) && echo "$f\" ist keine gewöhnliche Datei"; return 1; fi; if [[ ! -x "$f" ]]; then (( verbose )) && echo "$f\" ist keine ausführbare Datei"; return 1; fi; done; return 0; } # check_executeable # gzip-komprimierte Datei(en) hinzufügen add_gzip () { local f; for f in "$@"; do [[ -f $f ]] || continue; [[ -f $f.gz ]] && rm $f.gz; [[ -e $f.gz ]] && continue; (( verbose )) && echo "erstelle $f.gz"; gzip --best --stdout $f > $f.gz ; (( verbose )) && echo "umbennen $f -> $f."; mv $f $f.; done ; } # add_gzip # Hilfsfunktion: temporäre Javascript und CSS-Dateien erzeugen # proc_tempfiles subdir proc_tempfiles () { local td=$1 ; # Verzeichis der Zwischendateien local s; # Quelldatei local o; # Ausgabedatei local i=$srcbase/sitestyle ; local p=$srcbase/pinw ; check_executeable $i/clean_js.pl $i/clean_css.pl $p/js_conditional.pl || return 1; for s in $p/*.js ; do [[ -f $s ]] || continue; o=$td/${s##*/} ; check_outfile $o || continue; $p/js_conditional.pl < $s \ | $i/clean_js.pl --in - --out $o ; done; for s in $p/*.css ; do [[ -f $s ]] || continue; o=$td/${s##*/} ; check_outfile $o || continue; $i/clean_css.pl --in $s --out $o ; done; } # proc_tempfiles # Hilfsfunktion: Datei mit kurzen Text-Schlüsseln # proc_shortids subdir proc_shortids () { local sd=$1; (( verbose )) && echo "proc_shortids $1"; local p=$srcbase/sitestyle/shortids.pl ; local t=$srcbase/sitestyle/localization_idlist.xslt ; local ids=$sd/shortids.xml ; local loc=$srcbase/sitestyle/local.xml.de ; check_executeable $p || return 1 ; check_infile $t $loc || return 1 ; check_outfile $ids || return 1 ; xsltproc $t $loc | $p > $ids; return 0 ; } # proc_shortids # XSLT-Dateien process_xslt () { local s ; # Pfad einer XSLT-Datei (Quelle) local n ; # Basis-Name ohne Verzeichnispfad oder Suffix local a ; # Dateinamenssuffix '.xslt' oder '.stub' local o=$docroot/style ; # Zielverzeichnis $docroot/style [[ -L $o ]] && return ; local o2; # Zielpfad local i=$srcbase/sitestyle ; local p=$srcbase/pinw ; (( verbose )) && echo "process_xslt"; local t=$tempdir/upload ; [[ -f $t ]] || check_outfile $t || return; local td="$tempdir/files" ; proc_tempfiles $td ; proc_shortids $td || return ; for n in pinw values ; do for a in .xslt .stub ; do s=$p/$n$a ; check_infile $s || continue; o2=$o/$n$a ; check_outfile $o2 || continue; xsltproc \ --stringparam p_tmpprefix $td/ \ --stringparam p_shortids $td/shortids.xml \ $i/styleincl_step_1.xslt $s \ | xsltproc --xinclude $i/styleincl_step_2.xslt - \ > $o2 ; add_gzip $o2 ; echo "put ${o2#$docroot/}." >> $t; echo "put ${o2#$docroot/}.gz" >> $t; done; done; (( keeptmp )) || rm --recursive $td ; } # process_xslt # XHTML-Dateien process_xhtml () { (( verbose )) && echo "process_xhtml"; local p=$srcbase/pinw ; local d=$p/pival.pl ; local e=$p/rm_installpi.xslt ; local i=$srcbase/sitestyle ; local t0=$i/localization_repltext.xslt ; local t1=$i/help_step_1.xslt; local t2=$i/help_step_2.xslt; local r=$i/rmxmlns.pl ; check_executeable $d $r || return; check_infile $e $t0 $t1 $t2 || return; local t=$tempdir/upload ; [[ -f $t ]] || check_outfile $t || return; local s; # Quelle local o; # Ziel local l; # Sprache local td="$tempdir/files" ; proc_tempfiles $td ; for s in $p/*.xhtml*; do [[ $s =~ ~$ ]] && continue; o=$($d < $s); [[ -n "$o" ]] || continue; [[ $o =~ ^/ ]] || o=$docroot/$o; check_outfile $o || continue; o=$(realpath $o); if [[ $s =~ \.xhtml\.([a-z]+)$ ]]; then l=${BASH_REMATCH[1]}; else l=de ; fi; check_infile $i/local.xml.$l || continue; xsltproc $e $s \ | xsltproc \ --stringparam p_local $i/local.xml.$l \ $t0 - \ | xsltproc --stringparam p_tmpprefix $td/ $t1 - \ | xsltproc --xinclude $t2 - \ | $r > $o ; add_gzip $o; echo "put ${o#$docroot/}." >> $t; echo "put ${o#$docroot/}.gz" >> $t; done; } # process_xhtml # CGI-Skripte process_cgi () { (( verbose )) && echo "process_cgi"; local b=$srcbase/pinw; check_infile $secrets || return; local r=$srcbase/localization/replace.pl; local c=$b/clean_pl.pl; local p=$b/pival.pl check_executeable $r $c $p || return; local d; local o; local t=$tempdir/upload ; [[ -f $t ]] || check_outfile $t || return; local f; for f in $b/*.cgs; do o=$($p < $f); (( verbose )) && echo "$f -> $o"; if [[ -z "$o" ]]; then (( verbose )) && echo "Zielpfad nicht angegeben"; o=${f#$b}; o=${o%.cgs}; fi; o=$docroot/$o; check_outfile $o || continue; $c < $f | $r --val $secrets > $o; chmod +x $o; echo "put ${o#$docroot/}" >> $t; done; } # process_cgi # htaccess - Dateien process_htaccess () { (( verbose )) && echo "process_htaccess"; local so=$overwrite ; overwrite=1 ; local o; # Ziel-Datei local s=$webbase/secrets; local d=$srcbase/pinw/pival.pl ; local r=$srcbase/localization/replace.pl; local c=$srcbase/sitestyle/clean_config.pl; if ! check_executeable $d $r $c; then overwrite=$so; return; fi; local t=$tempdir/upload ; if ! [[ -f $t ]] && ! check_outfile $t; then overwrite=$so; return; fi; local f; local w= ; (( websrv )) && w="--var web"; for f in $srcbase/pinw/*htaccess; do o=$($d < $f); [[ -n "$o" ]] || continue; [[ $o =~ ^/ ]] || o=$docroot/$o; check_outfile $o || continue; o=$(realpath $o); $r --val $s $w < $f | $c > $o; if (( websrv )) && [[ $o =~ ^$docroot ]]; then echo "put ${o#$docroot/}" >> $t; fi; done; overwrite=$so; } # process_htaccess # htpasswd - Datei process_htpasswd () { (( verbose )) && echo "process_htpasswd"; local htpw=$(which htpasswd); local g=$srcbase/pinw/get_data.pl ; check_executeable $htpw $g || return; check_infile $secrets || return; local pwf=$docroot/cgi-bin/private/.htpasswd ; check_outfile $pwf || return; local u=$($g --key website.login < $secrets); local p=$($g --key website.passwd < $secrets); if [[ -z $u ]]; then (( verbose )) && echo "website.login nicht definiert"; return; fi; if [[ -z $p ]]; then (( verbose )) && echo "website.passwd nicht definiert"; return; fi; $htpw -bc $pwf $u $p; check_infile $pwf || return; local t=$tempdir/upload ; [[ -f $t ]] || check_outfile $t || return; echo "put ${pwf#$docroot/}" >> $t; } # process_htpasswd # freistehende XSLT-Dateien process_freexsl () { (( verbose )) && echo "process_freexsl"; local p=$srcbase/pinw ; local d=$p/pival.pl ; check_executeable $d || return; local e=$p/rm_installpi.xslt ; local m=$base/pool/xslt_minimize.xslt ; check_infile $e $m || return; local t=$tempdir/upload ; [[ -f $t ]] || check_outfile $t || return; local f; local s; local o; for f in \ attrvals_pinw_xslt \ attrvals \ storage_keys \ pinw_help_keys ; do s=$p/$f.xslt; check_infile $s || continue; o=$($d < $s); [[ -n "$o" ]] || continue; [[ $o =~ ^/ ]] || o=$docroot/$o; check_outfile $o || continue; o=$(realpath $o); xsltproc $e $s \ | xsltproc $m - > $o ; add_gzip $o; echo "put ${o#$docroot/}." >> $t; echo "put ${o#$docroot/}.gz" >> $t; done; } # process_freexsl # run_sql name (user, db) run_sql () { local s=$srcbase/pinw/like_$1.sql ; local r=$srcbase/localization/replace.pl ; local m=$(which mysql); check_infile $s $secrets || return 1; check_executeable $r $m || return 2; (( verbose )) && echo $s; $r --val $secrets < $s | $m ; } # run_sql # Lokale Datenbank anlegen process_db () { (( verbose )) && echo "process_db"; run_sql db ; } # process_db # Lokale Datenbank-Tabellen anlegen process_dbtables () { (( verbose )) && echo "process_dbuser"; run_sql tables ; } # process_dbtables # Lokalen Datenbank-Nutzer anlegen process_dbuser () { (( verbose )) && echo "process_dbuser"; run_sql user ; } # process_dbuser # Dateien hochladen process_upload () { (( verbose )) && echo "process_upload"; local verb; (( verbose )) && verb=--verbose ; local t=$tempdir/upload ; if check_infile $t && check_executeable $srcbase/localization/ftp.pl; then $srcbase/localization/ftp.pl $verb --putbase $docroot < $t ; fi; (( keeptmp )) || rm $t; } # process_upload # Sicherheit # export PATH=/bin:/usr/local/bin:/usr/bin ; IFS=$' \t\n' ; set -o noclobber ; # existierende Dateien werden nicht überschrieben shopt -s extglob nullglob ; init_vars ; read_args "$@" ; set_defaults ; check_command xsltproc realpath date stat pwd which || exit 1; (( verbose > 1 )) && show_variables ; run_actions ; exit 0; # end of file KLEIDER/web/src/pinw/pinw_setup