8. Svečių knyga

Viena populiariausių CGI programų pasauliniame Internet tinkle - svečių knyga (guestbook). Tai yra paprasčiausia forma, kuri leidžia tinklapio ar svetainės lankytojams palikti atsiliepimus bei informaciją apie save to tinklapio ar svetainės šeimininkui, o taip pat kitiems lankytojams. Informacija patalpinama į failą, kurį gali peržiūrinėti kiekvienas. Tai elektroninis variantas svečių knygos, su kuria turbūt beveik visiems teko susidurti realiame gyvenime - muziejuose, memorialuose, kitose įvairiausių žmonių lankomose vietose.

Kurdami svečių knygą internete, turime išskirti šiuos etapus:

Programa prasideda:

#!/usr/local/bin/perl
$webmaster = "virga\@soften\.ktu\.lt";
$method = $ENV{'REQUEST_METHOD'};
$script = $ENV{'SCRIPT_NAME'};
$query = $ENV{'QUERY_STRING'};

$document_root = "/home/staff/Virga/public_html";
$guest_file = "/CGI_knyga/guestbook.html";
$root_url = "http://www.soften.ktu.lt/~virga";
$guest_url = $root_url . $guest_file;
$full_path = $document_root . $guest_file;

Šiame inicializavimo kode document_root kintamasis yra katalogas, kuriame yra jūsų HTML failai. Guest_file kintamajo reikšmė yra reliatyvus kelias iki svečių knygos failo, imant šakniniu katalogą, nurodytą document_root kintamajame. O full_path yra pilnas kelias iki svečių knygos failo. Kintamasis root_url yra pilnas jūsų svetainės internetinis adresas. O guest_url yra pilnas internetinis adresas, pagal kurį pasiekiamas svečių knygos failas. Labai svarbu atskirti pilną ir reliatyvų kelią, kaip pamatysime toliau nagrinėdami programą.

$exclusive_lock = 2;
$unlock = 8;

Failo užrakinimo ir atrakinimo reikšmės yra saugomos atitinkamai exclusive_lock ir unlock kintamuosiuose.

