Jak na CGI
(aneb Daliho u₧ ne tak ·pln∞ malink² pr∙vodce s ruΦenφm omezen²m)

Autor: Ing. Dalibor èrßmek, email: dali@kumbal.vse.cz


Obsah

  1. ┌vodem
  2. Co je to CGI
  3. Kam umφstit CGI
  4. V²stup z CGI skriptu (p°φklad v shellu)
  5. HlaviΦka CGI skriptu
  6. Jak²m jazykem psßt
  7. Prost°edφ CGI skriptu (p°φklad v Perlu)
  8. DefinovanΘ prom∞nnΘ prost°edφ
  9. Co je to ten QUERY_STRING
  10. Vstup dat do CGI skriptu
  11. Vstup metodou POST (p°φklad v C)
  12. Pou₧itφ vstupnφch polφ
  13. Nad dopisy Φtenß°∙
  14. U₧iteΦnΘ dodatky (SSI, ...)

┌vodem

Tento text vznikl, jeliko₧ m∞ n∞kolik p°ßtel po₧ßdalo o radu p°i tvorb∞ CGI skript∙ a necht∞lo se mi to ka₧dΘmu zvlßÜ¥ opakovat. Pro jeho usp∞ÜnΘ strßvenφ se p°edpoklßdajφ obecnΘ znalosti operaΦnφho systΘmu a programovßnφ. KonkrΘtnφ p°φklady jsou z prost°edφ Unixu. ObecnΘ informace lze v∞tÜinou vyu₧φt i na jin²ch platformßch.

RuΦenφ omezenΘ, zmφn∞nΘ v podtitulu, se vztahuje zejmΘna na t°i skuteΦnosti:

Co je to CGI

CGI (Common Gateway Interface) je jednou z mo₧nostφ, jak zavΘst dynamiku do WWW strßnek. V zßsad∞ je to p°edpis pro program spouÜt∞n² na serveru, jak mß Φφst data od WWW serveru a jak mu mß data posφlat. V²sledkem je, ₧e kdy₧ se odkazujete na link s CGI programem (obvykle naz²van²m CGI skriptem), nenatßhne se do WWW klienta tento skript, ale v²stup zφskan² jeho spuÜt∞nφ.

Klasick²m p°φkladem jsou poΦφtadla p°φstupu na WWW strßnky, gatewaye k r∙zn²m slovnφk∙m a databßzφm nebo t°eba obm∞≥ujφcφ se reklamy na WWW. Nßsledujφcφ odkazy vedou k ukßzkßm CGI skript∙ na serveru Kumbßl, kterΘ dokumentujφ n∞kterΘ ze zp∙sob∙ vyu₧itφ:

Kam umφstit CGI

CGI skript nelze z d∙vodu bezpeΦnosti spouÜt∞t z jakΘhokoliv mφsta na serveru. Obvykle je t°eba dohodnout se se sprßvcem serveru o podmφnkßch provozovßnφ CGI skript∙. Mimo jinΘ byste se m∞li dozv∞d∞t, kam skript umφstit. Umφst∞nφ souvisφ takΘ s problΘmem rozpoznßnφ skriptu WWW serverem, kter² musφ rozliÜit, zda mß soubor poslat klientovi nebo jej spustit. RozliÜenφ se provßdφ bu∩ dle adresß°e, kde soubor je (skripty jsou jen v definovanych adresß°φch), nebo dle koncovky souboru (skripty mφvajφ .cgi,.pl).

Je takΘ t°eba zajistit, aby CGI skript byl spustiteln² pro WWW server. V UnixovΘm prost°edφ b∞₧φ WWW server obvykle s minimßlnimi prßvy (nap°. u₧ivatel nobody), proto musφ mφt skript nastavenß prßva pro spouÜt∞nφ jak²mkoliv u₧ivatelem.

