#!/usr/bin/perl # ausführliche Fehlermeldungen # und besseres Perl use strict; use warnings; # einfacher Zugriff auf Übertragene Daten use CGI; # Fehler an den Browser senden use CGI::Carp qw(warningsToBrowser fatalsToBrowser); # mit Cookies arbeiten use CGI::Cookie; # zum Verschlüsseln von Passworten use Digest::MD5 qw(md5_hex); # konstante zum Exklusiven Lock einer Datei importieren use Fcntl qw(LOCK_EX); # Script ist UTF-8 use utf8; # Ausgabe ist UTF-8 binmode( STDOUT, ':encoding(UTF-8)' ); ######################################################################## # Alle wichtigen Einstellungen # das Verzeichnis in dem sich die die "Session Datenbank" und die "User Datenbank befinden" # dieses Verzeichnis sollte nicht im Browser aufrufbar sein! # Wenn jeder weiß welche Sessions aktiv sind und welche Nutzer sich einloggen dürfen, # dann kann man das auch gleich weg lassen my $basis_verzeichnis = '/kunden/homepages/30/d11176583/htdocs/werftler/gaestebuch'; # das Verzeichnis in dem sich die Gästebuchdatei befindet my $template_verzeichnis = '/kunden/homepages/30/d11176583/htdocs/werftler/gaestebuch'; # der "web-pfad" zum Ordner mit dem Gästebuch. # das ist der Pfad der vom Browser aus sichtbar ist my $web_werzeichnis = '/werftler/gaestebuch/'; # die Datei in dem die Sessions gespeichert werden my $session_database = 'session.db'; # die Datei in dem die Login Daten gespeichert sind my $nutzer_database = 'user.db'; # die Datei in der das Template eingefügt wird # das ist das Gästebuch my $website = 'gaestebuch.html'; # der Name dieses Scripts my $script_name = $ENV{SCRIPT_NAME}; # wie lange ist eine Session gültig my $session_timeout = 3600; # sekunden; ######################################################################## # HTML Blöcke zusammengefasst, # das macht den restlichen Code übersichtlicher. # Teile die später ersetzt werden sind mit %--name--% gekennzeichnet # die Funktion replace_template ersetzt die Werte. # Das Login my $login_page = << 'END_OF_HTML'; BVB-FAN!

Login

Benuzername:
Passwort:


END_OF_HTML # Die Gästebucheingabe my $input_page = << 'END_OF_HTML'; BVB-FAN!

Eintrag schreiben

%--error_msg--%

Author:
Email:
Homepage:
Text:


END_OF_HTML # Die Danke Seite mit der Weiterleitung my $danke_page = << 'END_OF_HTML'; BVB-FAN!

Danke für Ihren Eintrag

zurück END_OF_HTML # das Template, welches in das Gästebuch eingefügt wird my $entry_template = << 'END_OF_HTML';
Text: %--text--%
Author: %--author--%
Email: %--email--%
Homepage: %--homepage--%