if ($method eq "GET") {

if ($query eq "add") {

Ši programa parašyta truputį kitaip nei kitos programos, kurias sutikote šiojeknygoje. Pirmiausia pažiūrėkime, kaip į programą (guestbook.pl) galime kreiptis:

Kaip matote, programa yra labai visapusiška. Ji atlieka visus veiksmus su svečių knyga. Visiškai nesunku yra suskaidyti programą į jos sudėtines dalis: HTML formą, programą svečių knygos išvedimui (nebūtina), ir programą iš formos gautai informacijai dekoduoti. Bet kuris atvejis turi savų privalumų. Visų veiksmų apjungimas vienoje programoje garantuoja, kad visi programos komponentai yra vienoje vietoje ir failai negali būti atsitiktinai patalpinti ne ten. Iš kitos pusės, atskyrimas užtikrina kiekvieno svečių knygos komponento savarankiškumą - visi jie gali būti modifikuojami nerizikuojant pažeisti kitų komponentų vientisumo. Tad pasirinkimas priklauso tik nuo to, kas konkrečiam asmeniui, kuriančiam svečių knygą, yra svarbiau.

$date_time = &get_date_time();

Paprogramė get_date_time grąžina einamąją datą ir laiką.

&MIME_header ("text/html", "CGI puslapio svečių knyga");

Paprogramė MIME_header išveda nurodytą MIME headerį ir suteikia dokumentui pavadinimą, kurį vartotojas perdavė kaip argumentą. Vienintelė priežastis, kodėl čia naudojama paprogramė - tai noras padaryti programą kuo kompaktiškesne.

print <<End_Of_Guestbook_Form;

Tai yra  svečių knygos CGI skriptas, leidžiantis žmonėms palikti šiame puslapyje atsiliepimus, kuriuos galės matyti ir kiti lankytojai. Prašome įvesti visą reikiamą informaciją, <B>ir</B> jei jūs turite savo tinklapį, įveskite jo adresą, kad būtų galima sukurti hipertekstinę nuorodą.
<P>
Dabar yra: $date_time
<HR>

Pirmiausia yra išvedamas pasisveikinimas kartu su einamąja data ir laiku. (Jūs negalite kviesti paprogramių print "blokų" viduje, todėl get_date_time paprogramė datos ir laiko nustatymui buvo iššaukta anksčiau, o jos grąžinta reikšmė priskirta kintamajam date_time.).

<FORM METHOD="POST">
<PRE>
<EM>Pilnas vardas</EM>:<INPUT TYPE="text" NAME="name" SIZE=40>
<EM>E-mail</EM>:<INPUT TYPE="text" NAME="from" SIZE=40>
<EM>WWW tinklapis</EM>:<INPUT TYPE="text" NAME="www" value="http://" SIZE=40>
</PRE>
<P>
<EM>Įveskite informaciją, kurią norite pateikti:</EM><BR>
<TEXTAREA ROWS=3 COLS=60 NAME="comments"></TEXTAREA><P>
<INPUT TYPE="submit" VALUE="Įtraukti į svečių knygą">
<INPUT TYPE="reset"  VALUE="Išvalyti informaciją"><BR>
<P>
</FORM>
<HR>

End_Of_Guestbook_Form

Kaip matote, nėra nurodytas  ACTION atributas <FORM> aprašyme. Kai ACTION atributas praleistas, naršyklė pagal nutylėjimą persiunčia užpildytą formą šitai pačiai CGI programai. METHOD priskirta reikšmė POST - kaip pamatysite vėliau, pagal tai svečių knygos programa sužinos, kad forma užpildyta.

Čia yra išvedami įvairūs sudarantys formą elementai. Komanda <PRE> išlygina tekstinius laukus. Štai taip užpildytą formą atvaizduoja naršyklė Inernet Explorer4:

ie4form.gif (32070 bytes)

Jeigu kreipinyje į programą nebuvo užklausos parametro, yra išvedamas svečių knygos duomenų failas.

} else {

if (open(GUESTBOOK, "<" . $full_path) ) {

flock (GUESTBOOK, $exclusive_lock);

Kintamojo full_path reikšmė yra pilnas kelias iki svečių knygos failo. Pagrindinė priežastis, kodėl reliatyvus kelias ir pilnas kelias iki failo yra saugomi atskirai, yra ta, kad hipertekstiniams tikslams reikalingas reliatyvus kelias, o failui atidaryti reikalingas pilnas kelias. Prieš atidarant bet kokį failą, derėtų patikrinti, ar tas failas gali būti atidarytas.

&MIME_header ("text/html", "Tai yra mūsų svečių knyga!");

while (<GUESTBOOK>) {
    print;
}

flock (GUESTBOOK, $unlock);
close(GUESTBOOK);

Ciklas eina per visas failo eilutes ir standartiškai išveda jas:

ie4svkn.gif (34115 bytes)

} else {

&return_error (500, "Svečių knygos failo klaida", "Negaliu nuskaityti svečių knygos failo [$full_path].");

}

}

Jeigu atidarant failą iškilo kokios nors problemos, kliento programai (naršyklei) perduodamas pranešimas apie klaidą. Paprogramė return_error yra tokia pati, kokia buvo pateikta 4 skyriuje, "Formos ir CGI".

Prisimenate "add" formą, kurioje <FORM> aprašyme buvo nurodytas POST metodas?
Štai čia ši forma yra apdorojama. Jeigu užklausos metodas yra POST, tai reiškia, kad vartotojas užpildė formą ir pateikė ją atgal šiai programai.

} elsif ($method eq "POST") {

if (open(GUESTBOOK, ">>" . $full_path) ) {

flock (GUESTBOOK, $exclusive_lock);

$date_time = &get_date_time();
&parse_form_data (*FORM);

Dabar įtrauksime naują įrašą į svečių knygą. Pirmiausia programa patikrina, ar gali rašyti į svečių knygos failą. Jeigu neįvyksta klaida, failas atidaromas papildymo režimui ir užrakinamas, kad nebūtų prieinamas jokiems kitiems procesams. Formos informacija yra dekoduojama ir patalpinama į asociatyvų masyvą FORM. Paprogramė parse_form_data šioje programoje yra šiek tiek kitokia, nei ta, kurią sutikome 4 skyriuje; ji netikrina GET užklausų, kadangi programa naudoja ją tik POST metodui.

$FORM{'name'} = "Anonimas"           if !$FORM{'name'};
$FORM{'from'} = $ENV{'REMOTE_HOST'} if !$FORM{'from'};

Aukščiau matote konstrukciją, kurios galbūt neteko sutikti anksčiau. Tai paprastesnis užrašymo būdas tokių išraiškų:

if (!$FORM{'name'}) {
     $FORM{'name'} = "Anonimas";
}
if (!$FORM{'from'}) {
     $FORM{'from'} = $ENV{'REMOTE_HOST'};
}

Kitaip sakant, formos kintamieji name ir from yra patikrinami, ar yra užpildyti. Jei laukai tušti, jiems priskiriamos reikšmės pagal nutylėjimą.

$FORM{'comments'} =~ s/\n/<BR>/g;

Informacija, kurią lankytojas įvedė <TEXTAREA> lauke yra saugoma comments.Visi naujos eilutės simboliai pakeičiami HTML perėjimo žymėmis. Tai užtikrina, kad informacija bus išvedama teisingai. Atkreipkite dėmesį, kad jei vartotojas kaip komentarų dalį įveda HTML kodą (arba SSI direktyvas) , kodas bus interpretuojamas. Taigali būti pavojinga. Žiūrėkite skyrių "Sąsajos (gateways), duomenų bazės ir paieškos/indeksavimo priemonės", kuriame pateiktas būdas, kaip "išvengti" HTML kodo.

print GUESTBOOK <<End_Of_Write;

<P>
<B>$date_time:</B><BR>
Žinutę atsiuntė <EM>$FORM{'name'}</EM> iš <EM>$FORM{'from'}</EM>:
<P>
$FORM{'comments'}

End_Of_Write

Lankytojo vardas, internetinės mašinos vardas ir komentarai, kartu su einamąja data ir laiku, yra išvedami i svečių knygos failą.

if ($FORM{'www'} and !($FORM{'www'} eq "http://")) {

print GUESTBOOK <<End_of_Web_Address;

<P>
$FORM{'name'} taip pat turi tinklapį:
<A HREF="$FORM{'www'}">$FORM{'www'}</A>

End_of_Web_Address

}

print GUESTBOOK "<P><HR>";

Jei vartotojas pateikė HTTP adresą, pastarasis taip pat bus išvestas.

flock (GUESTBOOK, $unlock);
close(GUESTBOOK);

Failas atrakinamas ir uždaromas. Labai svarbu nepamiršti atrakinti ir uždaryti svečių knygos failą, norint užtikrinti priėjimą prie jo kitiems žmonėms.

Galiausiai, jei viskas tvarkoje, išvedamas padėkos pranešimas ir nuoroda į svečių knygą, kad lankytojas galėtų ją peržiūrėti.

&MIME_header ("text/html", "Ačiū!");

print <<End_of_Thanks;

Dėkojame už apsilankymą mūsų svečių knygoje. Jeigu norite ją peržiūrėti, paspauskite <A HREF="$guest_url">čia</A> (tikras svečių knygos HTML failas), arba <A HREF="$script">čia</A> (svečių knygos skriptas be užklausos parametro).

End_of_Thanks

Jei programa negali rašyti į svečių knygos failą, generuojamas pranešimas apie klaidą. Kitoks pranešimas yra generuojamas, jeigu naudojamas netinkamas kreipimosi į CGI programą metodas.

} else {

&return_error (500, "Svečių knygos failo klaida", "Negaliu rašyti į svečių knygos failą [$full_path].");

}

} else {

&return_error (500, "Serverio klaida", "Serveris naudoja nežinomą metodą");

}