Abyste se vyhnuli zbyteΦnΘmu hledßnφ chyby ve skriptu, kdy₧ ve skuteΦnosti je Üpatn∞ nakonfigurovan² server, doporuΦuji nejd°φve zkusit zcela jednoduchΘ skripty - ideßln∞ nap°φklad nφ₧e uveden² v²pis prom∞nn²ch prost°edφ.

UrΦitou mo₧nostφ, jak spouÜt∞t skripty na serveru je takΘ SSI.

V²stup z CGI skriptu

Jak ji₧ bylo uvedeno, je CGI skript v podstat∞ normßlnφ program. V unixovΘm prost°edφ to m∙₧e b²t t°eba b∞₧n² skript shellu, program v jazyce Perl nebo program zkompilovan² v n∞jakΘm z klasick²ch jazyk∙ (C, Pascal...).

Chcete-li, aby CGI skript m∞l n∞jak² v²stup, musφ jej zapisovat na za°φzenφ STDOUT (standardnφ v²stup - Φili obvykle obrazovka). Nßsledujφcφ p°φklad ukazuje velmi jednoduch² skript, kter² vypisuje datum (p°φklady jsou psßny pro Bourne shell a jemu podobnΘ).

#!/bin/sh
echo Content-type: text/plain
echo
date
Prvnφ °ßdek urΦuje, jak se bude CGI skript interpretovat - konkrΘtn∞ °φkß, ₧e skript se mß spustit pomocφ shellu /bin/sh. Pokud je CGI skript napsan² v interpretovanΘm jazyce (shell, Perl), je nutnΘ tento °ßdek uvßd∞t. Pro kompilovanΘ programy pochopiteln∞ nemß smysl.
Druh² a t°etφ °ßdek vypisuje povinnou hlaviΦku pro WWW server, kterß urΦuje typ p°enßÜen²ch dat. HlaviΦka je ukonΦena prßzdn²m °ßdkem (t°etφ °ßdek skriptu) - tak server poznß, ₧e nßsledujφcφ data jsou ji₧ pro klienta. O formßtu hlaviΦky bude jeÜt∞ psßno dßle.
Poslednφ °ßdek skriptu je p°φkaz vypisujφcφ datum.

Nazvete-li tento skript nap°. date.cgi a budete se na n∞j odkazovat linkem http://mujserver/mojecesta/date.cgi, server skript vykonß a v²sledek v podob∞ aktußlnφho data zaÜle klintovi.

Drobnou zm∞nou hlaviΦky docφlφte mo₧nosti formßtovat v²stup pomocφ HTML.

#!/bin/sh
echo Content-type: text/html
echo
echo "<HTML>"
echo "<BODY>"
echo "<H3>Aktußlnφ datum a cas</H3>"
echo "<B>"
date
echo "</B>"
echo "</BODY>"
echo "</HTML>"
HTML tagy se vypisujφ takΘ na standardnφ v²stup (povÜimnete si, ₧e v p°φkladu jsou uzav°eny do uvozovek, aby se zabrßnilo interpretaci specißlnφch znak∙ shellem).

JeÜt∞ poznßmka o standardnφch I/O za°φzenφch pro nep°φliÜ zkuÜenΘ programßtory. Se za°φzenφm STDIN a STDOUT se obvykle pracuje t∞mi nejjednoduÜφmi rutinami vstupu a v²stupu. V jazyve C jsou to nap°φklad printf a puts, v Pascalu pak writeln.

HlaviΦka CGI skriptu

Jak jsme vid∞li, musφ CGI skript vypisovat na STDOUT hlaviΦku pro klienta, kterß sestßvß minimßln∞ z jednoho °ßdku a je ukonΦena prßzdn²m °ßdkem. Podφvejme se, co vφce se dß jeÜt∞ s hlaviΦkou dosßhnout.
#!/bin/sh
echo Content-type: text/html
echo Pragma: no-cache
echo
Tato hlaviΦka (p°esn∞ji pridan² druh² °ßdek) °φkß klientovi, ₧e dokument nemß uchovßvat ve svΘ pam∞ti cache, co₧ je u prom∞nliv²ch v²stup∙ z CGI skript∙ velmi d∙le₧itΘ.

