#!/bin/bash # # bt_ - Implementazione prototipo in Bash # # Realizzato da: icosaedro.it di Umberto Salsi - www.icosaedro.it # # Versione: 2004-02-14 # # Maggiori info: www.icosaedro.it/bt_/implementazione.html # # Programmi esterni utilizzati: # date cat dd md5sum urlencode urldecode mkdir mv sed grep tr umask 0077 BASE_DIR=/tmp export PATH=/bin:/usr/bin:/usr/local/bin ##################################################### # # bt_ core # ##################################################### function log() { echo "$( date "+%m-%d, %H:%M:%S" ) $0, $REMOTE_ADDR: $@, query=$query" | cat -vt >&2 } # Formato del bt_file # ------------------- # # Il bt_file e' costituito da zero o piu' righe. Ogni riga e' # costituita da 3 o piu' colonne: # # 1. Indice come risulta dal parametro "i" (se la pagina e' stata # invocata con metodo GET) o dal nome del bottone "buttonXX" (se la # pagina e' stata invocata con metodo POST). # # 2. "forw" e' la call-forward; "back" e' la call-backward. Ad una # call-forw. puo' corrispondere zero o una call-back. # # 3. Eventuali argomenti della funzione di call-forw. o call-back. # Contatore dei link (ancore e bottoni): link_n=0 function _encode_parameters() { local p p="" while [ $# -gt 0 ]; do [ -n "$p" ] && p="$p " if [ -z "$1" ]; then p="$p %00" else p="$p$( printf %s "$1" | urlencode )" fi shift done echo "$p" } function bt_stack_push() { if [ $# -lt 1 ]; then log "stack_push(): manca parametro" exit 1 fi _encode_parameters "$@" >> $bt_stack } function bt_stack_pop() { if [ ! -s $bt_stack ]; then log "bt_stack_pop(): stack vuoto o assente" menu exit fi tos="$( tail -1 $bt_stack )" sed \$d $bt_stack > $bt_stack.x mv $bt_stack.x $bt_stack } function bt_link() # # bt_link FUNCTION { ARGUMENT } # # Genera URL del link che chiama la call-forward proc. indicata. # Ritorna l'URL nella var. globale $link. # NOTA: sarebbe stato piu' elegante ritornare il risultato della # funzione su stdout. Pero' questo avrebbe richiesto l'esecuzione # in un sottoprocesso, impedendo cosi' di incrementare il valore # della var. globale $link_n. # # Esempio: # # bt_link MostraMenu file edit view # echo "Mostra il menu" # { if [ $# -lt 1 ]; then log "bt_link(): manca parametro" exit 1 fi if [ $link_n -eq 0 ]; then > $bt_file fi link_n=$(( link_n + 1 )) echo "$link_n forw $( _encode_parameters "$@" )" >> $bt_file link="$SELF?i=$link_n" } function bt_alink() # # Come bt_link(), ma genera in output l'ancora ... # # Parametri: # $1: testo dell'ancora # $2: funzione call-forward da chiamare # $3, ...: eventuali parametri da passare alla call-forward proc. # # Esempio: # # bt_alink "MENU PRINCIPALE" MostraMenu file edit view # { if [ $# -lt 2 ]; then log "bt_alink(): mancano parametri" exit 1 fi local text text="$1" shift bt_link "$@" echo -n "$text" } function bt_form() { echo "
" } function bt_button() # # Genera in output un bottone e imposta la call-forward proc. che lo # gestisce in caso di submit. # Argomenti: # $1: testo del bottone # $2: funzione da chiamare # $3, ...: eventuali parametri # { if [ $# -lt 2 ]; then log "bt_button(): manca parametro" exit 1 fi local button="$1" shift if [ $link_n -eq 0 ]; then > $bt_file fi link_n=$(( link_n + 1 )) echo "$link_n forw $( _encode_parameters "$@" )" >> $bt_file echo "" } function bt_return_to() # # Argomenti: # $1: funzione # $2, ...: eventuali parametri della funzione # # FIXME: controlla che sia $link_n >= 1. # FIXME: controlla che non venga chiamata 2 o piu' volte per un dato link. { if [ $# -lt 1 ]; then log "bt_return_to(): manca parametro" exit 1 fi echo "$link_n back $( _encode_parameters "$@" )" >> $bt_file } function _call_function() # FIXME: certi caratteri (come \n) vengono convertiti in spazi. { declare -a p[] local np i np=$# i=1 while [ $i -le $np ]; do p[$i]="$( echo -n "$1" | urldecode )" shift i=$(( i + 1 )) done "${p[@]}" } function bt_return() { bt_stack_pop _call_function $tos "$@" exit } function bt_go() # # bt_go N # # Invoca la funzione call-forw. numero "N" del bt_file. # Se e' presente una corrispondente call-back., quest'ultima viene # preventivamente inserita nello stack. # { i="$1" if ! echo "$i" | tr -d -c 0-9 | grep -q "^[0-9]\\{1,4\\}$"; then log "bt_go(): i=$i: sintassi errata" menu exit fi if [ ! -r $bt_file ]; then log "bt_go(): il file $bt_file non esiste o non e' leggibile" exit 1 fi # Recupera la call-backward function e mettila nello stack: b="$( grep "^$i back " $bt_file | sed "s/^[0-9]* back //" )" [ -n "$b" ] && echo "$b" >> $bt_stack # Recupera la call-forward function e richiamala: f="$( grep "^$i forw " $bt_file | sed "s/^[0-9]* forw //" )" if [ -z "$f" ]; then log "indice i=$i non presente nel bt_file" menu exit fi # Svuota il file di backtracking, che non serve piu': > $bt_file _call_function $f } function bt_set() # bt_set NAME VALUE # NOTE: we cannot use "echo" because "bt_set -n" write nothing to the file # and "echo -- $2" is not undestood by "echo". { printf %s "$2" > $BASE_DIR/$bt_session/$1 } function bt_get() # bt_get NAME DEFAULT # Stdout: the value, or DEFAULT if not defined. { f=$BASE_DIR/$bt_session/$1 if [ -f $f ]; then cat $f else printf %s "$2" fi } function set_cookie() # # $1 = nome variabile # $2 = valore variabile # $3 = durata (secondi) # $4 = path (opzionale) # { value=$( printf %s "$2" | urlencode ) if [ -z "$4" ]; then path="" else path="; Path=$4" fi # vecchia versione: #echo -n "Set-Cookie: $1=$value$path; expires=" #date -u --date="$3 seconds" "+%a, %d-%b-%y %H:%M:%S GMT" # nuova versione: echo "Set-Cookie: $1=$value$path; Max-Age=$3" } function get_cookie() { echo "$HTTP_COOKIE" | tr ' ' '\n' | tr -d ';' \ | grep "^$1=" | head -1 | sed "s/^[^=]*=//" | urldecode } #################################################### # # Utilita' # #################################################### function get_parameter () # # Ritorna in stdout il parametro di query specificato. # Se manca, ritorna la stringa vuota. # Se il parametro e' duplicato, ritorna l'ultima ricorrenza. # { printf %s "$query" | tr '&' '\n' | grep "^$1=" | tail -1 \ | sed "s/^.*=//" | urldecode } function text2html() # # Converte i suoi argomenti o il suo stdin in testo HTML. # { if [ $# -eq 0 ]; then cat else printf %s "$@" fi | sed -e 's/\&/\&/g' -e 's//\>/g' } function text2attribute() # # Converte i suoi argomenti o il suo stdin in attributo HTML. # Aggiunge le doppie virgolette di delimitazione ". # { if [ $# -eq 0 ]; then cat else printf %s "$@" fi | sed -e 's/\&/\&/g' -e 's/"/\"/g' \ | ( echo -n '"'; cat; echo -n '"' ) } function header() { echo "Content-Type: text/html" echo "Pragma: no-cache" echo "Cache-Control: Cache-Control: no-store, no-cache, must-revalidate" echo echo "" echo "bt_/bash" echo "" } function footer() { echo "" } function title() { echo "