exit(0);

Paprogramė MIME_header išveda MIME headerį, o taip pat dokumento pavadinimą ir antraštę. Jei trečiasis argumentas nenurodytas, pavadinimas ir antraštė bus tokie patys.

sub MIME_header
{

local ($mime_type, $title_string, $header) = @_;

if (!$header) {

$header = $title_string;

}

print "Content-type: ", $mime_type, "\n\n";
print "<HTML>", "\n";
print "<HEAD><TITLE>", $title_string, "</TITLE></HEAD>", "\n";
print "<BODY>", "\n";
print "<H1>", $header, "</H1>";
print "<HR>";

}

Paprogramė get_date_time grąžina einamąją datą ir laiką.

sub get_date_time
{

local ($months, $weekdays, $ampm, $timestring);

$months = "Sausio/Vasario/Kovo/Balandžio/Gegužės/" .
              "Birželio/Liepos/Rugpjūčio/Rugsėjo/" .
              "Spalio/Lapkričio/Gruodžio";

$weekdays = "Sekmadienis/Pirmadienis/Antradienis/" .
                "Trečiadienis/Ketvirtadienis/" .
                "Penktadienis/Šeštadienis";

local ($sec, $min, $hour, $day, $nmonth, $year,
           $wday, $yday, $isdst) = localtime(time);

Funkcija localtime grąžina devynių elementų masyvą, kuris sudarytas iš laiko, datos ir esamos laiko zonos. Ankstesniuose pavyzdžiuose mes naudojome tik pirmuosius tris šio masyvo elementus; šiame pavyzdyje mes priskiriame visus devynis.

$year += 1900;

$week  = (split("/", $weekdays))[$wday];
$month = (split("/", $months))[$nmonth];

Funkcijos localtime grąžinamos skaitmeninės savaitės dienos (wday) ir mėnesio (nmonth) reikšmės yra skaičiuojamos nuo nulio. Kintamajam week yra priskiriamas savaitės dienos pavadinimas paimant iš kintamojo weekdays eilutę, atitinkančią skaitmeninę savaitės dienos reikšmę. Toks pats veiksmas pakartojamas nustatant mėnesio pavadinimą.

$time_string = sprintf("%s m., %s %s d., %s - %02d:%02d:%02d", $year, $month, $day, $week, $hour, $min, $sec);

return ($time_string);

}

Taigi, paprogramė get_date_time grąžins datą tokia forma:

1998 m., Lapkričio 25 d., Trečiadienis - 15:45:02

Paskutinė paprogramė svečių knygos tvarkymo programoje yra parse_form_data.

sub parse_form_data
{

local (*FORM_DATA) = @_;

local ( $request_method, $post_info, @key_value_pairs, $key_value, $key, $value);

read (STDIN, $post_info, $ENV{'CONTENT_LENGTH'});

@key_value_pairs = split (/&/, $post_info);

foreach $key_value (@key_value_pairs) {

($key, $value) = split (/=/, $key_value);
$value =~ tr/+/ /;
$value =~ s/%([\dA-Fa-f][\dA-Fa-f])/pack ("C", hex ($1))/eg;

if (defined($FORM_DATA{$key})) {

$FORM_DATA{$key} = join ("\0", $FORM_DATA{$key}, $value);

} else {

$FORM_DATA{$key} = $value;

}

}

}

Kaip jau buvo minėta anksčiau, paprogramė netikrina GET užklausų. Nėra poreikio tai daryti, kadangi tai atliekama pagrindinėje programoje.