Jinou mo₧nostφ je vrßtit klientovi stavov² k≤d:

#!/bin/sh
echo Content-type: text/html
echo Status: 200 OK
echo
Pou₧itelnΘ k≤dy jsou zejmΘna: Nap°φklad k≤d 204 zp∙sobφ, ₧e klient nebude strßnku natahovat, co₧ se dß vyu₧φt v p°φpad∞ ÜpatnΘho zadßnφ vstupnφch hodnot - na obrazovce z∙stane stßle to stejnΘ.

Samoz°ejm∞, ₧e ne vÜechny prohlφ₧eΦe tyto k≤dy sprßvn∞ interpretujφ, ale v dneÜnφ dob∞ jich u₧ bude v∞tÜina.

To co se vypisuje v uveden²ch p°φpadech nestaΦφ jako kompletnφ hlaviΦka HTML dokumentu. Server v₧dy doplnφ dalÜφ hodnoty (nezadßte-li stavov² k≤d, server jej vlo₧φ). N∞kterΘ servery umo₧≥ujφ nakonfigurovat tak, ₧e v²stupy zadan²ch skript∙ ji₧ nejsou kontrolovßny (Non Parsed Headers), co₧ m∙₧e vΘst ke zv²Üenφ rychlosti odezvy. Musφte vÜak ve skriptu peΦliv∞ generovat kompletnφ hlaviΦku.

Jak²m jazykem psßt

AΦkoliv p°φklady v tomto textu jsou psßny jako skripty pro unixovsk² shell, nenφ takov² postup obecn∞ vhodn². D∙vodem je nep°φliÜ velkß efektivita, kterß znamenß vyÜÜφ zßte₧ serveru p°i spouÜt∞nφ (ve v²se zmφn∞nΘm skriptu, kter² vracφ aktußlnφ stav serveru, se nap°φklad spouÜtφ vφce ne₧ 50 proces∙).

Pro mΘn∞ Φasto spouÜt∞nΘ CGI skripty se doporuΦuje pou₧φvat (v unixovΘm prost°edφ) jazyk PERL, kter² obsahuje mimo jinΘ velmi mocnΘ nßstroje pro prßci s textem. ╚asto spouÜt∞nΘ skripty je optimßlnφ vytvo°it v n∞jakΘm kompilovanΘm jazyce. C a C++ sice neposkytujφ p°i programovßnφ skriptu tak silnΘ prost°edky, to je vÜak vyvß₧eno efektivitou provßd∞nφ.

P°i troÜe snahy lze dnes na Internetu nalΘzt sluÜnΘ mno₧stvφ knihoven pro C a C++, kterΘ umo₧≥ujφ pohodlnΘ pou₧itφ t∞chto jazyk∙ pro psanφ CGI skript∙.

Prost°edφ CGI skriptu

P°i spuÜt∞nφ CGI skriptu jsou nastaveny r∙znΘ prom∞nnΘ prost°edφ, ze kter²ch lze zjistit zajφmavΘ informace. VyzkouÜejte nßsledujφcφ p°φklad.
#!/bin/sh
echo Content-type: text/plain
echo
echo "Ukßzka n∞kter²ch prom∞nn²ch:"
echo "REMOTE_HOST = $REMOTE_HOST"
echo "REMOTE_USER = $REMOTE_USER"
echo "QUERY_STRING = $QUERY_STRING"
P°ed°azenφ znaku "$" oznßmφ shellu, ₧e se jednß o prom∞nnou prost°edφ. Zavolßte-li tento skript, m∞li byste obdr₧et aktußlnφ hodnoty adresy klienta a jmΘno u₧ivatele (pokud mßte unixovΘho klienta).

DefinovanΘ prom∞nnΘ prost°edφ