END_OF_HTML ######################################################################## # Das eigentliche Script # eine CGI Instanz erzeugen # das ist viel einfacher und sicherer als es von Hand zu machen my $cgi=CGI->new(); # Session id holen. Daran wird festgestellt, # ob ein Login schon erfolgt ist my $session_id=$cgi->param('session_id'); # Ist die Session ID in einem Cookie? unless($session_id) { my %cookies = CGI::Cookie->fetch; if($cookies{'session_id'}) { $session_id = $cookies{'session_id'}->value; } } # alle Sessions laden my %sessions=load_sessions("$basis_verzeichnis/$session_database"); my $session_data={}; # ist eine Session ID vorhanden? if($session_id and exists($sessions{$session_id})) { $session_data=$sessions{$session_id}; } # eine neue Session erzeugen else { ($session_id,$session_data)=make_new_session(%sessions); $sessions{$session_id}=$session_data; } # ein neues Cookie mit der SessionID erzeugen my $cookie=CGI::Cookie->new(-name=>'session_id' , -value=>$session_id); # http Header ausgeben # Ausgabe ist UTF-8 und Cookie setzen print $cgi->header(-charset => 'utf-8', -cookie=>[$cookie] ); # Perl Warungen im Browser ausgeben (sind Kommentare im erzeugten HTML) warningsToBrowser(); # überprüfen ob sich jemand einloggen will my $user_name=$cgi->param('name'); my $user_pass=$cgi->param('passwort'); if($user_name and $user_pass) { # Alle Nutzerdaten laden my %user=load_user("$basis_verzeichnis/$nutzer_database"); # ist der Nutzer vorhanden? if(exists($user{$user_name})) { # alle Passworte sind gesalzen, # das macht es schwehrer diese zu entschlüsseln my $salt=$user{$user_name}->{salt}; # den Passwort-hash erzeugen my $passwort=md5_hex("$salt$user_pass"); # den Passwort-hasch mit dem Gespeicherten vergleichen if($passwort eq $user{$user_name}->{passwort}) { # login erfolgreich $session_data->{login_ok}=1; } } } # ist der Benutzer noch nicht eingeloggt? if(!$session_data->{login_ok}) { # login Page zeigen: print replace_template( $login_page, script_name => $script_name ); } # login ist erfolgt else { # timeout der Session zurück setzen $session_data->{timeout}=time()+$session_timeout; my $author = $cgi->param('author') // ''; my $email = $cgi->param('email') // ''; my $homepage = $cgi->param('homepage') // ''; my $text = $cgi->param('text') // ''; # sind die Texte OK? kein HTML erlaubt! my $text_ok=1; $text_ok=0 if( $author =~ s/<[^>]*>//gs ); $text_ok=0 if( $email =~ s/<[^>]*>//gs ); $text_ok=0 if( $homepage =~ s/<[^>]*>//gs ); $text_ok=0 if( $text =~ s/<[^>]*>//gs ); # wenn Eingaben Fehlen oder Fehlerhaft sind if( !($author and $email and $text and $text_ok) ) { # schon was Eingegeben aber nicht vollständig my $error_message=''; if( !($author and $email and $text) ) { $error_message.=" Daten nicht vollständig! Bitte geben sie Autor und Email und einen Text ein! "; } # der Text enthielt unerlaubte zeichen (HTML ist nicht erlaubt!) if(!$text_ok) { $error_message.=" HTML ist nicht erlaubt!"; } # und die Eingabeseite anzeigen print replace_template( $input_page, script_name => $script_name, session_id => $session_id, author => $author, email => $email, homepage => $homepage, text => $text, error_msg => $error_message, ); } # jemand hat einen Text geschrieben else { # Daten in das Template eintragen my $data = replace_template( $entry_template, author => $author, email => $email, homepage => $homepage, text => $text, ); # Seite öffnen Datei ist UTF-8 kodiert open(my $fh, '+<:encoding(UTF-8)', "$template_verzeichnis/$website") or die "Can't open website - $!\n"; # exklusiven Zugriff, # nur immer ein laufende Script Instanz darf darauf zugreifen flock($fh, LOCK_EX) or die "Can't lock website - $!\n"; # alles auf einmal einlesen local $/=undef; my $site=<$fh>; # Inhalt ergänzen $site=~s/\Q\E/$data/; # zum Anfang der Datei seek($fh,0,0); # neuen Inhalt schreiben print $fh $site; # Datei schließen und damit Lock aufheben close($fh); # Danke Seite Ausgeben print replace_template( $danke_page, website => "$web_werzeichnis/$website", ); } } # Sessiondaten speichern save_session("$basis_verzeichnis/$session_database",%sessions); ######################################################################## # Funktionen: # angebene werte in den Templates ersetzen sub replace_template { my $template = shift; my %values = @_; # suche nach %--name--% im Template und ersetze es $template =~ s!%--(\w+)--%! $_ = $values{$1} // '' !egsi; return $template; } # Benutzerdatenbank laden sub load_user { my $file=shift; my %users; if(open(my $fh, '<:encoding(UTF-8)', $file)) { while(my $line = <$fh>) { chomp($line); next unless($line=~m!^([^;]+?);([^;]+?);([^;]+?)$!); $users{$1}={ name=>$1, salt=>$2, passwort=>$3 }; } } else { warn("Can't open userdb ($!)"); } return %users; } # alle Sessions aus einer Datei lesen sub load_sessions { my $file=shift; my %sessions; return %sessions unless(-f $file); if(open(my $fh, '<:encoding(UTF-8)', $file)) { while(my $line = <$fh>) { chomp($line); next unless($line=~m!^\s*([^;]+?)\s*;\s*([^;]+?)\s*;\s*([^;]+?)\s*$!); next if($3$1, login_ok=>$2, timeout => $3}; } close($fh); } else { warn("Can't open sessiondb ($!)"); } return %sessions; } # alles Sessions in eine Datei schreiben sub save_session { my $file=shift; my %sessions=@_; if(open(my $fh, '>:encoding(UTF-8)', $file)) { for my $val ( values %sessions ) { next unless( $val->{id} ); next unless( $val->{timeout} ); $val->{login_ok}=$val->{login_ok}?1:0; print $fh "$val->{id};$val->{login_ok};$val->{timeout}\n"; } } else { warn("Can't write sessiondb ($!)"); } } # eine neue SessionID erzeugen sub make_new_session { my %sessions=@_; my $id=''; while(!$id or exists($sessions{$id})) { $id.=int(rand(10)); } return $id,{id=>$id, login_ok=>0, timeout=>time }; }