$@

" } function error() { header title "ERRORE" echo "$@

" footer exit } function radio_button() { echo -n " $4" } function login_valida() { nome="$( get_parameter name )" pass="$( get_parameter pass )" if [ demo != "$name" -o demo != "$pass" ]; then login exit fi set $( dd if=/dev/urandom bs=10 count=1 2>&1 | md5sum ) bt_session=$1 if [ ! -d $BASE_DIR ]; then mkdir $BASE_DIR || exit 1 fi mkdir $BASE_DIR/$bt_session || exit 1 # Ok, sessione inizializzata con successo. Informa il client: set_cookie bt_session $bt_session 300 } function login() { cat - << EOT Content-Type: text/html Esempio bt_

Login

Nome:
Password:

 

Per la visualizzazione del programma dimostrativo inserire "demo" come nome e come password.

EOT } function logout_sicuro() { set_cookie "bt_session" "" 0 login } function logout() { header title "Logout" echo "

Sei sicuro di voler eseguire il logout?

" bt_form center bt_button Annulla menu hspace bt_button Logout logout_sicuro center_ bt_form_ footer } function vspace() { echo "
"
	echo "
" } function hspace() { echo "  " } function center() { echo "
" } function center_() { echo "
" } function bt_form_() { echo "" } function table_() { echo "" } ############################################### # # Applicazione di esempio # ############################################### function scegli_colore() # $1 = titolo # $2 = colore corrente (es. "#ffffff") { header title "$1" col_curr=$2 center echo "" colors="ff 88 44" for r in $colors; do echo "" for g in $colors; do for b in $colors; do color="#$r$g$b" echo "" done done echo "" done table_ vspace bt_form bt_button Annulla bt_return cancel bt_form_ center_ footer } function titolo_valida() { t="$( get_parameter titolo )" bt_set titolo "$t" bt_return } function titolo() { t="$( bt_get titolo "(senza titolo)" )" header title "Titolo del quadro" echo "

" echo "Dai un titolo all'opera d'arte che stai costruendo." echo "Più il titolo è enigmatico e suggestivo," echo "maggiore sarà l'interesse suscitato." echo "Non esiste un limite alla lunghezza del titolo, ma tieni" echo "presente che deve stare sotto il quadro, ed è" echo "meglio se la scritta non supera la larghezza del quadro." echo "

" bt_form center echo "Titolo: " vspace bt_button "Annulla" bt_return hspace bt_button "OK" titolo_valida center_ echo "" footer } function azzera_sicuro() { bt_set col_cornice "#ffffff" bt_set col_quadro "#ffffff" bt_set titolo "(senza titolo)" bt_return } function azzera() { header title "Azzera" cat - << EOT

Hai richiesto di azzerare il quadro che hai iniziato:

Sei sicuro di voler azzerare il quadro?

EOT vspace bt_form center bt_button Annulla bt_return hspace bt_button Azzera azzera_sicuro center_ echo "" footer } function arte() { if [ "$1" = col_quadro -a "$2" = ok ]; then bt_set col_quadro $3 fi if [ "$1" = col_cornice -a "$2" = ok ]; then bt_set col_cornice $3 fi col_quadro=$( bt_get col_quadro "#ffffff" ) col_cornice=$( bt_get col_cornice "#ffffff" ) header title "Arte astratta" echo "

Puoi comporre un semplice quadro di arte astratta!" echo "Scegli il colore della cornice e il colore del quadro che" echo "preferisci:

" center echo "
" if [ "$col_curr" = "$color" ]; then s="X" else s="   " fi bt_alink "$s" bt_return ok $color echo "
" echo "
" echo -n "
"
	echo "                   "
    echo "     ##     ##     "
	echo "    ##     ####    "
	echo "   ####   ##       "
	echo "  ##  #  ##        "
	echo " #####   ###       "
	echo "            ####   "
	echo -n "                   "
	echo "
" echo "
" echo "

" echo "" bt_get titolo "(senza titolo)" | text2html echo "

" center_ bt_alink "Colore del quadro" scegli_colore "Colore del quadro:" $col_quadro bt_return_to arte col_quadro echo "

" bt_alink "Colore della cornice" scegli_colore "Colore della cornice:" $col_cornice bt_return_to arte col_cornice echo "

" bt_alink "Titolo" titolo bt_return_to arte echo "

" bt_alink "Azzera" azzera bt_return_to arte echo "

" bt_form center bt_button OK bt_return center_ bt_form_ center_ footer } function altra_finestra() { header title "Finestra" echo "

Bè? che ti aspettavi mai di trovare? ;-)