Uve∩me si nekomentovan² v²pis skriptu, kter² vracφ hodnoty r∙zn²ch prom∞nn²ch prost°edφ (n∞kterΘ nßzvy hovo°φ samy za sebe, v∞tÜinu ostatnφch pochopφte z dalÜφho p°φkladu).
#!/bin/sh
echo Content-type: text/plain
echo
echo SERVER_SOFTWARE = $SERVER_SOFTWARE
echo SERVER_NAME = $SERVER_NAME
echo GATEWAY_INTERFACE = $GATEWAY_INTERFACE
echo SERVER_PROTOCOL = $SERVER_PROTOCOL
echo SERVER_PORT = $SERVER_PORT
echo REQUEST_METHOD = $REQUEST_METHOD
echo HTTP_ACCEPT = "$HTTP_ACCEPT"
echo PATH_INFO = "$PATH_INFO"
echo PATH_TRANSLATED = "$PATH_TRANSLATED"
echo SCRIPT_NAME = "$SCRIPT_NAME"
echo QUERY_STRING = "$QUERY_STRING"
echo REMOTE_HOST = $REMOTE_HOST
echo REMOTE_ADDR = $REMOTE_ADDR
echo REMOTE_USER = $REMOTE_USER
echo AUTH_TYPE = $AUTH_TYPE
echo CONTENT_TYPE = $CONTENT_TYPE
echo CONTENT_LENGTH = $CONTENT_LENGTH
A abychom okusili takΘ z jinΘho soudku, tak te∩ funkΦn∞ podobn² skript v PERLu.
#!/usr/bin/perl
print "Content-type: text/html\n\n";
while (($key, $val) = each %ENV) {
	print "$key = $val
\n";
}
Jak poznajφ i ti, kte°φ PERL nikdy nevid∞li, vypisuje tento skript v cyklu vÜechny prom∞nnΘ prost°edφ, kterΘ jsou definovßny. FunkΦnφ variantu si m∙₧ete vyzkouÜet na serveru Kumbßl.

Co je to ten QUERY_STRING

Do prom∞nnΘ QUERY_STRING ulo₧φ WWW server p°i spouÜt∞nφ skriptu to, co bylo p°i jeho volßnφ v URL za znakem "?". Zkuste zavolat skript p°edchozφho p°φkladu takhle: http://mujserver/mojecesta/priklad.cgi?kukacka
V²pis QUERY_STRINGu by m∞l obsahovat slovo kukacka.

Vstup dat do CGI skriptu

Jednu mo₧nost vstupu do CGI skriptu jsme ji₧ vid∞li v podob∞ QUERY_STRINGu, kter² lze zadßvat p°φmo v URL. CGI skripty se vÜak Φasto pou₧φvajφ ke zpracovßnφ dat z HTML formulß°∙. P°edstavme si nßsledujφcφ HTML soubor.
<HTML>
<BODY>
<FORM ACTION="/mojecesta/mujskript.cgi" METHOD="GET">
Zadejte prosφm jmΘno:
<INPUT TYPE="text" NAME="jmΘno">
<BR>
Zadejte prosφm p°φjmenφ:
<INPUT TYPE="text" NAME="prijmeni">
<BR>
<INPUT TYPE="submit" VALUE="odeslat">
</FORM>
</BODY>
</HTML>
Hodnota ACTION u tagu formulß°e urΦuje skript, kter² se spustφ po odeslßnφ formulß°e. Hodnota METHOD urΦuje zp∙sob, jak²m budou data z formulß°e skriptu p°edßna. V naÜem p°φpad∞ (metoda GET) bude daty napln∞na prom∞nnß QUERY_STRING. Formßt prom∞nnΘ bude nßsledujφcφ: jmeno=zadanejmeno&prijmeni=zadaneprijmeni
╚ili vidφme, ₧e jednotlivß polφΦka jsou p°edßna ve form∞ nßzev=hodnota a jsou odd∞lena znakem "&".

Aby to bylo slo₧it∞jÜφ, jsou specißlnφ znaky vyskytujφcφ se v hodnotßch prom∞nn²ch zak≤dovßny nßsledujφcφm zp∙sobem:

