#!/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
END_OF_HTML
# Die Gästebucheingabe
my $input_page = << 'END_OF_HTML';
BVB-FAN!
Eintrag schreiben
%--error_msg--%
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