" bt_form center bt_button OK bt_return center_ echo "" footer } function menu() # # Pagina base dell'applicazione. # { header title "Menu principale" bt_alink "Arte astratta" arte bt_return_to menu vspace bt_alink "Finestra 2" altra_finestra bt_return_to menu vspace center bt_form bt_button Logout logout bt_form_ center_ footer } # # main: # # # Individua la $query: # if [ "$REQUEST_METHOD" = POST ]; then query=$( head --bytes="$CONTENT_LENGTH" ) else query="$QUERY_STRING" fi # # Individua il cookie di sessione: # bt_session="$( get_cookie bt_session | tr -d -c 0-9a-f )" if ! printf %s "$bt_session" | grep -q '^[0-9a-f]\{32\}$' \ || [ ! -d "$BASE_DIR/$bt_session" ]; then # Sessione non valida. name="$( get_parameter name )" if [ -n "$name" ]; then # E' il POST dalla maschera di login: login_valida else # Presenta la maschera di login: login exit fi fi # # La sessione esiste: # SELF="$SCRIPT_NAME" bt_file=$BASE_DIR/$bt_session/bt_file bt_stack=$BASE_DIR/$bt_session/bt_stack # rinfresca scadenza cookie: set_cookie bt_session $bt_session 300 # # Richiama la funzione dal bt_file: # i="" if [ "$REQUEST_METHOD" = GET ]; then i="$( get_parameter i | tr -d -c 0-9 )" elif [ "$REQUEST_METHOD" = POST ]; then i=$( echo "$query" | tr '&' '\n' | grep "^button[0-9]\\+=" | head -1 \ | sed "s/^button\\([0-9]\\+\\).*/\1/" ) fi if [ -n "$i" ]; then bt_go "$i" else menu fi # #### Fine!