JednotlivΘ prohlφ₧eΦe se mφrn∞ liÜφ v tom, kterΘ znaky k≤duji, ale uvedenß pravidla platφ obecn∞. Pro dek≤dovßnφ tedy staΦφ v²skyty znaku "+" nahradit mezerami a v²skyty kombinacφ "%hh" nahradit znakem s p°φsluÜn²m ASCII k≤dem. Snadno tedy dek≤dujeme °et∞zec: Ahoj%20lidi%21.

Metoda GET mß jak je zvykem svΘ v²hody a nev²hody. V²hodou je, ₧e klient zasφlß data pro CGI skript v URL. Pro v²Üe uveden² formulß° se bude volat URL: /mojecesta/mujskript.cgi?jmeno=zadanejmeno&prijmeni=zadaneprijmeni. Vidφme, ₧e takov² skript lze volat i ruΦn∞ bez vypl≥ovßnφ formulß°e. ╪ekn∞me, ₧e bychom cht∞li vytvo°it skript, kter² by pracoval jako anglicko-Φesk² slovnφk. Vstup bychom mohli zabezpeΦit nap°φklad nßsledujφcφm formulß°em.

<HTML>
<BODY>
<FORM ACTION="/mojecesta/slovnik.cgi" METHOD="GET">
Zadejte prosφm hledanΘ slovo:
<INPUT TYPE="text" NAME="slovo">
<BR>
<INPUT TYPE="submit" VALUE="odeslat">
</FORM>
</BODY>
</HTML>
Stejn∞ dob°e by ale tento slovnφk Üel pou₧φt p°φmo zßpisem: http://muj.server.cz/mojecesta/slovnik.cgi?slovo=meslovo.

To, ₧e se data p°edßvajφ jako souΦßst URL je vÜak zßrove≥ nev²hodou metody GET. Prohlφ₧eΦe toti₧ neumo₧≥ujφ neomezenou dΘlku URL a tak se metoda GET nehodφ pro v∞tÜφ objemy dat. NezanedbatelnΘ nenφ ani to, ₧e tyto °et∞zce se obvykle uschovßvajφ v logu nap°φklad na proxy serveru, pokud jej pou₧φvßte, co₧ znamenß snφ₧enφ ji₧ tak malΘho soukromφ.

Vstup metodou POST

Omezenφ mno₧stvφ dat vstupujφcφch do CGI skriptu odstra≥uje metoda POST. CGI skriptu jsou v p°φpad∞ pou₧itφ metody POST data sm∞°ovßna na standardnφ vstup. Skript m∙₧e vyu₧φt prom∞nnou prost°edφ CONTENT_LENGTH, kterß obsahuje celkovou dΘlku dat v bytech. Postup je pak takov², ₧e se zjistφ z prom∞nnΘ poΦet byt∙ k naΦtenφ a Φte se ze standardnφho vstupu.

Dlouho jsem na tomto mφst∞ sliboval p°φklad - te∩ tady koneΦn∞ je a abych zabil vφce much jednou ranou, tak je v jazyce C. Ve form∞, kterou vidφte, by jej m∞lo jφt bez problΘm∙ zkompilovat na Unixu. V jin²ch OS budou mo₧nß malinko jinΘ pot°ebnΘ hlaviΦkovΘ soubory a nahrazenφ funkce strcasecmp, kterß porovnßvß °et∞zce bez ohledu na velikost pφsmen.

Funkce p°φkladu:

Upozor≥uji, ₧e p°φklad neobsahuje ₧ßdnou kontrolu na chyby!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int main()
{
 char *method;
 char *data;
 /* vypis hlavicky */
 puts("Content-type: text/plain\n");
 /* zjisteni zpusobu zaslani dat */
 method=getenv("REQUEST_METHOD");
 /* kontrola definice promenne */
 if (method==NULL)
 {
  puts("Chyba: neni definovana promenna REQUEST_METHOD!");
  exit(1);
 }
 /* zkopirovani QUERY_STRINGu, je-li metoda GET */
 if (!strcasecmp(method,"GET"))
 {
  data=strdup(getenv("QUERY_STRING"));
 }
 /* nacteni dat ze STDIN, je-li metoda POST */
 if (!strcasecmp(method,"POST"))
 {
  int length;
  length=atoi(getenv("CONTENT_LENGTH"));
  data=malloc(length+1);
  /* cteni daneho poctu bytu */
  fread(data,length,1,stdin);
  data[length]=0;
 }
 /* pokusny vypis datoveho retezce */
 puts(data);
 /* uvolneni pameti a konec */
 free(data);
 return(0);
}

Pou₧itφ vstupnφch polφ

Zatφm jsme vid∞li v jakΘ podob∞ se CGI skriptu p°edajφ data zφskanß z polφΦek formulß°e typu text. ┌pln∞ stejn∞ se p°edßvajφ data z polφΦek password a textarea.

DalÜφ prvky formulß°e si uvedeme na p°φkladu:

Pokusn² formulß°
PolφΦko text:
PolφΦko password:
PolφΦko textarea:
PolφΦko checkbox:
1:
2:
3:
PolφΦko radio button:
1:
2:
3:
PolφΦko select:

Po odeslßnφ se spustφ CGI skript, kter² vypφÜe hodnoty vÜech prom∞nn²ch zaslan²ch z formulß°e.

Zdrojov² k≤d formulß°e si m∙₧ete prohlΘdnout, pokud zvolφte v klientovi zobrazenφ zdrojovΘho k≤du strßnky. K≤d CGI skriptu pro v²pis prom∞nn²ch je velmi jednoduch²:
#!/bin/sh

echo "Content-type: text/plain"
echo
echo "V²pis prom∞nn²ch formulß°e:"
echo
echo "$QUERY_STRING" | tr "&" "\n"
Vidφme, ₧e pro tento skript je nutnΘ zadßvat data metodou GET. Filtr tr pak nahradφ vÜechny znaky ampersand znakem konce °ßdky. Zaslanß data m∙₧ete takΘ vypsat pomocφ skriptu uvedenΘho jako p°φklad u metody POST.

Vra¥me se jeÜt∞ jednou k polφΦk∙m fomulß°e v p°φkladu:

P°esnΘ vyu₧itφ jednotliv²ch polφΦek a jejich atributu je dobrΘ nastudovat ve specifikaci HTML.

Nad dopisy Φtenß°∙

V e-mailech se Φasto ptßte, proΦ nechodφ to Φi ono. Bohu₧el v∞tÜinou m∙₧e b²t p°φΦin velkΘ mno₧stvφ. Proto jen struΦn∞ shrnu, v Φem mohou b²t chyby nejΦast∞ji:

U₧iteΦnΘ dodatky

SSI

SSI neboli Server Side Includes jsou definovanΘ vsuvky v dokumentu HTML, kterΘ jsou interpretovßny WWW serverem p°ed p°edßnφm dokumentu klientovi. Server pro tuto Φinnost samoz°ejm∞ musφ b²t nakonfigurovßn. Soubory, kterΘ tyto vsuvky obsahujφ, majφ Φasto p°φpony shtml nebo ssi.

SSI umo₧≥ujφ nap°φklad vlo₧it do strßnky datum jejφ poslednφ ·pravy nebo v²stup zadanΘho p°φkazu nebo CGI skriptu. Obecn² formßt vsuvky je nßsledujφcφ:
<!--#p°φkaz parametr1=hodnota parametr2=hodnota ... -->

Pou₧itelnΘ p°φkazy a jejich parametry:

P°φkladem ΦastΘho vyu₧itφ SSI je poΦφtadlo p°φstup∙ volanΘ p°φkazem exec.
(c)1997,98 Dalibor èrßmek (dali@kumbal.vse.cz). Poslednφ ·prava: 8. ·nora 1998.